数据流帧封装模块
这一模块的实现是相对简单的,只需要注意数据对齐就好,虽然USB传输为8位并口,但考虑到数据都是以4字节为单位,故扩展帧头为2字节,类型为1字节,长度为1字节,便于上位机进行数据的处理。
代码的主要思路是检测有效信号的上升沿,表示第一个数据到来,并且数据流是连贯的,在信号的上升沿,需要判断数据的类型以及传输长度,消耗1个时钟周期。再下一个时钟周期,则进行帧头类型长度信息的传输,故有效数据需要延迟两个时钟周期,再进行传输,并且因为需要传输帧尾,故将有效指示信号延迟3个时钟周期。
ADC数据、FFT数据的处理流程大致都是一样的,关于最大幅值和信号频率,因为是并行传输,故需要再多延迟一个时钟周期,具体的代码如下
module frame_pack(
input i_clk ,
input i_rst ,
input [31:0] i_usb_adc_data ,
input i_usb_adc_valid ,
input [31:0] i_usb_fft_data ,
input i_usb_fft_valid ,
input [31:0] i_usb_max_pow ,
input [31:0] i_usb_max_index ,
input i_usb_max_valid ,
input [15:0] i_usb_data_len ,
output [31:0] o_usb_frame_adc_data ,
output o_usb_frame_adc_valid ,
output [31:0] o_usb_frame_fft_data ,
output o_usb_frame_fft_valid ,
output [31:0] o_usb_frame_max_data ,
output o_usb_frame_max_valid
);
localparam P_FREAM_HEAD = 8'hA5 ,
P_FREAM_END = 8'hD5 ;
reg ri_usb_adc_valid ;
reg ri_usb_fft_valid ;
reg ri_usb_max_valid ;
reg ri_usb_adc_valid_dly ;
reg ri_usb_fft_valid_dly ;
reg ri_usb_adc_valid_dly2 ;
reg ri_usb_fft_valid_dly2 ;
reg ri_usb_max_valid_dly ;
reg ri_usb_max_valid_dly2 ;
reg ri_usb_max_valid_dly3 ;
reg [31:0] ri_usb_adc_data ;
reg [31:0] ri_usb_fft_data ;
reg [31:0] ri_usb_max_pow ;
reg [31:0] ri_usb_max_index ;
reg [31:0] ri_usb_adc_data_dly ;
reg [31:0] ri_usb_fft_data_dly ;
reg [31:0] ri_usb_max_pow_dly ;
reg [31:0] ri_usb_max_index_dly ;
reg [31:0] ri_usb_max_index_dly2 ;
reg [7 :0] r_frmae_data_type ;
reg [7 :0] r_frmae_data_len ;
reg [31:0] ro_usb_frame_adc_data ;
reg ro_usb_frame_adc_valid ;
reg [31:0] ro_usb_frame_fft_data ;
reg ro_usb_frame_fft_valid ;
reg [31:0] ro_usb_frame_max_data ;
reg ro_usb_frame_max_valid ;
reg rw_usb_adc_valid_pos ;
reg rw_usb_fft_valid_pos ;
reg rw_usb_max_valid_pos ;
wire w_usb_adc_valid_pos ;
wire w_usb_fft_valid_pos ;
wire w_usb_max_valid_pos ;
assign w_usb_adc_valid_pos = i_usb_adc_valid & (!ri_usb_adc_valid);
assign w_usb_fft_valid_pos = i_usb_fft_valid & (!ri_usb_fft_valid);
assign w_usb_max_valid_pos = i_usb_max_valid & (!ri_usb_max_valid);
assign o_usb_frame_adc_data = ro_usb_frame_adc_data ;
assign o_usb_frame_adc_valid = ro_usb_frame_adc_valid;
assign o_usb_frame_fft_data = ro_usb_frame_fft_data ;
assign o_usb_frame_fft_valid = ro_usb_frame_fft_valid;
assign o_usb_frame_max_data = ro_usb_frame_max_data ;
assign o_usb_frame_max_valid = ro_usb_frame_max_valid;
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst) begin
ri_usb_adc_valid <= 'd0;
ri_usb_fft_valid <= 'd0;
ri_usb_max_valid <= 'd0;
ri_usb_adc_valid_dly <= 'd0;
ri_usb_fft_valid_dly <= 'd0;
ri_usb_adc_valid_dly2<= 'd0;
ri_usb_fft_valid_dly2<= 'd0;
ri_usb_max_valid_dly <= 'd0;
ri_usb_max_valid_dly2<= 'd0;
ri_usb_max_valid_dly3<= 'd0;
ri_usb_adc_data <= 'd0;
ri_usb_fft_data <= 'd0;
ri_usb_max_pow <= 'd0;
ri_usb_max_index <= 'd0;
ri_usb_adc_data_dly <= 'd0;
ri_usb_fft_data_dly <= 'd0;
ri_usb_max_pow_dly <= 'd0;
ri_usb_max_index_dly <= 'd0;
ri_usb_max_index_dly2<= 'd0;
end
else begin
ri_usb_adc_valid <= i_usb_adc_valid ;
ri_usb_fft_valid <= i_usb_fft_valid ;
ri_usb_max_valid <= i_usb_max_valid ;
ri_usb_adc_valid_dly <= ri_usb_adc_valid;
ri_usb_fft_valid_dly <= ri_usb_fft_valid;
ri_usb_adc_valid_dly2<= ri_usb_adc_valid_dly;
ri_usb_fft_valid_dly2<= ri_usb_fft_valid_dly;
ri_usb_max_valid_dly <= ri_usb_max_valid;
ri_usb_max_valid_dly2<= ri_usb_max_valid_dly;
ri_usb_max_valid_dly3<= ri_usb_max_valid_dly2;
ri_usb_adc_data <= i_usb_adc_data ;
ri_usb_fft_data <= i_usb_fft_data ;
ri_usb_max_pow <= i_usb_max_pow ;
ri_usb_max_index <= i_usb_max_index ;
ri_usb_adc_data_dly <= ri_usb_adc_data ;
ri_usb_fft_data_dly <= ri_usb_fft_data ;
ri_usb_max_pow_dly <= ri_usb_max_pow ;
ri_usb_max_index_dly <= ri_usb_max_index;
ri_usb_max_index_dly2<= ri_usb_max_index_dly;
end
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_frmae_data_type <= 'd0;
else if(w_usb_adc_valid_pos)
r_frmae_data_type <= 8'h01;
else if(w_usb_fft_valid_pos)
r_frmae_data_type <= 8'h02;
else if(w_usb_max_valid_pos)
r_frmae_data_type <= 8'h03;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_frmae_data_len <= 'd0;
else if(w_usb_adc_valid_pos || w_usb_fft_valid_pos) begin
if(i_usb_data_len == 'd2048)
r_frmae_data_len <= 8'h01;
else if(i_usb_data_len == 'd4096)
r_frmae_data_len <= 8'h02;
else if(i_usb_data_len == 'd48192)
r_frmae_data_len <= 8'h03;
end
else if(w_usb_max_valid_pos)
r_frmae_data_len <= 8'h04;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst) begin
rw_usb_adc_valid_pos <= 'd0;
rw_usb_fft_valid_pos <= 'd0;
rw_usb_max_valid_pos <= 'd0;
end
else begin
rw_usb_adc_valid_pos <= w_usb_adc_valid_pos;
rw_usb_fft_valid_pos <= w_usb_fft_valid_pos;
rw_usb_max_valid_pos <= w_usb_max_valid_pos;
end
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_usb_frame_adc_data <= 'd0;
else if(rw_usb_adc_valid_pos)
ro_usb_frame_adc_data <= {P_FREAM_HEAD,P_FREAM_HEAD,r_frmae_data_type,r_frmae_data_len};
else if(ri_usb_adc_valid_dly)
ro_usb_frame_adc_data <= ri_usb_adc_data_dly;
else if(ri_usb_adc_valid_dly2)
ro_usb_frame_adc_data <= {P_FREAM_END,P_FREAM_END,P_FREAM_END,P_FREAM_END};
else
ro_usb_frame_adc_data <= 'd0;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_usb_frame_adc_valid <= 'b0;
else if(ri_usb_adc_valid || ri_usb_adc_valid_dly || ri_usb_adc_valid_dly2)
ro_usb_frame_adc_valid <= 'b1;
else
ro_usb_frame_adc_valid <= 'b0;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_usb_frame_fft_data <= 'd0;
else if(rw_usb_fft_valid_pos)
ro_usb_frame_fft_data <= {P_FREAM_HEAD,P_FREAM_HEAD,r_frmae_data_type,r_frmae_data_len};
else if(ri_usb_fft_valid_dly)
ro_usb_frame_fft_data <= ri_usb_fft_data_dly;
else if(ri_usb_fft_valid_dly2)
ro_usb_frame_fft_data <= {P_FREAM_END,P_FREAM_END,P_FREAM_END,P_FREAM_END};
else
ro_usb_frame_fft_data <= 'd0;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_usb_frame_fft_valid <= 'b0;
else if(ri_usb_fft_valid || ri_usb_fft_valid_dly || ri_usb_fft_valid_dly2)
ro_usb_frame_fft_valid <= 'b1;
else
ro_usb_frame_fft_valid <= 'b0;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_usb_frame_max_data <= 'd0;
else if(rw_usb_max_valid_pos)
ro_usb_frame_max_data <= {P_FREAM_HEAD,P_FREAM_HEAD,r_frmae_data_type,r_frmae_data_len};
else if(ri_usb_max_valid_dly)
ro_usb_frame_max_data <= ri_usb_max_pow_dly;
else if(ri_usb_max_valid_dly2)
ro_usb_frame_max_data <= ri_usb_max_index_dly2;
else if(ri_usb_max_valid_dly3)
ro_usb_frame_max_data <= {P_FREAM_END,P_FREAM_END,P_FREAM_END,P_FREAM_END};
else
ro_usb_frame_max_data <= 'd0;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_usb_frame_max_valid <= 'b0;
else if(ri_usb_max_valid || ri_usb_max_valid_dly || ri_usb_max_valid_dly2 || ri_usb_max_valid_dly3)
ro_usb_frame_max_valid <= 'b1;
else
ro_usb_frame_max_valid <= 'b0;
end
endmodule
进行仿真,验证逻辑功能的正确性
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst) begin
i_usb_adc_data <= 'd0;
i_usb_adc_valid <= 'd0;
i_usb_data_len <= 'd0;
end
else if(i_usb_adc_data == 'd4096) begin
i_usb_adc_data <= 'd4096;
i_usb_adc_valid <= 'd0;
i_usb_data_len <= 'd0;
end
else begin
i_usb_adc_data <= i_usb_adc_data + 'b1;
i_usb_adc_valid <= 'b1;
i_usb_data_len <= 'd4096;
end
end
传输4096个ADC数据
帧头、类型、长度信息传输正确。
帧尾信息传输正确
传输本帧最大频率与最大幅值
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst) begin
i_usb_max_pow <= 'd0;
i_usb_max_index <= 'd0;
i_usb_max_valid <= 'd0;
i_usb_data_len <= 'd0;
end
else if(i_usb_max_pow == 'd752) begin
i_usb_max_pow <= 'd752;
i_usb_max_index <= 'd0;
i_usb_max_valid <= 'd0;
i_usb_data_len <= 'd0;
end
else begin
i_usb_max_pow <= 'd752;
i_usb_max_index <= 'd102;
i_usb_max_valid <= 'd1;
i_usb_data_len <= 'd2;
end
end
传输正确。
整体工程逻辑梳理
整体工程的代码文件如上所示。
其中 clk_gen模块用于输出ADC采样所需时钟。
rst_gen模块用于产生复位信号。
adc_drive模块用于接收ADC采样数据,并进行通道封装。
fft_2048x2模块用于对输入ADC数据进行FFT计算,并计算FFT的幅值,搜索每一帧的最大值与信号频率。
frame_pack模块用于对上传数据进行帧头、数据类型、数据长度、帧尾封装。
ft232h_drive模块用于搭建usb2.0驱动,并对各类上传数据与接收数据进行缓存,传输给上下游模块。
cmd_prase模块用于解析上位机发送的指令,传输给下级模块,进行指令回应。
至此,下位机工程搭建完毕,因为硬件原因,关于驱动FT232H实现FPGA与PC的通信,还需要等待一段时间。