FPGA 学习系列(4):Verilog 时序逻辑设计
在 FPGA 设计中,时序逻辑(Sequential Logic) 是构建存储和控制电路的关键部分,与组合逻辑不同,时序逻辑依赖时钟信号(Clock)和寄存器(Register)来存储和处理数据。本篇博客将介绍 Verilog 时序逻辑设计的基本方法,包括 always @(posedge clk)
语法、寄存器、触发器以及有限状态机(FSM)设计。
1. 什么是时序逻辑?
时序逻辑依赖 时钟信号 进行数据存储和更新。典型的时序逻辑电路包括:
- 触发器(D 触发器、JK 触发器等)
- 移位寄存器(Shift Register)
- 计数器(Counter)
- 有限状态机(Finite State Machine, FSM)
下图展示了一个典型的时序逻辑结构:
+------------+ 时钟输入 clk
| 寄存器单元 | 输入信号 D
| (触发器) | ---> 输出信号 Q
+------------+
时序逻辑的核心特点:
- 存储数据:状态由寄存器存储,直到下一个时钟沿更新。
- 同步更新:所有时序逻辑电路在时钟信号的上升沿或下降沿触发。
- 受时序约束:数据更新必须满足建立时间(Setup Time)和保持时间(Hold Time)。
2. Verilog 时序逻辑设计方法
2.1 always @(posedge clk)
Verilog 时序逻辑通常使用 always @(posedge clk)
代码块:
always @(posedge clk) begin
q <= d; // d 触发器:时钟上升沿采样 d,并存入 q
end
posedge clk
表示 时钟上升沿 触发更新。- 使用 非阻塞赋值 (
<=
),以确保寄存器同步更新。
3. 时序逻辑实例
3.1 D 触发器(基本存储单元)
module d_flip_flop (
input wire clk, d,
output reg q
);
always @(posedge clk) begin
q <= d; // 时钟上升沿更新数据
end
endmodule
d
为输入信号,在clk
上升沿时存入q
。
3.2 4 位移位寄存器(Shift Register)
module shift_register (
input wire clk,
input wire d,
output reg [3:0] q
);
always @(posedge clk) begin
q <= {q[2:0], d}; // 向左移位,输入 d 进入最低位
end
endmodule
q
的内容在每个clk
上升沿 左移一位,新数据d
进入最低位。
3.3 计数器(Counter)
module counter (
input wire clk, rst,
output reg [3:0] count
);
always @(posedge clk or posedge rst) begin
if (rst)
count <= 4'b0000; // 复位时清零
else
count <= count + 1; // 计数递增
end
endmodule
rst
为 异步复位 信号,高电平时立即清零count
。count
在clk
上升沿 递增。
3.4 2 位有限状态机(FSM)
FSM 是控制逻辑的核心,如通信协议、指令解析、流水线控制等。
(1) FSM 状态定义
typedef enum logic [1:0] {
S_IDLE = 2'b00, // 空闲状态
S_LOAD = 2'b01, // 载入数据
S_EXEC = 2'b10 // 执行操作
} state_t;
S_IDLE
:初始状态,等待触发。S_LOAD
:数据载入状态。S_EXEC
:执行操作。
(2) FSM 状态机设计
module fsm_example (
input wire clk, rst, start,
output reg [1:0] state
);
always @(posedge clk or posedge rst) begin
if (rst)
state <= S_IDLE; // 复位时回到初始状态
else begin
case (state)
S_IDLE: if (start) state <= S_LOAD;
S_LOAD: state <= S_EXEC;
S_EXEC: state <= S_IDLE;
default: state <= S_IDLE;
endcase
end
end
endmodule
rst
复位状态机至S_IDLE
。start
触发状态转换,按照IDLE → LOAD → EXEC
的顺序执行。
4. 时序逻辑优化技巧
-
避免组合逻辑反馈
- 确保
always @(posedge clk)
代码块只用于时序逻辑,避免误用always @(*)
造成不稳定反馈。
- 确保
-
使用非阻塞赋值 (
<=
)- 时序逻辑必须用
<=
,保证寄存器同步更新:always @(posedge clk) begin q <= d; // 正确 end
- 若使用
=
,可能导致竞争问题:always @(posedge clk) begin q = d; // 可能会产生竞态 end
- 时序逻辑必须用
-
优化 FSM 状态数量
- 状态机设计应尽量减少状态数量,避免冗余状态影响性能。
-
使用同步复位
- 同步复位 (always @(posedge clk)) 更易满足 FPGA 时序约束:
always @(posedge clk) begin if (rst) q <= 0; else q <= d; end
- 同步复位 (always @(posedge clk)) 更易满足 FPGA 时序约束:
5. 结语
本篇博客介绍了 Verilog 时序逻辑设计 的基本方法,包括 always @(posedge clk)
语法、D 触发器、移位寄存器、计数器和 FSM 设计。在 FPGA 设计中,时序逻辑用于数据存储、流水线控制和信号同步,是所有复杂电路的基础。
下一期:《FPGA 学习系列(5):Verilog 设计仿真与调试》