Skip to content

Verilog / SystemVerilog 状态机

Verilog SystemVerilog

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).

防御:

  1. 顶部 next_light = current_light; 默认 stay
  2. 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 到 current
  • light = current_light 直接连线, 同步

5. 易错点清单

错修
parameter 之间用 ; 分隔用 ,, 最后一个无分隔
端口列表 ) 后忘 ;必加
typedef enum 类型名 {...}类型名在最后: typedef enum logic [1:0] {...} 类型名;
enum 成员用 ; 分用 ,
case (x) begin ... endcase 不用 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 后缀低有效
  • 缩进对齐, 注释只标”为何”, 不标”是什么”

参考

Discussion

Loading comments…

Checking sign-in…