本次实验要实现的是一个三级抽取CIC滤波器,抽取系数为64。回顾上一章节中的CIC滤波器结构,可以发现其硬件实现是非常简单的,积分器的部分通过加法器与D触发器即可实现,降采样通过分频器实现,梳状器的部分则通过减法器和触发器实现。
编写分频器的verilog实现,其输入信号为时钟信号clk与复位信号rst_n,输出信号为64倍分频后的时钟信号clk_div。
分频器使用计数器实现。当复位信号为低时,计数器值复位为0,clk_div输出为0;当复位信号为高时,计数器在时钟clk的每个上升沿计数,当第32个上升时钟沿到来时clk_div进行翻转,由于计数器位数为5位,第32个上升时钟沿到来的同时计数器也清零了,直到下一次的第32个上升时钟沿再重复上述过程,故clk_div两次翻转的时间(一个周期)为64个clk周期,从而实现了64倍分频。
分频器的verilog代码实现如下:
登录后复制
module divider(
input clk,
input rst_n,
output reg clk_div
);
reg [4:0]count;
always @(posedge clk or negedge rst_n) begin
if (rst_n == 0) begin
count <= 5'd0;
clk_div <= 1'b0;
end
else if (count < 31) begin
count <= count + 1;
clk_div <= clk_div;
end
else begin
count <= count + 1;
clk_div <= ~clk_div;
end
end
endmodule
编写CIC滤波器的verilog实现,其输入信号为时钟clk,复位信号rst_n以及调制器输入信号in,输出信号为量化值out。
由于积分运算会导致数据位宽变宽,需要通过公式计算
\[B_{out}=B_{in}+Nlog_2(RM)
\]
公式中,\(B_{out}\)为输出数据的位数,\(B_{in}\)为输入数据的位数,\(N\)为滤波器级数,\(R\)为滤波器阶数,\(M\)为降采样系数。代入数据后可以计算得到所需要的位数为19位,因此设计寄存器位宽为19。
需要注意的是梳状器作为降采样部分的后级,所有的时钟信号都需要使用分频器64倍分频后的时钟clk_div。
CIC滤波器的verilog实现代码如下:
登录后复制
module cic_filter(
input clk,
input rst_n,
input in,
output [18:0] out
);
reg [18:0]out_reg;
wire clk_div;
reg [18:0]sum1,sum2,sum3;
wire [18:0]sum1_nxt,sum2_nxt,sum3_nxt;
// 积分器加法器部分
assign sum1_nxt = sum1 + in;
assign sum2_nxt = sum2 + sum1;
assign sum3_nxt = sum3 + sum2;
// 积分器D触发器部分
always @(posedge clk or negedge rst_n) begin
if (rst_n == 0) begin
sum1 <= 19'b0;
sum2 <= 19'b0;
sum3 <= 19'b0;
end
else begin
sum1 <= sum1_nxt;
sum2 <= sum2_nxt;
sum3 <= sum3_nxt;
end
end
// 调用分频器
divider div(
.clk(clk),
.rst_n(rst_n),
.clk_div(clk_div)
);
reg [18:0]sub1,sub2,sub3;
wire [18:0]sub1_nxt,sub2_nxt,sub3_nxt;
// 梳状器减法器部分
assign sub1_nxt = sum3_nxt - sub1;
assign sub2_nxt = sub1_nxt - sub2;
assign sub3_nxt = sub2_nxt - sub3;
// 梳状器D触发器部分
always @(posedge clk_div or negedge rst_n) begin
if (rst_n == 0) begin
sub1 <= 19'b0;
sub2 <= 19'b0;
sub3 <= 19'b0;
end
else begin
sub1 <= sum3_nxt;
sub2 <= sub1_nxt;
sub3 <= sub2_nxt;
end
end
// 输出
always @(posedge clk_div or negedge rst_n) begin
if (rst_n == 0) begin
out_reg <= 0;
end
else begin
out_reg <= sub3_nxt;
end
end
assign out = out_reg;
endmodule
编写testbench用于仿真,文件1k1000mv.txt中存储了一段∑-Δ调制器对一个周期的正弦波采样后输出的码流,通过系统任务$readmemb将其读入声明好的存储器mem中,并将数据按次序以1位码流的形式进行输出到CIC滤波器的输入端口in,此外在testbench中设置时钟信号clk周期为156.25ns,复位信号rst_n在仿真开始500ns后由低电平变为高电平。
testbench的verilog实现代码如下:
登录后复制
`timescale 1ns/1ns
`define period 78.125
module testbench;
// input
reg clk,rst_n,in;
// output
wire [18:0]out;
// 设置时钟周期为156.25ns
always #`period clk <= ~clk;
// 初始化
initial begin
rst_n <= 1'b0;
clk <= 1'b0;
#500;
rst_n <= 1'b1;
end
integer i;
// 定义存储器mem
reg mem[0:3000000];
// 将1k1000mv.txt文件读入mem
initial $readmemb("1k1000mv.txt",mem);
// 将mem中数据次序输出到in
always @(posedge clk or negedge rst_n) begin
if(rst_n == 0) begin
i = 0;
in <= 0;
end
else begin
in <= mem[i];
i = i + 1;
end
end
// 调用cic滤波器
cic_filter cic(
.clk(clk),
.rst_n(rst_n),
.in(in),
.out(out)
);
endmodule
1.打开Modelsim,File---->New---->Project.. 建立新工程,命名为CICFilter,并设置工程路径
2.将编写好的verilog文件、1k1000mv.txt全部放到工程路径下
3.在Modelsim的Project标签页中右键,点击Add to Project ---> Existing File... 选择工程路径下的verilog文件,Add file as type选为Verilog。将三个verilog文件全部添加进工程。
4.在Modelsim的Project标签页中右键,点击Compile ---> Compile All,编译全部verilog文件
5.若编译成功,下方Transcript窗口会输出编译成功的信息。若提示失败则根据报错信息修改verilog并再次编译。
6.点击Simulate--->Start Simulation... 在弹出的窗口中,点击work左侧的小加号展开,在展开的栏目中点击选中testbench,进入仿真环境。
7.在仿真环境中①为Instance窗口,所有例化的模块都会显示在其中;②为Objects窗口,选中①中的模块后,该窗口会对应显示模块中的信号,寄存器,存储器等;③为Wave窗口,用于显示仿真波形。在Instance窗口中点击testbench,在Objects窗口中选中out并右键,Add to ---> Wave ---> Selected Signals,将out信号加入Wave窗口
8.以同样的方式将in信号也添加进Wave窗口
9.修改右上角仿真时间,设置为1ms,再点击其右侧的run图标
10.此时Wave窗口应出现绿色波形,点击左侧小放大镜自动缩放波形时间轴
11.在Wave窗口中右键out信号,Format ---> Analog(automatic)
12.可以看到一个周期正弦波信号
13.多次点击run图标运行仿真,可以看到多个周期的正弦波
14.使用左侧放大镜功能调整波形横轴分度,可以看到在out值大的部分(波峰),in信号是以1为主的码流,在out值小的部分(波谷),in信号是以0为主的码流,和上一章节的调制器输出与CIC滤波器输出的关系是一致的。
至此完成了CIC滤波器的Verilog编写及前仿真的实验流程。
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删