Verilog / SystemVerilog 状态机 (FSM)
核心一句话:current 是寄存器存的”现在”, next 是组合算出的”下一拍”,边沿那一瞬间, next 进 current. 两者永远差 1 拍.
1. 三段式 FSM 是什么
FSM = Finite State Machine. 把电路行为拆成 状态集 + 转移规则 + 输出函数.
三段式 = 3 个 always 块各管 1 件事:
| 段 | 块类型 | 赋值 | 职责 |
|---|---|---|---|
| 时序 | always_ff | <= 非阻塞 | 寄存器更新 (state, cnt) |
| 组合 | always_comb | = 阻塞 | 算下一状态 next_state |
| 输出 | always_comb / assign | = | 当前状态 → 输出译码 |
好处: 分工清晰, 综合工具友好, 易调试, 防 latch.
Moore vs Mealy
- Moore: 输出只看
current_state(本笔记交通灯是 Moore) - Mealy: 输出 = f(current_state, input), 反应更快但易 glitch
2. 交通灯实例 (作业)
需求:
- 输入:
clk,rst_n - 输出:
light(0 绿 / 1 黄 / 2 红) parameter控制 3 色持续时长- FSM 控制颜色循环 GREEN → YELLOW → RED → GREEN
状态图
cnt == green_dur - 1
GREEN ─────────────────────► YELLOW
▲ │
│ cnt == red_dur - 1 │ cnt == yellow_dur - 1
│ ▼
└────────────────────────── RED
完整代码
参见 fsm_trafficlight.sv.
module traffic_light #(//状态机
parameter green_duration = 1,
parameter yellow_duration = 2,
parameter red_duration = 3
)(
input logic clk,
input logic rst_n,
output logic [1:0] light
);
localparam MAX_DUR = (green_duration > yellow_duration ?
green_duration : yellow_duration) > red_duration ?
(green_duration > yellow_duration ? green_duration : yellow_duration)
: red_duration;
localparam CNT_WIDTH = (MAX_DUR<=1)? 1: $clog2(MAX_DUR);
typedef enum logic [1:0]{
IDLE = 2'b00,
GREEN = 2'b01,
YELLOW = 2'b10,
RED = 2'b11
} light_type;
light_type current_light;
light_type next_light;
logic [CNT_WIDTH-1:0] cnt;
always_ff @( posedge clk or negedge rst_n ) begin
if(!rst_n) begin
current_light <= IDLE;
cnt <= 'b0;
end
else begin
current_light <= next_light;
// cnt <= cnt + 1;
case(current_light)
GREEN:
if(cnt == green_duration - 1) cnt <= 0;
else cnt <= cnt + 1;
YELLOW:
if(cnt == yellow_duration - 1) cnt <= 0;
else cnt <= cnt + 1;
RED:
if(cnt == red_duration - 1) cnt <= 0;
else cnt <= cnt + 1;
default: cnt <= 0;
endcase
end
end
//next 是"为下一拍准备"的值, 永远 next 领先 current 1 拍
always_comb begin
next_light = current_light; //防 latch:if 条件不成立, else 不存在,则保持当前状态
case (current_light)
IDLE: next_light = GREEN;
GREEN: if (cnt == green_duration - 1) next_light = YELLOW;
YELLOW: if (cnt == yellow_duration - 1) next_light = RED;
RED: if (cnt == red_duration - 1) next_light = GREEN;
default: next_light = GREEN;
endcase
end
assign light = current_light;
endmodule
3. 关键设计点
3.1 parameter vs localparam vs typedef enum
| 关键字 | 用途 | 外部可改? |
|---|---|---|
parameter | 模块对外配置 (写在 #() 内) | ✓ |
localparam | 模块内派生常量 (如 CNT_WIDTH = $clog2(...)) | ✗ |
typedef enum | 状态机状态类型 | — |
3.2 cnt 作 timer
FSM 自身无时间概念. cnt = “当前状态停了几拍” 计数器.
- 每拍 +1
- 到
当前状态 duration - 1→ 清 0 + 触发状态切换 - duration 跟
current_light走 → 用case区分
3.3 cnt 位宽计算
localparam CNT_WIDTH = $clog2(MAX_DUR);
logic [CNT_WIDTH-1:0] cnt;
两件事别混:
$clog2(N)算 “装 N 个不同值需几 bit”. cnt 数0~MAX_DUR-1共MAX_DUR个值 →$clog2(MAX_DUR)bit[N-1:0]是 SV 声明 N bit 信号的语法 (位索引 0-based)
边界
MAX_DUR = 1→$clog2(1) = 0→ cnt 位宽 -1 异常- parameter 由调用者保证 ≥ 2
3.4 默认 stay + default 防 latch
always_comb 里若某条路径没给 next_light 赋值, 综合工具会推出 latch (锁存器, 严重 bug).
防御:
- 顶部
next_light = current_light;默认 stay case加default分支
3.5 非阻塞与阻塞 时序语义
| 块 | 赋值符 | 生效时机 |
|---|---|---|
always_ff | <= 非阻塞 | 下一个 clk 边沿 commit |
always_comb | = 阻塞 | 信号变化立即重算 |
关键洞察: 一个 clock cycle 内, 寄存器值固定不变. cnt <= cnt + 1 不是”本拍立刻加”, 是”下一拍 cnt 才变成新值”.
4. 时序波形 (WaveDrom)
参数 g=1, y=2, r=3 的 1 个完整循环:
读图:
- T0 reset, 所有寄存器 X
- T1 rst_n 拉高, current 进 GREEN
- GREEN 占 1 拍, YELLOW 占 2 拍, RED 占 3 拍 ✓
next_light比current_light提前 1 拍 — 组合算出来后, 下个 clk 边沿才 commit 到 currentlight=current_light直接连线, 同步
5. 易错点清单
| 错 | 修 |
|---|---|
parameter 之间用 ; 分隔 | 用 ,, 最后一个无分隔 |
端口列表 ) 后忘 ; | 必加 |
typedef enum 类型名 {...} | 类型名在最后: typedef enum logic [1:0] {...} 类型名; |
enum 成员用 ; 分 | 用 , |
case (x) begin ... end | case 不用 begin/end, 用 case ... endcase |
always_ff 内用 = / ++ | 一律 <= 非阻塞 |
always_comb 内用 <= | 一律 = 阻塞 |
| 组合块某路径未赋值 | 顶部默认值 + default 兜底, 防 latch |
| 编码值乱给 (e.g. GREEN=01) | 对齐输出规格 (本例 0/1/2) 省一段译码 |
cnt 位宽 [N:0] (多 1 bit) | [N-1:0] |
6. PPT 代码风格 checklist
- ANSI 端口声明 (
input logic clk, ...) -
rst_n低有效, 异步复位 -
always_ff(时序) +always_comb(组合) 分开, 不用always @(*) - 非阻塞
<=用于时序, 阻塞=用于组合 -
typedef enum定义状态类型 -
parameter外部配置,localparam内部派生 - case 都有
default - 命名:
current_*/next_*/*_n后缀低有效 - 缩进对齐, 注释只标”为何”, 不标”是什么”
参考
- C. E. Cummings, “Synthesizable Finite State Machine Design Techniques Using the New SystemVerilog 3.0 Enhancements,” SNUG San Jose, 2003,见Paradigm Works技术文库
- Wikipedia, Finite-state machine
Discussion
Loading comments…
Checking sign-in…