Verilog 三段式状态机的技巧

在数字电路设计中,状态机是一种非常重要的工具。它可以用于描述有限状态系统的行为,在各种数字系统中(如通信协议处理、控制逻辑等)都有广泛应用。Verilog 中的三段式状态机是一种结构化良好、易于理解和维护的设计方式。本文将详细介绍 Verilog 三段式状态机的技巧,包括其结构、常见实践、最佳实践以及示例用法等。

目录#

  1. 三段式状态机的结构
    • 状态定义段
    • 状态转移段
    • 输出段
  2. 常见实践
    • 状态编码
    • 复位处理
  3. 最佳实践
    • 状态机的命名规范
    • 避免组合逻辑冒险
    • 状态机的层次化设计
  4. 示例用法
    • 简单的状态机示例
  5. 参考

1. 三段式状态机的结构#

1.1 状态定义段#

在 Verilog 中,首先需要定义状态机的各个状态。通常使用 parameter 来定义状态常量。例如:

module state_machine(
    input clk,
    input rst_n,
    input in_signal,
    output reg out_signal
);
 
parameter IDLE = 2'b00;
parameter STATE1 = 2'b01;
parameter STATE2 = 2'b10;
 
reg [1:0] current_state, next_state;

这里定义了三个状态 IDLE(空闲状态)、STATE1STATE2,并且声明了两个寄存器 current_state(当前状态)和 next_state(下一个状态)来保存状态机的状态信息。

1.2 状态转移段#

这一段主要是根据当前状态和输入信号来确定下一个状态。一般使用同步时序逻辑来实现状态转移。例如:

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        current_state <= IDLE;
    end else begin
        current_state <= next_state;
    end
end
 
always @(*) begin
    case (current_state)
        IDLE: begin
            if (in_signal) begin
                next_state = STATE1;
            end else begin
                next_state = IDLE;
            end
        end
        STATE1: begin
            next_state = STATE2;
        end
        STATE2: begin
            if (!in_signal) begin
                next_state = IDLE;
            end else begin
                next_state = STATE2;
            end
        end
        default: next_state = IDLE;
    endcase
end

第一个 always 块是同步复位的状态更新,当复位信号有效(低电平)时,状态机回到 IDLE 状态;否则,在下一个时钟上升沿更新当前状态为下一个状态。第二个 always 块是组合逻辑的状态转移判断,根据当前状态和输入信号确定下一个状态。

1.3 输出段#

输出段根据当前状态或者当前状态和输入信号来确定输出信号的值。同样可以使用同步时序逻辑或者组合逻辑来实现。例如:

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        out_signal <= 1'b0;
    end else begin
        case (current_state)
            IDLE: out_signal <= 1'b0;
            STATE1: out_signal <= 1'b1;
            STATE2: out_signal <= 1'b0;
            default: out_signal <= 1'b0;
        endcase
    end
end

这里使用同步时序逻辑根据当前状态来确定输出信号 out_signal 的值。

2. 常见实践#

2.1 状态编码#

  • 二进制编码:如上面示例中的 2'b00 等形式,简单直观,占用资源较少,但在状态较多时,状态转移逻辑可能会比较复杂。
  • 独热编码:每个状态用一个单独的位来表示,例如 parameter IDLE = 3'b001STATE1 = 3'b010STATE2 = 3'b100(假设有三个状态)。这种编码方式状态转移逻辑简单,但会占用更多的寄存器资源。
  • 格雷码编码:相邻状态之间只有一位不同,在状态切换时可以减少毛刺的产生,提高电路的稳定性。

2.2 复位处理#

  • 同步复位:如上面示例中的方式,复位信号在时钟边沿触发时起作用。优点是可以滤除复位信号上的毛刺,提高电路的可靠性;缺点是复位信号的有效时间必须满足时钟周期要求。
  • 异步复位:always @(posedge clk or negedge rst_n) 中的复位信号判断可以改为 if (!rst_n) 直接进行复位操作(不通过时钟边沿)。优点是复位响应快;缺点是可能会引入亚稳态问题,需要在复位释放时进行同步处理。

3. 最佳实践#

3.1 状态机的命名规范#

  • 给状态机模块、状态参数、状态寄存器等都取有意义的名字,例如 uart_rx_state_machine(串口接收状态机),STATE_RX_IDLE(接收空闲状态)等,方便代码的阅读和维护。

3.2 避免组合逻辑冒险#

在状态转移和输出逻辑中,尽量使用完整的 case 语句或者 if - else if - else 结构,并且添加 default 分支来处理未定义的情况,避免产生锁存器或者逻辑冒险。

3.3 状态机的层次化设计#

当状态机比较复杂时,可以将其拆分为多个子状态机或者将状态转移逻辑、输出逻辑等分别封装成模块,提高代码的可读性和可维护性。

4. 示例用法#

4.1 简单的状态机示例#

假设我们设计一个简单的状态机来控制一个 LED 的亮灭。输入信号 button 表示按钮按下(高电平有效),输出信号 led 控制 LED(高电平亮)。状态机有两个状态:LED_OFF(LED 灭)和 LED_ON(LED 亮)。

module led_state_machine(
    input clk,
    input rst_n,
    input button,
    output reg led
);
 
parameter LED_OFF = 1'b0;
parameter LED_ON = 1'b1;
 
reg current_state, next_state;
 
// 状态转移(同步复位)
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        current_state <= LED_OFF;
    end else begin
        current_state <= next_state;
    end
end
 
// 状态转移逻辑(组合逻辑)
always @(*) begin
    case (current_state)
        LED_OFF: begin
            if (button) begin
                next_state = LED_ON;
            end else begin
                next_state = LED_OFF;
            end
        end
        LED_ON: begin
            if (button) begin
                next_state = LED_OFF;
            end else begin
                next_state = LED_ON;
            end
        end
        default: next_state = LED_OFF;
    endcase
end
 
// 输出逻辑(同步时序)
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        led <= 1'b0;
    end else begin
        case (current_state)
            LED_OFF: led <= 1'b0;
            LED_ON: led <= 1'b1;
            default: led <= 1'b0;
        endcase
    end
end
 
endmodule

在这个示例中,当按钮按下时,状态机在 LED_OFFLED_ON 之间切换,从而控制 LED 的亮灭。

5. 参考#

  • 《Verilog HDL 高级数字设计》(第二版),作者:Michael D. Ciletti
  • 《数字设计和计算机体系结构》,作者:David Money Harris 等
  • Verilog 官方文档

希望通过本文的介绍,读者能够更好地掌握 Verilog 三段式状态机的设计技巧,在数字电路设计中灵活运用状态机来实现各种复杂的逻辑功能。