本文介绍两个问题
1.如何使用matlab生成正弦波的.coe文件并在Vivado中使用ROM例化输出正弦波,设计滤波器对正弦波滤波。
2.ROM生成的正弦波波形是乱的,不平滑问题的解决。
使用matlab生成正弦波.coe文件
depth = 4096; %存储器的单元数4096
widths = 16; %数据宽度为16位
fidc = fopen('sine.coe','wt'); %给文件起名字
fprintf(fidc , 'memory_initialization_radix=%d;\nmemory_initialization_vector=\n',10);
for x = 1 : depth-1
fprintf(fidc,'%d,\n',round((2^(widths-1)-1)*sin(2*pi*(x-1)/4096)+2^(widths-1)));
end % 加上直流分量2^(widths-1) 保证值为正范围为(1,2^16-1)
fprintf(fidc,'%d;\n',round((2^(widths-1)-1)*sin(2*pi*(depth-1)/4096)+2^(widths-1)));
fclose(fidc);
设置存储器也就是Vivado中的ROM核的深度为4096,数据位宽为16,使用fidc作为文件标识符打开或新建一个名为sine.coe的文件并准备写入,设置数据进制为10,依次向fidc文件写入正弦波的值。给正选波加上直流分量2^(widths-1)是为了保证输出值是正数,并且范围为(1,2^16-1)。最最后使用fclose(fidc)
来关闭文件,以确保数据正确保存并释放系统资源。
保存运行后,会在跟此matlab脚本文件同一文件夹内生成.coe文件
然后在Vivado中单击IP Catalog搜索Rom 双击Distributed Memory Generator
Distributed Memory Generator 适用与数据量较少的情况,如果数据量较大深度较大推荐使用下面的Block Memory Genertor。
如下图所示 配置ROM 深度为4096,数据宽度为16
输入输出都选择寄存
如下图所示,点击按钮选中使用matlabn生成的那个.coe文件
进制设为10进制,点击OK
接下来创建一个design source文件
代码如下
`timescale 1ns / 1ps
module rom(
input clk, //100M
input rst,
output [15:0] data_3M,
output [15:0] data_19M
);
parameter fre_word1 = 12'd122;// fre_word = f_out * 2^N / fclk N为累加器位宽
//N 为地址的位宽 实际122.8,不四舍五入
parameter fre_word2 = 12'd778;
reg [11:0] addr_sin_3m;
reg [11:0] addr_sin_19m;
//相位累加器
always @(posedge clk or rst)
begin
if(!rst)begin
addr_sin_3m <= 12'd0;
addr_sin_19m <= 12'd0;
end
else begin
addr_sin_3m <= addr_sin_3m + fre_word1;
addr_sin_19m <= addr_sin_19m + fre_word2;
end
end
wire [11:0] addra1 = addr_sin_3m[11:0];
wire [11:0] addra2 = addr_sin_19m[11:0];
dist_mem_gen_0 Rom_3M (
.a(addra1),
.clk(clk),
.qspo(data_3M)
);
dist_mem_gen_0 Rom_19M (
.a(addra2),
.clk(clk),
.qspo(data_19M)
);
endmodule
代码中将上面我们创建的ROM IP核例化了两次分别命名为Rom_3M和Rom_19m
需要注意的是频率步进字fre_word = f_out * 2^N / fclk 的计算,其中fout代表想要输出的频率,例如我这里想输出3M那么fout = 3*10^6,fclk是代码中使用的时钟,我的时钟是100M,那么fclk = 100*10^6,N代表累加器的位宽,在这里就是ROM的地址端口的位宽如下图为12位,那么要输出3M频率的正弦波,我的频率步进字fre_word = 3*10^6 * 2^12/ 100*10^6 = 122.88,取122。输出19M同理。
接下来创建一个simulation sources文件
代码如下
`timescale 1ns / 1ps
module rom_sim();
reg clk;
reg rst;
wire [15:0] data_3M;
wire [15:0] data_19M;
wire [39:0] wave_out; //要看 IP端口的位数,而不是Summary中的 Output Width
reg [16:0] wave_sum; //3M正弦波和19M正弦波相加
wire [16:0] wave_inn; //将3M和19M的合除以4
wire [15:0] wave_in; //将wave_inn的结果截取16位,符合fir滤波器ip核的input width
wire s_axis_data_tready;
wire m_axis_data_tvalid;
always #5 clk = ~clk;
always@ (posedge clk or rst)begin
if(!rst)
wave_sum = 17'd0;
else begin
wave_sum = data_3M + data_19M;
end
end
assign wave_inn = wave_sum>>2; //除以四
assign wave_in = wave_inn[15:0];
initial begin
clk = 1'b0;
rst = 1'b0;
#1 rst = 1'b1;
end
rom rom_inst(
.clk(clk),
.rst(rst),
.data_3M(data_3M),
.data_19M(data_19M)
);
fir_compiler_0 fir (
.aclk(clk), // input wire aclk
.s_axis_data_tvalid(1'b1), // input wire s_axis_data_tvalid
.s_axis_data_tready(s_axis_data_tready), // output wire s_axis_data_tready
.s_axis_data_tdata(wave_in), // input wire [15 : 0] s_axis_data_tdata
.m_axis_data_tvalid(m_axis_data_tvalid), // output wire m_axis_data_tvalid
.m_axis_data_tdata(wave_out) // output wire [39 : 0] m_axis_data_tdata
);
endmodule
如图测试代码中例化了一个FIR滤波器,这个滤波器的.coe文件由matlab滤波器产生,测试代码中"wave_inn = wave_sum>>2; " 3m和19m合路信号为什么相加后要除以4下面来解释。
下面介绍使用matlab生成FIR滤波器系数文件
首先打开matlab,如图所示点击APP - 显示更多
下拉找到如图所示滤波器设计工具,别的matlab版本也叫Fliter Designer
按照下图所示配置FIR模式设为最小二乘法,Fs采样时钟设为100Mhz,这个要跟Vivado中的时钟设为一致,通带频率Fpass设为8M,即允许8Mhz以下的信号通过,截止频率Fstop设为15Mhz
如下图点击左侧按钮,设置滤波器算法为定点,分子字长设为16位,即为输入数据宽度
点击输入/输出,可以看到滤波器的输入范围为(-1,1),超出这个范围会出错。因此在我们输入数据位宽为16位的情况下,输入应该是(-2^16 +1,2^16 - 1),并且输入时是带符号数,即最高位是符号位,在matlab中我们的正弦波值为加上直流分量的值round((2^(widths-1)-1)*sin(2*pi*(x-1)/4096)+2^(widths-1)));值的范围是(1,2^16 -1),这个值代表无符号数的值,要除以2将最高位的符号位空出来才能表示有符号数,否则会出错,因此单路rom出来的正弦波要除以2才能送入滤波器,那么两路rom出来的正弦波加一块要除以4才能送入滤波器。
点击应用后,点击目标生成XILINX系数指定文件名生成.coe文件
接下来在Vivado中找到fir滤波器ip核
双击配置fir ip核,点击下图所示按钮将刚才生成的滤波器.coe文件导入
将采样频率和时钟频率都设为100m
输入宽度设为16位,数据类型为signed
最终配置结果如下所示
点击OK,ip核即配置完成
回到我们的sim文件,点击Run Simlaution
打开ModelSim文件,如下图所示设置波形为进制为Decimal,模拟波形
发现data_3m和data_19m的波形不太对,原因是因为他们没有除以2之前还代表无符号数,因此将他们和wave_sum的数据类型改成unsigned后波形正常,而wave_out和wave_in本来就是有符号数因此波形表示正常,如图所示,3M和19M信号进入滤波器后成功将高频信号滤除,留下3M的信号。