3. 时序逻辑基础:always块、reg类型、时钟与复位、D触发器的Verilog描述
好,咱们今天聊点硬核的。时序逻辑,说白了就是带记忆的电路。组合逻辑只管当前输入,时序逻辑可不一样——它记得过去的状态。我刚开始学的时候,总觉得这玩意儿玄乎,后来才发现,其实就是几个核心概念串起来的事。
3.1 always块——时序逻辑的“心脏”
在Verilog里,描述时序逻辑全靠always块。组合逻辑也用always,但写法完全不同。时序逻辑的always块,敏感列表里通常只放时钟和复位。
我个人的习惯是:时钟沿触发,复位异步清零。这样写出来的代码,综合工具一眼就能认出来是寄存器。
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0;
else
q <= d;
end
你看,这个写法很经典。posedge clk表示时钟上升沿触发,negedge rst_n表示低电平复位。注意这里的rst_n,我习惯用_n后缀表示低有效,这是很多公司的编码规范。
3.2 reg类型——别被名字骗了
很多新手会犯一个错误:以为reg就是寄存器。其实不是。reg只是表示一个变量类型,它可以在always块里被赋值。组合逻辑里也可以用reg,只不过综合出来的是连线。
说白了:在时序逻辑里,reg综合成触发器;在组合逻辑里,reg综合成连线。关键看你怎么用。
我举个例子:
reg [3:0] counter; // 这个reg,在时序逻辑里就是4位计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
counter <= 4'b0;
else
counter <= counter + 1'b1;
end
这个counter,综合出来就是4个D触发器。为什么?因为它在时钟沿赋值,有记忆功能。
3.3 时钟与复位——设计的“脉搏”
时钟是时序逻辑的脉搏。没有时钟,触发器就不知道什么时候该干活。复位则是让电路回到初始状态。
我遇到过不少项目,复位设计没做好,导致上电后状态不确定。这里分享几个经验:
- 时钟要干净:不要用组合逻辑产生时钟,除非你很清楚自己在做什么。门控时钟容易出毛刺。
- 复位要可靠:异步复位虽然方便,但要注意复位释放时的时序。同步复位更安全,但需要额外的逻辑。
- 时钟域要统一:一个模块尽量只用一个时钟。跨时钟域处理是另一个话题,但能避免就避免。
来看一个带同步复位的写法:
always @(posedge clk) begin
if (!rst_n)
q <= 1'b0;
else
q <= d;
end
这个写法,复位信号不在敏感列表里。它只在时钟沿判断复位条件。综合出来是带同步复位的D触发器。
3.4 D触发器的Verilog描述——从理论到代码
D触发器是时序逻辑的基本单元。一个D触发器,就是一个1位寄存器。它的Verilog描述,其实就是我们刚才写的那个always块。
咱们来拆解一下:
| 信号 | 含义 | 说明 |
|---|---|---|
| clk | 时钟 | 上升沿或下降沿触发 |
| rst_n | 复位 | 低电平有效,异步清零 |
| d | 数据输入 | 时钟沿到来时采样 |
| q | 数据输出 | 保持到下一个时钟沿 |
一个完整的D触发器描述:
module dff (
input wire clk,
input wire rst_n,
input wire d,
output reg q
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0;
else
q <= d;
end
endmodule
你看,就这么几行代码。但背后对应的是实实在在的硬件电路。每个reg变量,在综合后都会变成一个或多个D触发器。
嗯,这里要注意:D触发器的输出变化,只发生在时钟沿。其他时间,输出保持不变。这就是“记忆”的本质。
3.5 知识体系梳理
为了让你更直观地理解本章的知识结构,我画了一张图:
这张图把本章的核心知识点串起来了。你看,四个模块相互关联,缺一不可。always块是载体,reg类型是变量,时钟复位是控制,D触发器是最终实现。
好了,这一章的内容就这些。记住:时序逻辑的核心就是“在时钟沿干活,用复位定初态”。把这个原则刻在脑子里,写出来的代码就不会跑偏。
公众号:蓝海资料掘金营,微信deep3321