XILINX FIR IP核系数重载功能的学习以及测试
最近在学习宽带数字接收机的一些东西,其中多相滤波是属于其中比较关键的一环,笔者在matlab上成功仿真了多相滤波这一环节后,便想着在FPGA上实现多相滤波,多相滤波器的一个重要环节便是滤波器组的设计,简单来讲,滤波器组是由原型低通滤波器调制而成,在这里不做过多介绍,因为笔者信道化的数目是32信道,在实际工程中64,128,256信道都是可能采用的,就本文来讲,那么就需要32个不同系数的FIR滤波器,如果直接例化32个FIR ip核,生成32个coe文件,那未免也太过麻烦。所以笔者在想,是否有什么简单方法,可以导入滤波器系数至FIR IP。经过对fir ip核手册的查阅,发现该ip支持系数重载功能,即通过代码配置fir ip的滤波系数,显然,这是一个很有用的功能,既然IP支持这个功能,我们便要把它利用起来,以简化我们的工程设计。
在本文中,笔者并不直接构造多相滤波,而是使用matlab生成两个正弦叠加信号,进行低通滤波,然后将系数保存为coe文件,验证系数重载功能。
首先在matlab中生成1个1Mhz的正弦波信号与3Mhz的正弦波信号,采样率选择50Mhz,将二者叠加在一起。代码如下
% 参数设置
fs = 50e6; % 采样频率 (50 MHz)
t = 0:1/fs:1e-3; % 时间向量 (1 ms)
% 生成正弦波
f1 = 1e6; % 1 MHz
f2 = 3e6; % 3 MHz
signal1 = sin(2*pi*f1*t); % 1 MHz 正弦波
signal2 = sin(2*pi*f2*t); % 3 MHz 正弦波
% 叠加信号
combined_signal = signal1 + signal2;
为了能够在vivado仿真中,明显看出滤波器系数重载后的滤波变化,在matlab中生成两个低通滤波器,一个截至频率为2Mhz,一个截至频率为5Mhz,显然在截至频率为5Mhz的低通滤波器工作环境下,不考虑滤波器增益影响,信号波形不会发生变化,而在截至频率为2Mhz的低通滤波器工作环境下,3Mhz的信号将被滤除。
生成指定阶数的低通滤波器,至于为什么需要明确两个滤波器具有相同阶数,是因为在vivado中,系数重载时传输的系数个数需要与配置ip时,滤波系数的个数保持严格一致,否则会出现bug,无法成功更新,这点在fir ip手册中有明确说明。
分别使用2Mhz低通滤波器与5Mhz低通滤波器对叠加信号做滤波,绘制滤波后时域与频域波形,如下所示
将叠加信号与滤波器系数进行量化,存储至coe文件,供vivado使用
上图是导入了5Mhz的低通滤波器系数,可以看到vivado自动计算出了系数个数为51个,并且笔者勾选了Use Reloadable Coeefficients功能,可以看出在左侧框图出现了S_AXIS_RELOAD与S_AXIS_CONFIG通道。其符合标准AXI_STREAM流协议,S_AXIS_RELOAD通道为重载系数的传输通道,而S_AXIS_CONFIG通道为确认配置通道,在本文中,在传输完成系数后,使用该通道确定系数重载。
下图为系数位宽,与系数结构两个选项,系数位宽选择matlab量化系数时选取的位宽即可,当然,matlab的具体量化位宽,由用户综合考量决定。实际上对滤波器系数的量化上,需要综合考虑滤波增益,与位宽增加诸多情况,在本文中,不做详细讨论。
关于系数结构,第一种为可推测的,即ip通过配置ip核时填写的系数向量,推测系数向量属于哪种结构,第二种为非对称结构,第三种为对称结构;在测试demo中,使用的是普通低通滤波器,故选择第一种或第三种皆可,文中选用第三种对称结构。
需要特别注意的是,因为初始化的系数向量结构为对称类型,且包含51个系数,则在重载时,ip核默认只会接收26个数据,之后便将s_axis_reload_tready信号拉低,所以我们只需要输出26个前系数,对应的tvalid和tlast信号,也需要符合传输26个系数的逻辑变化,否则无法成功写入。在reload通道成功传输系数后,还需要进行使能,才能成功的使得IP核的工作系数发生变化,也即配置s_axis_config通道,ip手册给出的配置时序图,如下图所示
本demo中,在系数重载通道的last信号拉高后,进行周期延迟,之所以进行一定延迟,是因为在手册中提到要保证系数成功传输完成后,再将config通道的valid信号拉高,并data传输1,保证配置成功传输后,拉低valid,清零data。如下图逻辑所示。i_reload_cmd为系数重载命令,系数重载控制模块,接收到这一命令后开始进行系数重载逻辑。高电平有效,拉高一个周期即可。
系数重载控制模块的逻辑采用状态机设计,代码如下
/*The module realizes receiving FIR filter coefficients update instructions and transmitting filter coefficients to FIR IP*/
module coe_reload(
input i_clk ,
input i_rst ,
input i_reload_cmd ,
output m_axis_reload_tvalid ,
input m_axis_reload_tready ,
output m_axis_reload_tlast ,
output [15:0] m_axis_reload_tdata ,
output m_axis_config_tvalid ,
input m_axis_config_tready ,
output [7 :0] m_axis_config_tdata
);
localparam P_RELOAD_LENGTH = 'd26 ;
localparam P_ST_IDLE = 0 ,
P_ST_RELOAD = 1 ,
P_ST_WAIT = 2 ,
P_ST_CONFIG = 3 ;
reg [2 :0] r_st_current ;
reg [2 :0] r_st_next ;
reg [2 :0] r_st_cnt ;
reg ri_reload_cmd ;
reg rm_axis_reload_tvalid ;
reg rm_axis_reload_tlast ;
reg [2 :0] rm_axis_reload_tlast_buf;
reg rm_axis_config_tvalid ;
reg [7 :0] rm_axis_config_tdata ;
reg r_rom_en ;
reg [5 :0] r_rom_addr ;
wire signed [11:0] w_rom_dout ;
wire w_config_activate ;
ROM_12X51 ROM_12X51_U0 (
.clka (i_clk ),
.ena (r_rom_en ),
.addra (r_rom_addr ),
.douta (w_rom_dout )
);
assign w_config_activate = rm_axis_config_tvalid & m_axis_config_tready;
assign m_axis_reload_tvalid = rm_axis_reload_tvalid ;
assign m_axis_reload_tlast = rm_axis_reload_tlast ;
assign m_axis_reload_tdata = {4'd0,w_rom_dout} ;
assign m_axis_config_tvalid = rm_axis_config_tvalid ;
assign m_axis_config_tdata = rm_axis_config_tdata ;
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_st_current <= 'd0;
else
r_st_current <= r_st_next;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_st_cnt <= 'd0;
else if(r_st_current != r_st_next)
r_st_cnt <= 'd0;
else
r_st_cnt <= r_st_cnt + 1;
end
always@(*)
begin
case(r_st_current)
P_ST_IDLE : r_st_next = i_reload_cmd ? P_ST_RELOAD : P_ST_IDLE;
P_ST_RELOAD : r_st_next = r_rom_addr == P_RELOAD_LENGTH - 1 ? P_ST_WAIT : P_ST_RELOAD;
P_ST_WAIT : r_st_next = r_st_cnt == 'd2 ? P_ST_CONFIG : P_ST_WAIT;
P_ST_CONFIG : r_st_next = w_config_activate ? P_ST_IDLE : P_ST_CONFIG;
default : r_st_next = P_ST_IDLE;
endcase
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_rom_en <= 'b0;
else if(r_rom_addr == P_RELOAD_LENGTH - 1)
r_rom_en <= 'b0;
else if(r_st_current == P_ST_RELOAD && m_axis_reload_tready)
r_rom_en <= 'b1;
else
r_rom_en <= 'b0;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_rom_addr <= 'd0;
else if(r_rom_addr == P_RELOAD_LENGTH - 1)
r_rom_addr <= 'd0;
else if(r_rom_en)
r_rom_addr <= r_rom_addr + 1;
else
r_rom_addr <= 'd0;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
rm_axis_reload_tvalid <= 'b0;
else if(rm_axis_reload_tlast & rm_axis_reload_tvalid)
rm_axis_reload_tvalid <= 'b0;
else if(r_rom_en)
rm_axis_reload_tvalid <= 'b1;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
rm_axis_reload_tlast <= 'b0;
else if(rm_axis_reload_tlast)
rm_axis_reload_tlast <= 'b0;
else if(rm_axis_reload_tvalid && r_rom_addr == P_RELOAD_LENGTH - 1)
rm_axis_reload_tlast <= 'b1;
else
rm_axis_reload_tlast <= 'b0;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
rm_axis_config_tvalid <= 'b0;
else if(rm_axis_config_tvalid & m_axis_config_tready)
rm_axis_config_tvalid <= 'b0;
else if(r_st_current == P_ST_CONFIG)
rm_axis_config_tvalid <= 'b1;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
rm_axis_config_tdata <= 'd0;
else if(rm_axis_config_tvalid & m_axis_config_tready)
rm_axis_config_tdata <= 'd0;
else if(r_st_current == P_ST_CONFIG)
rm_axis_config_tdata <= 'd1;
end
编写tb文件,进行实际测试,因为笔者默认导入的滤波器系数为5Mhz低通滤波器,所以可以看出,在该滤波器工作下,信号的波形并无明显变化。在i_reload_cmd指令拉高一个周期后,系数重载控制模块开始工作,滤波后波形发生变化,整体情况如下图所示
实际在该仿真中,笔者使用了DDS ip核进行了两个信号的产生,并进行了叠加。而DDS同样也支持在工作过程中,改变输出波形频率,有时间笔者也会对这一功能做篇文章,如果大家感兴趣的话。
整体来说,这个功能实现起来还是比较简单的,笔者也是个萌新,在学习过程中探索,如果有什么问题,欢迎大家在评论区批评指正。
如果觉得笔者的文章对您有些帮助,希望点个赞,激励下笔者的创作,不胜感激!
关于本文的完整matlab代码与vivado工程,可以私信博主免费领取!!!
标签:FIR,系数,IP,重载,reload,XILINX,config,reg,axis From: https://blog.csdn.net/zy12022000/article/details/142547774