(1)单Bit数据的异步信号同步 (2)边沿检测 (3)计数器模块设计(4)在仿真测试激励文件中引入随机数发生函数
(1)常见的同步方法使用两级触发器,使用触发器对信号打两拍的方式进行与系统时钟同步
上述电路用verilog语言描述出来为
reg key_in_a,key_in_b;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
key_in_a <= 1'b0;
key_in_b <= 1'b0;
end
else begin
key_in_a <= key_in;
key_in_b <= key_in_a;
end
(2)边沿检测
它的原理图和异步信号同步类似,都使用了两个寄存器。使用D触发器存储两个相邻时钟上升沿时外部输入信号(已经同步到系统时钟域中)的电平状态。有一个需要注意的点:上升沿和下降沿
reg key_tmpa,key_tmpb;
wire pedge,nedge;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
key_tmpa <= 1'b0;
key_tmpb <= 1'b0;
end
else begin
key_tmpa <= key_in_sb;
key_tmpb <= key_tmpa;
end
assign nedge=(!key_tmpa)&key_tmpb;
assign pedge=key_tmpa & (!key_tmpb);
(3)计数器模块设计
在状态转移表中应有20ms计数器模块以及计数器使能模块。
20ms=20000000ns,要计数1000000次
计数器:只有当给到计数使能信号的时候才开始计数,否则处于清零状态。
计数器使能模块,告诉状态机已经计数满了。
reg [19:0]cnt;
reg en_cnt;
always@(posedge Clk or nedege Rst_n)
if(!Rst_n)
cnt <= 20'd0;
else if(en_cnt)
cnt <= cnt + 1'b1;
else
cnt <= 20'd0;
reg cnt_full;
always@(posedge Clk or nedege Rst_n)
if(!Rst_n)
cnt_full <= 1'b0;
else if(cnt == 20'd999_999)
cnt_full <= 1'b1;
else
cnt_full <= 1'b0;
(4)状态机这一部分的代码能够根据语言描述编写出来,但是IDEL和DOWN这两段状态里的key_flag容易忘记。在写复位状态时,记得把default状态也写上,要不容易忘记。
(5)任务及随机函数的使用
一般用法是$random%b,b>0,生成一个范围在(-b+1):(b-1)中的随机数。如果只得到正数的随机数,可采用{$random}%b来产生。需要产生20ms以内的按下抖动和松手抖动,应该产生25'd20_000_000以内随机数的抖动。为了节约仿真时间,只产生16位的随机数也就是0-16'd65535。
/* 随机数发生函数 */
/* 实现50次的0-65535ns按下抖动然后key_in赋固定值0且延时50ms */
reg [15:0]myrand;
task press_key;
begin
repeat(50)begin
myrand = {$random}%65535;
#myrand key_in=~key_in;
end
key_in =0;
#50000000;
repeat(50)begin
myrand = {$random}%65535;
#myrand key_in=~key_in;
end
key_in =1;
#50000000;
end
endtask
initial begin
Rst_n = 1'b0;
key_in = 1'b1;
#(`clk_period*10)
Rst_n = 1'b1;
#30000;
press_key; #10000;
press_key; #10000;
press_key; #10000;
$stop;
end
(6)把(5)中的随机函数和任务放在key_model.v中,在key_tb中调用。在仿真设置中需要注意图中的几个点,把key_model添加到key_tb中。