今天重新复习了D触发器。
D触发器就是个真正的reg,时钟的有效沿到来时,输出值更新为输入值,其他时候输出值保持不变,与输入无关
reg q;
always@(posedge clk)begin
q<=d;
end
//clk是非常重要的概念
//低端FPGA如果跑软核,一般时钟频率100MHz
1.方波越窄,就要用越高频的正弦信号叠加
2.时钟是FPGA里面最繁忙的信号
3.当上升沿来临时,把d的值给q
4.上升沿是posedge,下降沿negedge
5.
Synchronous reset Asynchronous reset
同步复位:听时钟的话;
异步复位:不听时钟的话
6.reset表示高有效,reset_n表示低有效//养成良好的代码习惯!
7.但是时钟是FPGA最重要的东西,所以一般少用异步复位,多用同步复位
8.!!!如果没有重要的特殊功能,一般用同步复位。
1.D触发器要能锁存正确的数据,要求:
I.数据在时钟有效沿来到之前,必须稳定一段时间,称为“建立时间”Tsu;
II.数据在时钟有效沿来到之后,必须稳定一段时间,称为“保持时间”TH;
2.注意,Modesim的功能仿真,D触发器都是理想的
I.不需要建立和保持时间,如果数据变化与时钟有效沿同时出现,将锁存到数据变化后的新值(e.g.1),有因果关系的除外(e.g.2)
II.因此,建议在功能仿真时,将时钟与数据变化错开半个时钟周期,以免与实际情况混淆
III.仿真时,需要对D触发器赋初值,理想的D触发器初始值为X
两种方法1.initial 2.复位reset
(Flip-Flop)
1.自学格雷码计数、BCD码使用方法(2016.7.7需要学习数字电路)
module xxx(……);
……
reg [7:0] q;
always@(posedge clk) begin
q <= q + 8’b1;
end
reg [7:0] gray;
always@(posedge clk) begin
gray <= q ^ (q >> 1);
end
……
Endmodule
2.练习1:描述JK触发器,并仿真,注意使用initial块对寄存器赋初始值
module JKtest(input clk, input J,input K,output reg Q ); initial Q<=1; reg d; always@(posedge clk) begin d=(J & ~Q )| (~K & Q ); Q<=d; end endmodule
|
激励块://根据自己需要自行设计
module testbench ;
wire Q ; //output reg型对应是wire型!! reg J ; reg clk ; reg K ; JKtest DUT ( .Q (Q ) , .J (J ) , .clk (clk ) , .K (K ) ); initial begin clk = 1'b0; J=1'b0; K=1'b0; end always #10 clk=~clk; always #15 J=~J; always #20 K=~K;
initial #1000 $stop; endmodule |
1.T触发器
计数器
1.八位计数器(不一定自成模块)
module XXX(...)
...
reg[9:0] cnt;
always@(posedge clk) begin
cnt <=cnt + 10’b1;
end
...
endmodule
2.带有复位
...
Reg [9:0] cnt;
always@(posedge clk) begin
If(rst) cnt <=10’b0;
Else cnt <=cnt + 10’b1;
End
...
3.计到特定值(模)
...
Reg[9:0] cnt;
always@(posedge clk) begin
If(cnt< 10’d999) cnt<=cnt + 10’b1;
Else cnt <=10’b0;
End
...
Tips:这可以实现一个数字域的锯齿波(单极性)
4.计到特定范围
...
Reg signed [9:0] cnt;
always@(posedge clk) begin
If(cnt < 10’sd499) cnt <= cnt +10’sd1;
Else cnt <= -10’sd500;
End
...
Tips:这可以实现一个数字域的锯齿波(双极性)
练习2:编写testbench,仿仿看带有复位输入、进位输出的模M计数器
编写感想:Modesim自动生成固然方便,但是Function定义就不会自动加上,让我迷惑了好久
`timescale 100ns/1ns
module testbench ;
parameter M = 60 ;
wire [Log2(M-1) : 0]cnt ;
reg rst ;
wire co ;
reg clk ;
function integer Log2(input integer x);
for(Log2 = 0; x > 1; x = x >> 1)
Log2 = Log2 + 1;
endfunction
initial clk=1'b0;
counterM #( M )
DUT (
.cnt (cnt ) ,
.rst (rst ) ,
.co (co ) ,
.clk (clk ) );
always
#10 clk=~clk;
initial begin
rst=1'b0;
#1000 rst=1'b1;
end
endmodule
分频(很大的坑来了啊)
问题:50MHz的时钟下,如果想让一个计数器每毫秒计一次怎么做?
可以用一个模50000的计数器(每毫秒计一圈)的最高位作为时钟!?
不行!!但凡在FPGA用到时钟必须谨慎,时钟在FPGA是神圣的,不能随便乱用。要求大家,写always块时,用到的时钟模块,必须用同一个clk!!(特殊情况以后再学)
可以用使能来做 ,都是同一个clk
module XXX(……); …… reg [15:0] cnt50k; always@(posedge clk) begin if(cnt50k < 16’d49999) cnt50k <= cnt50k + 16’b1; else cnt50k <= 16’b0; end wire enPer1ms = (cnt50k == 16’d49999);//每毫秒一个高电平 reg [9:0] cntPer1ms; always@(posedge clk) begin if(enPer1ms) cntPer1ms <= cntPer1ms + 10’b1; // end …… endmodule
|
2.用于传递事件的使能
编写testbench,仿仿看,注意不要“仿一天”,改为50Hz,26’d49
3.使能去抖
正确的做法,先把key的每个下跳沿处理为单个使能信号
module XXX(input key, ……);//并没有用两个reg,而使用了两位的寄存器 reg [1:0] keyDly; always@(posedge clk) begin keyDly <= {keyDly[0], key};//位拼接,keyDly【0】赋给了高位 end wire keyPress = (keyDly == 2’b10); reg [9:0] cntKey; always@(posedge clk) begin if(keyPress) cntKey <= cntKey + 10’b1; end …… endmodule
|
Tips:复习位拼接操作符{,}
位拼接操作符可以将多个操作数连接起来,组成一个操作数。拼接操作符的每个操作数必须是有确定位宽的数,这是由于为了确定拼接结果的位宽,必须知道每个操作数的位宽,因此无位宽的数不能作为拼接操作符的操作数
E.g.
//a=1’b1,b=2’b00,c=2’b10,d=3’b110 Y={b , c}//结果为4’b0010 Y={a , b , c , d, 3’b001}//结果为11’b10010110001 Y={a,b[0],c[1]}//结果为3’b101 |