本系列内容来自于知乎专栏,链接如下:https://zhuanlan.zhihu.com/c_1131528588117385216 本系列文章将和读者一起巡礼数字逻辑在线学习网站 HDLBits 的教程与习题,并附上解答和一些作者个人的理解,相信无论是想 7 分钟精通 Verilog,还是对 Verilog 和数电知识查漏补缺的同学,都能从中有所收获。
Problem 80 : D flip-flop (Dff)
接下来的题目是属于触发器,锁存器的专题。我们会从用 Verilog 实现基础 D 触发器开始,学习触发器这一数字电路中最重要的电路之一。
D 触发器是一个电路,存储 1bit 数据,并定期地根据触发器的输入(d)更新这 1 bit 数据,更新通常发生在时钟上升沿(clk)。存储的数据会通过输出管脚(q)输出。
t1时刻: d -> 0
t2时刻: clk->1 上升沿到来,触发器存储的数据变成 0,输出 q 保持为存储的值:0,直到下一个时钟上升沿到来。
t3时刻: d -> 1(d:我变了),q 仍保持 0 不动摇(时钟沿还没来呢)
t4时刻: clk->1 上升沿到来,q->1(q:时钟沿来了,我该变身了)
绝大多数时候,我们不会在 Verilog 代码中显示例化一个触发器(作者没这么做过,但应该是可以做的),我们在时钟敏感的 always 块中的语句一般都会被综合工具转换为相应的触发器。
D 触发器可以认为是一个触发器和一段最简单的组合逻辑块(blob :想表达逻辑块的时候用我,别用 block)的组合。其中组合逻辑块仅仅是一段 wire。(q 直接输出了触发器的存储值)
牛刀小试
造个 D 触发器吧。
解答与分析
代码语言:javascript复制module top_module (
input clk, // Clocks are used in sequential circuits
input 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 assignments
always@(posedge clk) begin
q <= d;
end
endmodule
在每个时钟上升沿,输出 q 的值变为输入 d 的值。我们在 always 块中的语句就会被综合工具转换为触发器。这里使用 clk,q,d 对应于触发器的三个端口,反应了转换的对应关系。
上图是触发器在 ISE 中的综合结果,fd 是多种触发器中的基础款,我们会在后续的题目中认识更多款式。
Problem 81 : D flip-flops (Dff8)
牛刀小试
本题需要你实现 8 个 D 触发器。
解答与分析
代码语言:javascript复制module top_module (
input clk,
input [7:0] d,
output [7:0] q
);
always@(posedge clk) begin
q <= d;
end
endmodule
实现 8 个 D 触发器,听上去好像很累的样子,实则 Verilog 语言的抽象帮助我们省去不少麻烦。输入 a,b 的位宽变为 8 位,但 always 块中的语句与上一题完全相同。
综合工具根据位宽,综合出了 8 个 D 触发器。一般没有八位触发器的说法。这里反映了综合工具能分析代码,生成相应的触发器电路,其实综合器还能将复杂得多的语句转为相应的电路。
Problem 82 : DFF with reset (Dff8r)
牛刀小试
在上题的 8 个 D 触发器基础上,这题我们要给触发器配上同步复位端口。
什么同步复位?当时钟上升沿到来时,如果同步复位端有效(本题中复位高电平有效,即 reset),那么任凭你触发器此前输出或者输入的是 0,是 1,输出一律变为 0。
复位电路对于那些经常需要恢复到初始状态的电路是必要的,复位相较于断电重新加载程序恢复到初始状态的速度要快得多。但也有一些电路则不需要复位设计。(作者也是有所耳闻那些不需要复位的电路,平常自己还是会加上复位电路)
解答与分析
代码语言:javascript复制module top_module (
input clk,
input [7:0] d,
output [7:0] q
);
always@(posedge clk) begin
if(reset)
q <= 8'b0;
else
q <= d;
end
endmodule
从语法的角度来说,reset 实现就是加一个 if 语句判断 reset 是否有效,有效就将输出 q 置为 0 。
但从电路的角度来说,电路的角度往往更加重要,是使用一个带有复位端的 D 触发器 fdr,另一种 D 触发器单元。
Problem 83 : DFF with reset value (Dff8p)
牛刀小试
与上题的不同点在于,复位后的值不再是 0 (那么即为 1,对于 1bit 来说也没别的选择了),另外触发器的触发事件从时钟上升沿变成了时钟下降沿,这些在语法都没什么好提的。
解答与分析
代码语言:javascript复制module top_module (
input clk,
input reset,
input [7:0] d,
output [7:0] q
);
always@(negedge clk) begin
if(reset)
q <= 8'h34;
else
q <= d;
end
endmodule
从电路的角度来说,时钟下降沿触发,如果仍要使用上升沿触发的触发器,则通过将输入触发器的时钟取反实现。
复位后的值为 1,那么通常我们可以管这种复位为置位,在电路中使用 fds ,带有置位端 s 的触发器实现。
Problem 84 : DFF with asynchronous reset (Dff8ar)
牛刀小试
本题中的触发器引入了异步复位。当异步复位端有效时,触发器的输出复位为 0 。
对于上上题中引入的同步复位来说,存在一个问题。
我们假设一个工作时钟为 1Hz 的系统,比如你的床头闹钟,你按下隆隆叫的闹钟时,就好比按下了闹钟的同步复位键。那么当你清晨 6:30:00 的闹钟响起,不想起床的你拍下闹钟,最糟糕的情况下,闹钟还会再响接近一秒,因为你刚好错过了一个时钟上升沿,真是糟糕,这真是太可怕了!
对于同步复位系统来说,当同步复位事件发生时,等到下一个时钟上升沿才会得到响应,响应的速度比较慢。
与之相对的异步复位的响应就很快,因为在异步复位有效的时刻,复位响应就会发生,好像戳破气球一般。
解答与分析
代码语言:javascript复制module top_module (
input clk,
input areset, // active high asynchronous reset
input [7:0] d,
output [7:0] q
);
always@(posedge clk or posedge areset) begin
if(areset)
q <= 8'b0;
else
q <= d;
end
endmodule
多次重复,之后仍然会提及:语法的实现很简单。将异步复位加入 always 块的敏感列表当中,在电路上则又出现了一位新的小伙伴( ̄ω ̄( ̄ω ̄〃 ( ̄ω ̄〃)ゝ:fdc,它有一个异步复位端 CLR。