先说一个今天碰到的modelsim报错
原因在于代码模块名和文件名不一致,改成一致就不报错了
下面是modelsim波形图时间间隔调整,时间间隔,一般调成ns
下图是设置波形图数字进制,b是二进制,d是十进制,h是十六进制
下图左边红线是加黄色辅助线或者减少辅助线,右边红线是辅助线对准下一个上升沿或者下降沿
首先是计数器的框图,sys_clk是时钟信号,sys_rst_n是复位信号,cnt是中间变量起到计数功能。在开发板上以led灯实现,想做到1s中,前半秒led_out=0低电平亮灯,后半秒led_out=1高电平灭灯的效果。1s是50MHz,cnt计数到一半也就是24999999归零从新计数。
下面是verilog代码
module counter
#(parameter CNT_MAX = 25'd24_999_999)
(
input sys_clk ,
input sys_rst_n ,
output reg led_out
);
reg [24:0] cnt;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 25'd0;
else if(cnt == CNT_MAX )
cnt <= 25'd0;
else
cnt <= cnt + 25'd1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
led_out <= 1'b0;
else if(cnt == CNT_MAX )
led_out <= ~led_out;
else
led_out <= led_out;
endmodule
这里用到了parameter函数,就是定义一个数值,模块头部就可以随时修改数值,方便以后修改。
下面是写的tb文件,这边建议归零计数间隔缩小,有助于验证逻辑代码,毕竟50M的一半太大了。这里间隔设置的24ns
`timescale 1ns/1ns
module tb_counter();
reg sys_clk;
reg sys_rst_n;
wire led_out;
initial
begin
sys_clk = 1'b1;
sys_rst_n = 1'b0;
#20
sys_rst_n = 1'b1;
end
always #10 sys_clk = ~sys_clk;
initial
begin
$timeformat(-9,0,"ns",6);
$monitor("@time %t : led_out = %b ",$time,led_out);
end
counter
#( .CNT_MAX (25'd24))
counter_inst
(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.led_out (led_out)
);
endmodule
接下来是6分频,也就是每6个时钟周期就反转一次,创建一个新的时钟信号
这里有个问题,这种分频方式(分频)在低速下是没有问题的,高速下是有问题的。原因在于高速下下面那个6分频信号时间间隔太大了,与最原始的时钟信号sys_clk脉宽差太多了。所以一般采用下面方法(降频)6分频,不产生新的时钟信号,而是在没6个时钟周期产生一次高电平提醒。
详细差别如下所示,同样是每6个时钟周期a+1的指令,下面是第一种分频
always@(posedge clk_out or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
a<= 25'd0;
else
a <= a + 1'b1;
这是第二种降频
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
a<= 25'd0;
else if(cnt_flag == 1 )//这是第二种用cnt_flag做标志a+1,本质还是sys_clk做时钟信号
a <= a + 1'b1;
else
a <= a ;
接下来是6分频的rtl代码,注释里面是第一种分频方式,注释外面是第二种分频方式降频
module divider_six
(
input wire sys_clk,
input wire sys_rst_n,
output reg clk_flag
//output reg clk_out
);
reg [2:0] cnt;
/*always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 2'd0;
else if(cnt == 2'd2)
cnt <= 2'd0;
else
cnt <= cnt + 2'd1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
clk_out <= 1'b0;
else if(cnt == 2'd2)
clk_out <= ~clk_out;
else
clk_out <= clk_out;*/
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 3'd0;
else if(cnt == 3'd5)
cnt <= 3'd0;
else
cnt <= cnt + 3'd1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
clk_flag <= 1'b0;
else if(cnt == 3'd4)
clk_flag <= 1'b1;
else
clk_flag <= 1'b0;
endmodule
接下来是tb文件代码
`timescale 1ns/1ns
module tb_divider_six();
reg sys_clk;
reg sys_rst_n;
wire clk_flag;
initial
begin
sys_clk <= 1'b0;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk <= ~sys_clk;
divider_six divider_six_inst
(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.clk_flag (clk_flag)
);
endmodule
分频器本质其实就是计数器,核心都是计数器。