参考文档:pg149-fir-compiler
详细的可参考IP核的用户手册,此处主要记录一下使用过程中不太理解的地方。
1、滤波器系数保存方式有两种,Vector和COE FILE。其中注意的是Vector中输入的是十进制数。COE文件按照标准格式保存即可。
本文中使用的是COE文件,主要是方便matlab可以直接生成COE文件。
另外,本文使用了勾选了use reloadabel coefficients,可重新配置滤波器系数。
2、通道配置,此处没有需要特别介绍的,按照需要勾选即可。
3、实现方式上,滤波器系数均采用无符号数据,输入数据也为无符号数据,这样就可以得到无符号的滤波后数据。在左侧可以看到目前设置下数据的延时、滤波器系数和输出数据的类型与位宽、重新加载时的滤波器系数配置顺序。
4、滤波器架构选择转置滤波器,选择它的原因我所做的项目需要较少的数据延时。
5、最后是数据的接口,忽略了AXIS总线的LAST信号,使能了复位信号。按需勾选即可。
此处使用了DDS complier IP 模拟生成正弦波。DDS IP生成了9.3M左右的正弦波,配置截图如下:
此处有一个问题需要注意,DDS IP输出的为有符号数,而我们前面设置的FIR输入数据为无符号数。实际观察结果的时候,我们将DDS输出的波形按照无符号数波形进行观察。
FIR IP核控制模块如下:
`timescale 1ns / 1ps
module FIR_8_order(
input clk_300M, //300M时钟
input [15:0] FIR_coe0,//滤波器系数0
input [15:0] FIR_coe1,//滤波器系数1
input [15:0] FIR_coe2,//滤波器系数2
input [15:0] FIR_coe3,//滤波器系数3
input [15:0] FIR_coe4,//滤波器系数4
input [15:0] FIR_coe5,//C滤波器系数5
input [15:0] FIR_coe6,//滤波器系数6
input [15:0] FIR_coe7,//滤波器系数7
input [15:0] FIR_coe8,//滤波器系数8
input FIR_en,//CH1滤波器使能
input FIR_coe_update,//更新滤波器参数
input [15:0] data_in,//波形数据
input data_valid,
input data_end,//滤波结束标志
output reg [15:0] fir_data_out //滤波后数据输出
);
//-----------------------------信号定义-------------------------
(*mark_debug = "TRUE"*)reg [3:0]state; //状态机
reg [3:0]next_state; //状态机
localparam IDLE_STATE = 4'b0001; //空闲状态
localparam RELOAD_STATE = 4'b0010;//重加载状态
localparam DELAY_STATE = 4'b0100;//配置状态
localparam CONFIG_STATE = 4'b1000;//配置状态
//FIR滤波器输入数据
//wire s_axis_data_tvalid;
//wire s_axis_data_tready;
//wire [15:0] s_axis_data_tdata;
//FIR滤波器config数据
(*mark_debug = "TRUE"*)reg s_axis_config_tvalid;
(*mark_debug = "TRUE"*)wire s_axis_config_tready;
(*mark_debug = "TRUE"*)wire [7:0] s_axis_config_tdata;
//FIR滤波器reload数据
(*mark_debug = "TRUE"*)reg s_axis_reload_tvalid;
(*mark_debug = "TRUE"*)wire s_axis_reload_tlast;
(*mark_debug = "TRUE"*)wire s_axis_reload_tready;
(*mark_debug = "TRUE"*)reg [15:0] s_axis_reload_tdata;
//FIR滤波器输出数据
//wire m_axis_data_tvalid;
wire [39:0] m_axis_data_tdata;
//FIR滤波器标志数据
(*mark_debug = "TRUE"*)wire event_s_reload_tlast_missing;
(*mark_debug = "TRUE"*)wire event_s_reload_tlast_unexpected ;
reg fir_rstn;//滤波器复位标志
reg FIR_coe_update_d1;//FIR_coe_update同步信号
reg FIR_coe_update_d2;//FIR_coe_update同步信号
reg FIR_coe_update_pulse; //FIR_coe_update上升沿脉冲,有效2个时钟
reg [3:0] reload_cnt;//加载滤波器系数计数
localparam COE_NUM = 4'd9; //滤波器系数为9个
reg [1:0]config_cnt;//配置计数
reg reload_cmp;//加载滤波器系数完成
reg config_cmp;//配置滤波器完成
reg delay_cmp; //延时完成
reg [3:0] delay_cnt;//延时16个时钟
//-----------------------------逻辑实现-------------------------
//--------------------------------------------------------------
always@(posedge clk_300M)
begin
if(FIR_en) //滤波使能
fir_rstn <= !data_end;//读取完一整帧时,将FIR复位等待下次滤波
else
fir_rstn <= 1'b0;//不开启滤波器时,将IP核复位
end
always@(posedge clk_300M)
begin
FIR_coe_update_d1 <= FIR_coe_update;//延迟1个时钟
FIR_coe_update_d2 <= FIR_coe_update_d1;//延迟1个时钟
FIR_coe_update_pulse <= !FIR_coe_update_d2 & FIR_coe_update;//FIR_coe_update上升沿脉冲,有效2个时钟
end
//状态机控制
always@(posedge clk_300M)
begin
state <= next_state;
end
always@( * )
begin
case(state)
IDLE_STATE : begin
if(FIR_coe_update_pulse) //接收到ARM下发的更新FIR系数指令
next_state <= RELOAD_STATE;
else
next_state <= IDLE_STATE;
end
RELOAD_STATE : begin
if(reload_cmp) //加载完毕9个FIR系数,跳转状态机
next_state <= DELAY_STATE;
else
next_state <= RELOAD_STATE;
end
DELAY_STATE : begin
if(delay_cmp) //加载完毕9个FIR系数,跳转状态机
next_state <= CONFIG_STATE;
else
next_state <= DELAY_STATE;
end
CONFIG_STATE : begin
if(config_cmp) //配置完FIR
next_state <= IDLE_STATE;
else
next_state <= CONFIG_STATE;
end
default:next_state <= IDLE_STATE;
endcase
end
//各状态下下的操作----------------------
//RELOAD_STATE状态
always@(posedge clk_300M)
begin
if(state == RELOAD_STATE) begin
if(s_axis_reload_tvalid & s_axis_reload_tready) //每写入1次滤波器系数,reload_cnt加1
reload_cnt <= reload_cnt + 1;
end
else begin //其余状态加清零
reload_cnt <= 0;
end
end
//加载最后一个滤波器系数
assign s_axis_reload_tlast = (reload_cnt == COE_NUM -1) & s_axis_reload_tready & s_axis_reload_tvalid;
always@(posedge clk_300M)
begin
if(state == RELOAD_STATE) begin
if(reload_cnt < COE_NUM -1) //加载滤波器系数
s_axis_reload_tvalid <= 1'b1;
else if((reload_cnt == COE_NUM -1) && s_axis_reload_tready) //加载最后一个滤波器系数
s_axis_reload_tvalid <= 1'b0;
end
else begin
s_axis_reload_tvalid <= 1'b0;
end
end
always@(posedge clk_300M)
begin
s_axis_reload_tdata <= FIR_coe0; //默认输出寄存器系数0
if(s_axis_reload_tvalid & s_axis_reload_tready) begin //每写入1次滤波器系数,reload_cnt加1
case(reload_cnt)
'd0:s_axis_reload_tdata <=FIR_coe1;//写完第0个滤波器系数,立刻更新新的滤波器系数
'd1:s_axis_reload_tdata <=FIR_coe2;//写完第1个滤波器系数,立刻更新新的滤波器系数
'd2:s_axis_reload_tdata <=FIR_coe3;//写完第2个滤波器系数,立刻更新新的滤波器系数
'd3:s_axis_reload_tdata <=FIR_coe4;//写完第3个滤波器系数,立刻更新新的滤波器系数
'd4:s_axis_reload_tdata <=FIR_coe5;//写完第4个滤波器系数,立刻更新新的滤波器系数
'd5:s_axis_reload_tdata <=FIR_coe6;//写完第5个滤波器系数,立刻更新新的滤波器系数
'd6:s_axis_reload_tdata <=FIR_coe7;//写完第6个滤波器系数,立刻更新新的滤波器系数
'd7:s_axis_reload_tdata <=FIR_coe8;//写完第7个滤波器系数,立刻更新新的滤波器系数
default:;
endcase
end
end
always@(posedge clk_300M)
begin
if((reload_cnt == COE_NUM -1) & s_axis_reload_tready & s_axis_reload_tvalid) //加载完滤波器系数时,reload_cmp有效
reload_cmp <= 1'b1;
else
reload_cmp <= 1'b0;
end
DELAY_STATE状态
always@(posedge clk_300M)
begin
if(state == DELAY_STATE) //延时计数
delay_cnt <= delay_cnt + 1;
else
delay_cnt <= 0;
end
always@(posedge clk_300M)
begin
delay_cmp <= delay_cnt[3];//延时完成
end
//CONFIG_STATE状态
assign s_axis_config_tdata = 0;
always@(posedge clk_300M)
begin
if(state == CONFIG_STATE) begin
if(s_axis_config_tvalid & s_axis_config_tready)
config_cnt <= config_cnt +1;
end
else
config_cnt <= 0;
end
always@(posedge clk_300M)
begin
if(config_cnt == 2'b10)
config_cmp <= 1;
else
config_cmp <= 0;
end
always@(posedge clk_300M)
begin
if(state == CONFIG_STATE) begin
if(config_cnt < 2'b01)
s_axis_config_tvalid <= 1'b1;
else if(s_axis_config_tvalid & s_axis_config_tready)
s_axis_config_tvalid <= 1'b0;
end
else begin
s_axis_config_tvalid <= 1'b0;
end
end
fir_compiler_0 fir_compiler_0 (
.aresetn(fir_rstn), // input wire aresetn
.aclk(clk_300M), // input wire aclk
.s_axis_data_tvalid(data_valid), // input wire s_axis_data_tvalid
.s_axis_data_tready(), // output wire s_axis_data_tready
.s_axis_data_tdata(data_in), // input wire [15 : 0] s_axis_data_tdata
.s_axis_config_tvalid(s_axis_config_tvalid), // input wire s_axis_config_tvalid
.s_axis_config_tready(s_axis_config_tready), // output wire s_axis_config_tready
.s_axis_config_tdata(s_axis_config_tdata), // input wire [7 : 0] s_axis_config_tdata
.s_axis_reload_tvalid(s_axis_reload_tvalid), // input wire s_axis_reload_tvalid
.s_axis_reload_tready(s_axis_reload_tready), // output wire s_axis_reload_tready
.s_axis_reload_tlast(s_axis_reload_tlast), // input wire s_axis_reload_tlast
.s_axis_reload_tdata(s_axis_reload_tdata), // input wire [15 : 0] s_axis_reload_tdata
.m_axis_data_tvalid(), // output wire m_axis_data_tvalid
.m_axis_data_tdata(m_axis_data_tdata), // output wire [39 : 0] m_axis_data_tdata
.event_s_reload_tlast_missing(event_s_reload_tlast_missing), // output wire event_s_reload_tlast_missing
.event_s_reload_tlast_unexpected(event_s_reload_tlast_unexpected) // output wire event_s_reload_tlast_unexpected
);
always@(posedge clk_300M)
begin
if(FIR_en) //滤波器使能,输出数据为FIR数据
fir_data_out<= m_axis_data_tdata[31:16]; //取有效数据的高16bits
else //滤波器禁能
fir_data_out<= data_in;
end
endmodule
仿真顶层程序
`timescale 1ns / 1ps
//
module testbench(
);
reg clk_300M = 0;
always
begin
#1.66
clk_300M <= ~clk_300M;
end
wire [15:0] data_in;//波形数据
wire data_valid;//有效标志
localparam [15:0] CH1_FIR_coe0 = 16'h02ff;//CH1滤波器系数0
localparam [15:0] CH1_FIR_coe1 = 16'h0a40;//CH1滤波器系数1
localparam [15:0] CH1_FIR_coe2 = 16'h1e37;//CH1滤波器系数2
localparam [15:0] CH1_FIR_coe3 = 16'h3500;//CH1滤波器系数3
localparam [15:0] CH1_FIR_coe4 = 16'h3f14;//CH1滤波器系数4
localparam [15:0] CH1_FIR_coe5 = 16'h3500;//CH1滤波器系数5
localparam [15:0] CH1_FIR_coe6 = 16'h1e37;//CH1滤波器系数6
localparam [15:0] CH1_FIR_coe7 = 16'h0a40;//CH1滤波器系数7
localparam [15:0] CH1_FIR_coe8 = 16'h02ff;//CH1滤波器系数8
reg FIR_coe_update=0;
reg data_end=0;
reg [9:0] rd_cnt=0;
reg [12:0] update_cnt=0;
always@(posedge clk_300M)
begin
if(rd_cnt <= 10'h3FF)
rd_cnt <= rd_cnt + 1;
end
always@(posedge clk_300M)
begin
update_cnt <= update_cnt + 1;
end
always@(posedge clk_300M)
begin
if(rd_cnt >= 10'h3FC)
data_end <= 1'b1;
else
data_end <= 1'b0;
end
always@(posedge clk_300M)
begin
if(update_cnt >= 13'h1FFC)
FIR_coe_update <= 1'b1;
else
FIR_coe_update <= 1'b0;
end
dds_compiler_0 dds_compiler_0 (
.aclk(clk_300M), // input wire aclk
.m_axis_data_tvalid(data_valid), // output wire m_axis_data_tvalid
.m_axis_data_tdata(data_in) // output wire [15 : 0] m_axis_data_tdata
);
FIR_8_order FIR_8_order_CH1(
.clk_300M(clk_300M),//input clk_300M, //300M时钟
.FIR_coe0(CH1_FIR_coe0),//input [15:0] FIR_coe0,//滤波器系数0
.FIR_coe1(CH1_FIR_coe1),//input [15:0] FIR_coe1,//滤波器系数1
.FIR_coe2(CH1_FIR_coe2),//input [15:0] FIR_coe2,//滤波器系数2
.FIR_coe3(CH1_FIR_coe3),//input [15:0] FIR_coe3,//滤波器系数3
.FIR_coe4(CH1_FIR_coe4),//input [15:0] FIR_coe4,//滤波器系数4
.FIR_coe5(CH1_FIR_coe5),//input [15:0] FIR_coe5,//C滤波器系数5
.FIR_coe6(CH1_FIR_coe6),//input [15:0] FIR_coe6,//滤波器系数6
.FIR_coe7(CH1_FIR_coe7),//input [15:0] FIR_coe7,//滤波器系数7
.FIR_coe8(CH1_FIR_coe8),//input [15:0] FIR_coe8,//滤波器系数8
.FIR_en(1'b1),//input FIR_en,//CH1滤波器使能
.FIR_coe_update(FIR_coe_update),//input FIR_coe_update,//更新滤波器参数
.data_in(data_in),//input [15:0] compress_data,//波形数据
.data_valid(data_valid),//input compress_data_valid,//数据有效标志
.data_end(data_end),//input rd_dso_data_end,//波形结束标志
.fir_data_out()//output [15:0] fir_data_out //滤波后数据输出
);
endmodule
输入的波形和滤波后的波形对比如下,对9Mhz波形滤波后的波形与输入的波形差别不大,满足设计要求。
需要注意的是,输出波形数据需要按照设计需求截取位宽,否则会导致波形幅度不正确。(16位无符号的滤波器系数,相加之后最大值约为16‘hFFFF,乘上16bits位宽的无符号数据后,输出数据位宽为32bits,截取高16bits输出)
最后再说一下参数重加载的过程,按照手册要求时序图如下:
在这个过程中,需要注意的是,通过RELOAD通道配置完滤波器系数后,需要再通过配置CONFIG通道 连续有效两次s_axis_config_tvalid,以完成滤波器系数的更新。因为本次设计中只使用了1组滤波器系数,因此s_axis_config_tdata默认输出0x00即可。
问题:再调试过程中,在RELOAD通道配置完滤波器系数后,立刻在CONFIG通道有效两次s_axis_config_tvalid,希望能够完成滤波器系数的更新。但是造成RELOAD通道的s_axis_reload_tready信号拉低后无法拉高,RELOAD通道阻塞了。导致滤波器系数没有得到更新,并且后续也无法配置新的滤波器系数。
解决方式:后来各种更改程序,在RELOAD通道配置完滤波器系数后,延迟了几个时钟后再在CONFIG通道有效两次s_axis_config_tvalid,滤波器系数正常配置。
这样修改也符合用户手册中提供的接口时序。