目录
- 1 D触发器基础
- 1.1 创建D触发器
- 1.2 D触发器与复位功能
- 2 D触发器进阶
- 2.1 创建有特定功能的D触发器
- 2.2 D锁存器
- 3 D触发器与门电路的各种操作
- 3.1 选择器和触发器
- 3.2 门电路与触发器
- 3.3 边沿检测(重要)
1 D触发器基础
1.1 创建D触发器
D触发器是一种存储位的电路,并在时钟信号的(通常)上升沿定期进行更新。当使用clocked always block时,D触发器由逻辑合成器创建。
D触发器是“组合逻辑后跟触发器的blob”的最简单形式,其中组合逻辑部分只是一条线。
创建一个D触发器
solution:
module top_module (input clk, // Clocks are used in sequential circuitsinput d,output reg q );//// Use a clocked always block// copy d to q at every positive edge of clk// Clocked always blocks should use non-blocking assignmentsalways @(posedge clk) beginq <= d;endendmodule
创建8个D触发器。所有DFF应由clk的上升沿触发。
solution:
module top_module (input clk,input [7:0] d,output [7:0] q
);always @(posedge clk) beginq <= d;endendmodule
1.2 D触发器与复位功能
创建一个具有高电平同步复位功能的8位D触发器。所有D触发器都由时钟信号clk的上升沿触发。
异步Asynchronous:清零端与时钟无关,复位取决于clr(rst)的上升沿,always @(posedge clk or negedge rst)
同步Synchronous:复位的动作与时钟同步,复位取决于时钟的上升沿并且clr在高位,always @(posedege clk)
solution:
module top_module(input clk,input reset, // Synchronous resetinput [7:0] d,output [7:0] q
);always @(posedge clk) beginif (reset)q <= 0;elseq <= d;endendmodule
创建具有高电平有效同步复位的8 D触发器。触发器必须重置为0x34而不是零。所有DFF应由clk的下降沿触发。
提示:将寄存器重置为“ 1”有时称为“预设”。
solution:
module top_module(input clk,input reset,input [7:0] d,output [7:0] q
);always @(negedge clk) beginif (reset)q <= 6'h34;elseq <= d;endendmodule
创建一个具有高电平异步复位功能的8位D触发器。所有D触发器都由时钟信号clk的上升沿触发。
同步和异步复位触发器之间唯一的代码差异是灵敏度列表。
solution:
module top_module(input clk,input areset, // Asynchronous resetinput [7:0] d,output [7:0] q
);always @(posedge clk or posedge areset) beginif (areset)q <= 0;elseq <= d;endendmodule
2 D触发器进阶
2.1 创建有特定功能的D触发器
创建一个16位D触发器。有时候,只修改一组触发器的一部分是大有用处的。字节使能输入控制16个寄存器的每个字节是否应该在那个周期写入。byteena[1]控制高部分字节d[15:8],而byteena[0]控制低部分字节d[7:0]。
resetn是同步的、低电平有效。所有D触发器都由时钟信号clk的上升沿触发。
注意:这里的byteen有多种情况来控制字节的写入。
solution:
module top_module(input clk,input resetn,input [1:0] byteena,input [15:0] d,output [15:0] q
);always @(posedge clk) beginif (~resetn)q <= 0;else if (byteena[1] | byteena[0]) beginif (byteena[1])q[15:8] <= d[15:8];if (byteena[0])q[7:0] <= d[7:0];endendendmodule
2.2 D锁存器
实现下面的电路。
请注意,这是一个锁存器,因此会出现一个“having inferred a latch”的Quartus警告。
关于D触发器、D锁存器的概念,不妨查看这篇文章RS锁存器,D锁存器、D触发器简介。
由这篇文章可以知道,
- RS锁存器:当R(复位端)=S(置“1”端)=0时,输出保持不变,这保证了RS同时为0(断电)后,电路输出能够保持不变;
- 由于RS不可能同时变为0(电路时延不可能完全相同),那么就存在先后问题,就会给电路带来不确定性!因为我们不知道是谁先变成0,就更不知道输出会变成什么样!
- 为了解决RS锁存器带来的问题(RS不能同时为1),在此基础上,添加两个与门和一个非门,即可避免这种情况。升级版电路名字就叫D锁存器。当E端为0的时候,R端也会恒为0,S端则等于D端输入,亦即是此时输出直接等于输入。
提示:
- 锁存器是电平敏感的(非边沿敏感的)电路,因此在always块中,它们使用电平敏感的敏感度列表。
- 但是,它们仍然是顺序元素,因此应使用非阻塞分配。
- D锁存器在启用时的作用类似于导线(或同相缓冲器),而在禁用时保留当前值。
solution:
module top_module (input d, input ena,output q);always @(*) beginif (ena)q <= d;elseq <= q;endendmodule
实现下面的电路。
solution:
module top_module(input clk,input d, input ar, // asynchronous resetoutput q);always @(posedge clk or posedge ar) beginif (ar)q <= 0;elseq <= d;endendmodule
实现下面的电路。
solution:
module top_module(input clk,input d, input r, // synchronous resetoutput q);always @(posedge clk) beginif (r)q <= 0;elseq <= d;endendmodule
实现下面的电路:
module top_module (input clk,input in, output out);always @(posedge clk) beginout <= in ^ out;endendmodule
3 D触发器与门电路的各种操作
3.1 选择器和触发器
我们用3个包含触发器和多路选择器的子模块来实现图中电路。要求我们写出包含一个触发器和一个多路选择器的子模块。
module top_module(input clk,input L,input r_in,input q_in,output reg Q);wire temp;assign temp = L ? r_in : q_in;always@(posedge clk) beginQ <= temp; endendmodule
考虑一个 n-bit 移位寄存器。如上图所示,我们还是实现包含选择器和触发器的部分。
module top_module(input clk,input w, R, E, L,output Q
);wire temp;assign temp = L ? R :(E ? w : Q);always @(posedge clk) beginQ <= temp;endendmodule
3.2 门电路与触发器
如下图所示的状态机,假设D触发器在状态机启动之前初始化为0,实现该电路:
提示:本题为门电路与触发器的结合,上图包含三个触发器、异或门、与门和或门。只需要注意后两个触发器输出是取反即可。
module top_module(input clk,input x,output z
); reg temp1,temp2,temp3;assign z = ~(temp1 | temp2 | temp3);always @(posedge clk) begintemp1 <= x ^ temp1;temp2 <= x & ~temp2;temp3 <= x | ~temp3;endendmodule
⭐️
JK触发器的真值表如下图所示,仅使用D触发器和门电路来实现该JK触发器。其中Qold是D触发器在时钟上升沿之前的输出。
JK触发器和触发器中最基本的RS触发器结构相似,其区别在于,RS触发器不允许R与S同时为1,而JK触发器允许J与K同时为1。当J与K同时变为1的同时,输出的值状态会反转。也就是说,原来是0的话,变成1;原来是1的话,变成0。 对应表如下:
module top_module (input clk,input j,input k,output Q);always @(posedge clk) begincase ({j,k})2'b00: Q <= Q;2'b01: Q <= 0;2'b10: Q <= 1;2'B11: Q <= ~Q;endcaseendendmodule
3.3 边沿检测(重要)
⭐️ 输入上升沿的检测
对于每个8bit的变量,检测这些信号什么时候从0变为1(类似检测上升沿),输出应该在0到1 变化之后才有值。
下图给我们展示了输入in[1]和输出pedge[1]的时序关系图:
边沿检测经常用于按键输入检测电路中,按键按下时输入信号 key 变为低电平,按键抬起变为高电平。当输入的信号为理想的高低电平时(不考虑毛刺和抖动),边沿检测就发挥了很重要的作用。
由于输入的信号为一个连续值,我们需要通过时钟进行采样。根据采样定理,采样时钟的频率需要至少为被采信号频率的 2 倍。
module top_module (input clk,input [7:0] in,output [7:0] pedge
);reg [7:0] temp;always @(posedge clk) begintemp <= in;pedge <= ~temp & in; //检测上升沿用in(in由0变为1,temp是0),检测下降沿用~in(in由1变为0,temp是1)endendmodule
⭐️ 输入上升沿、下降沿的检测
对于每个8bit的变量,检测这些信号什么时候从0变为1(类似检测上升沿),输出应该在0到1 变化之后才有值。
下图给我们展示了输入in[1]和输出pedge[1]的时序关系图:
边沿检测的特性就是两边电平发生了变化,无非是0变1上升沿,1变0下降沿。
module top_module(input clk,input [7:0] in,output [7:0] anyedge
);reg [7:0] temp;always@ (posedge clk) begintemp <= in;anyedge <= temp ^ in; //anyedge <= (~temp & in) | (temp & ~in);endendmodule
🌟 捕获
对于32bit中的每一个变量,当输入信号从一个时钟周期的1变为下一个时钟周期的0时捕获,即我们需要捕获输入信号的下降沿。
其中捕获的意思就是说在寄存器复位之前,输出一直保持为 ‘1’ 。每一个输出bit类似SR触发器:输出信号从1变0发生时会保持一个周期。
输出会在时钟上升沿和reset信号为高时复位。如果上述事件在同一时间发生,reset有更高的优先级。在下图所示的最后4个周期内,reset信号比set信号早一个周期,所以没有冲突发生。
这个图大概是这个意思,out[1]是来捕获in[1]的下降沿的,即输出会在输入的下降沿后的一个周期,变为1,然后一直保持(类似于SR触发器,但是这里输出信号会长期保持)直到复位信号到来。紧接着,复位信号到来,那么输出信号在一个周期后置0,很巧的是,这时输入信号又一次产生了下降沿,于是过一个周期之后,out[1]变为了1。
根据题意,这里我们设置两个过渡的变量,一个用于记录输出捕获的in信号的下降沿,一个类似于前面一个例子中的temp,记录前一个输入的值。
solution:
module top_module (input clk,input reset,input [31:0] in,output [31:0] out
);reg [31:0] temp;wire [31:0] capture;always @(posedge clk) begintemp <= in;endassign capture = temp & ~in;integer i;always @(posedge clk) beginif (reset) out <= 0;else beginfor(i=0;i<=31;i++) beginif(capture[i])out[i] <= 1;//捕获的意思就是说在寄存器复位之前,输出一直保持为‘1’ endendendendmodule
🌟 双边沿检测
设计一个双边沿检测的触发器,时序如下图所示:
我们现在对时钟上升沿与下降沿都已经很熟悉了。但是FPGA没有一个同时检测双边沿的触发器,而且always中的敏感列表也不支持(posedge clk or negedge clk)。
本题相当于半个时钟周期上的输入信号的变化都可以检测出来,观察图可以知道q是d延后半个周期的波形,那么也就是说检测出d变化后q将进行翻转,而不是维持不变,本题有两种解题思路,一种是用MUX,一种是用XOR。
module top_module (input clk,input d,output q
);reg q1,q2;assign q = clk ? q1 : q2; //clk是1的话,说明前一阶段clk是上升沿;是0则说明前一阶段是下降沿always @(posedge clk) beginq1 <= d;endalways @(negedge clk) beginq2 <= d;endendmodule