23verilog状态机
Verilog状态机详解
📑 目录
1. 有限状态机简介
有限状态机(FSM, Finite State Machine)是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。FSM是数字电路设计中的核心概念,广泛应用于控制逻辑、协议实现、序列检测等场景。
状态机的基本要素:
- 🔢 状态集合:有限个离散状态
- ➡️ 状态转移:状态之间的转换条件
- 📥 输入集合:影响状态转移的信号
- 📤 输出集合:状态机的输出信号
- 🏁 初始状态:状态机的起始状态
2. 状态机类型
2.1 Moore型状态机
- 输出特性:输出只与当前状态有关,与输入无关
- 优点:输出稳定,无毛刺,易于调试
- 缺点:响应延迟大,需要额外时钟周期
// Moore型状态机示例
module moore_fsm(
input wire clk, rst_n, start,
output reg [1:0] state_out,
output reg done
);
// 状态定义
localparam IDLE = 2'b00,
WORK1 = 2'b01,
WORK2 = 2'b10,
DONE = 2'b11;
reg [1:0] current_state, next_state;
// 第一段:时序逻辑,状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
current_state <= IDLE;
else
current_state <= next_state;
end
// 第二段:组合逻辑,次态逻辑
always @(*) begin
case (current_state)
IDLE: next_state = start ? WORK1 : IDLE;
WORK1: next_state = WORK2;
WORK2: next_state = DONE;
DONE: next_state = IDLE;
default: next_state = IDLE;
endcase
end
// 第三段:组合逻辑,输出逻辑(仅依赖当前状态)
always @(*) begin
case (current_state)
IDLE: begin
state_out = 2'b00;
done = 1'b0;
end
WORK1: begin
state_out = 2'b01;
done = 1'b0;
end
WORK2: begin
state_out = 2'b10;
done = 1'b0;
end
DONE: begin
state_out = 2'b11;
done = 1'b1;
end
default: begin
state_out = 2'b00;
done = 1'b0;
end
endcase
end
endmodule
2.2 Mealy型状态机
- 输出特性:输出与当前状态和当前输入都有关
- 优点:响应速度快,逻辑简洁
- 缺点:可能产生毛刺,调试相对困难
// Mealy型状态机示例
module mealy_fsm(
input wire clk, rst_n, start, enable,
output reg [1:0] state_out,
output reg done
);
// 状态定义
localparam IDLE = 2'b00,
WORK = 2'b01,
WAIT = 2'b10;
reg [1:0] current_state, next_state;
// 第一段:时序逻辑
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
current_state <= IDLE;
else
current_state <= next_state;
end
// 第二段:组合逻辑,次态逻辑
always @(*) begin
case (current_state)
IDLE: next_state = start ? WORK : IDLE;
WORK: next_state = enable ? WAIT : WORK;
WAIT: next_state = IDLE;
default: next_state = IDLE;
endcase
end
// 第三段:组合逻辑,输出逻辑(依赖状态和输入)
always @(*) begin
case (current_state)
IDLE: begin
state_out = 2'b00;
done = start ? 1'b0 : 1'b1; // 输出依赖输入
end
WORK: begin
state_out = 2'b01;
done = enable ? 1'b1 : 1'b0; // 输出依赖输入
end
WAIT: begin
state_out = 2'b10;
done = 1'b1;
end
default: begin
state_out = 2'b00;
done = 1'b0;
end
endcase
end
endmodule
2.3 Moore vs Mealy对比
| 特性 | Moore型 | Mealy型 |
|---|---|---|
| 输出依赖 | 仅当前状态 | 当前状态+输入 |
| 响应速度 | 慢(需额外时钟) | 快(同时钟周期) |
| 输出稳定性 | 稳定,无毛刺 | 可能有毛刺 |
| 状态数量 | 可能较多 | 通常较少 |
| 调试难度 | 容易 | 相对困难 |
| 应用场景 | 控制器、计数器 | 译码器、检测器 |
3. 状态机设计风格
3.1 设计风格分类
根据代码组织方式,状态机可分为:
- 三段式:状态寄存器 + 次态逻辑 + 输出逻辑(推荐)
- 二段式:状态寄存器 + (次态逻辑+输出逻辑)
- 一段式:所有逻辑混合在一起(不推荐)
3.2 选择指导原则
graph TD
A[状态机设计] --> B{复杂度}
B -->|简单| C[二段式]
B -->|复杂| D[三段式]
D --> E{输出类型}
E -->|Moore型| F[三段式Moore]
E -->|Mealy型| G[三段式Mealy]
C --> H[适合小型FSM]
F --> I[稳定输出,易调试]
G --> J[快速响应,逻辑紧凑]
4. 三段式状态机
4.1 三段式结构
三段式状态机将状态机逻辑分为三个独立的always块:
- 第一段:时序逻辑,使用非阻塞赋值,实现状态寄存器
- 第二段:组合逻辑,使用阻塞赋值,实现次态逻辑
- 第三段:输出逻辑,根据类型选择组合或时序逻辑
4.2 三段式模板
module three_stage_fsm(
input wire clk, rst_n,
input wire [输入位宽-1:0] inputs,
output reg [输出位宽-1:0] outputs
);
// 状态参数定义
localparam STATE1 = 编码1,
STATE2 = 编码2,
// ... 更多状态
STATEn = 编码n;
// 状态变量声明
reg [状态位宽-1:0] current_state, next_state;
// 第一段:时序逻辑 - 状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
current_state <= STATE1; // 初始状态
else
current_state <= next_state;
end
// 第二段:组合逻辑 - 次态逻辑
always @(*) begin
next_state = current_state; // 默认保持当前状态
case (current_state)
STATE1: if (条件1) next_state = STATE2;
STATE2: if (条件2) next_state = STATE3;
// ... 更多状态转移
default: next_state = STATE1;
endcase
end
// 第三段:输出逻辑
// Moore型:组合逻辑
always @(*) begin
outputs = 默认值;
case (current_state)
STATE1: outputs = 输出值1;
STATE2: outputs = 输出值2;
// ... 更多输出
default: outputs = 默认值;
endcase
end
// Mealy型:组合逻辑(依赖输入)
/*
always @(*) begin
outputs = 默认值;
case (current_state)
STATE1: outputs = 条件 ? 输出值1a : 输出值1b;
STATE2: outputs = 条件 ? 输出值2a : 输出值2b;
default: outputs = 默认值;
endcase
end
*/
endmodule
4.3 三段式优势
- ✅ 结构清晰:逻辑分离,易于理解和维护
- ✅ 易于调试:可以单独验证每个部分
- ✅ 可综合性好:符合综合工具的优化要求
- ✅ 易于修改:修改状态转移或输出不影响其他部分
5. 二段式状态机
5.1 二段式结构
二段式状态机将次态逻辑和输出逻辑合并在一个always块中:
module two_stage_fsm(
input wire clk, rst_n, start, enable,
output reg done, error
);
// 状态定义
localparam IDLE = 2'b00,
WORK = 2'b01,
DONE = 2'b10,
ERROR = 2'b11;
reg [1:0] current_state;
// 第一段:时序逻辑 - 状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
current_state <= IDLE;
end else begin
// 第二段合并:次态逻辑 + 输出逻辑
case (current_state)
IDLE: begin
if (start) begin
current_state <= WORK;
done <= 1'b0;
error <= 1'b0;
end else begin
current_state <= IDLE;
done <= 1'b1;
error <= 1'b0;
end
end
WORK: begin
if (enable) begin
current_state <= DONE;
done <= 1'b0;
error <= 1'b0;
end else begin
current_state <= ERROR;
done <= 1'b0;
error <= 1'b1;
end
end
DONE: begin
current_state <= IDLE;
done <= 1'b1;
error <= 1'b0;
end
ERROR: begin
current_state <= IDLE;
done <= 1'b0;
error <= 1'b1;
end
default: begin
current_state <= IDLE;
done <= 1'b0;
error <= 1'b0;
end
endcase
end
end
endmodule
5.2 二段式特点
| 优点 | 缺点 |
|---|---|
| 代码简洁,逻辑紧凑 | 逻辑混合,调试困难 |
| 适合简单状态机 | 不易扩展和维护 |
| 减少always块数量 | 可能产生锁存器 |
6. 一段式状态机
6.1 一段式结构(不推荐)
一段式将所有逻辑混合在一个时序always块中:
// 一段式示例(不推荐)
module one_stage_fsm(
input wire clk, rst_n, start,
output reg done
);
localparam IDLE = 1'b0, WORK = 1'b1;
reg state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
done <= 1'b0;
end else begin
case (state)
IDLE: begin
if (start) begin
state <= WORK;
done <= 1'b0;
end
end
WORK: begin
state <= IDLE;
done <= 1'b1;
end
endcase
end
end
endmodule
6.2 为什么不推荐一段式
- ❌ 逻辑混乱:状态转移和输出逻辑混合
- ❌ 难以调试:无法单独验证各部分逻辑
- ❌ 维护困难:修改一处可能影响其他逻辑
- ❌ 综合问题:可能产生不必要的延迟
7. 状态编码方式
7.1 编码方式对比
| 编码方式 | 特点 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 二进制编码 | 最少位数 | 节省寄存器 | 译码复杂 | 状态数多 |
| 独热编码 | 每位代表一状态 | 译码简单,速度快 | 寄存器多 | 高速电路 |
| 格雷编码 | 相邻状态只差1位 | 降低功耗 | 译码复杂 | 低功耗设计 |
7.2 编码示例
// 二进制编码
localparam [2:0] IDLE = 3'b000, // 0
STATE1 = 3'b001, // 1
STATE2 = 3'b010, // 2
STATE3 = 3'b011, // 3
DONE = 3'b100; // 4
// 独热编码(One-Hot)
localparam [4:0] IDLE = 5'b00001, // 只有bit0为1
STATE1 = 5'b00010, // 只有bit1为1
STATE2 = 5'b00100, // 只有bit2为1
STATE3 = 5'b01000, // 只有bit3为1
DONE = 5'b10000; // 只有bit4为1
// 格雷编码
localparam [2:0] IDLE = 3'b000,
STATE1 = 3'b001,
STATE2 = 3'b011,
STATE3 = 3'b010,
DONE = 3'b110;
7.3 独热编码状态机
module onehot_fsm(
input wire clk, rst_n, start, done_in,
output reg [4:0] state_out,
output reg busy, ready
);
// 独热编码状态定义
localparam [4:0] IDLE = 5'b00001,
PREP = 5'b00010,
EXEC = 5'b00100,
WAIT = 5'b01000,
FINISH = 5'b10000;
reg [4:0] current_state, next_state;
// 状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
current_state <= IDLE;
else
current_state <= next_state;
end
// 次态逻辑(独热编码简化译码)
always @(*) begin
next_state = 5'b00000;
case (1'b1) // 独热编码case语句技巧
current_state[0]: next_state = start ? PREP : IDLE; // IDLE
current_state[1]: next_state = EXEC; // PREP
current_state[2]: next_state = WAIT; // EXEC
current_state[3]: next_state = done_in ? FINISH : WAIT; // WAIT
current_state[4]: next_state = IDLE; // FINISH
default: next_state = IDLE;
endcase
end
// 输出逻辑
always @(*) begin
busy = current_state[1] | current_state[2] | current_state[3]; // PREP|EXEC|WAIT
ready = current_state[0] | current_state[4]; // IDLE|FINISH
state_out = current_state;
end
endmodule
8. 状态机应用实例
8.1 序列检测器
// 检测"1011"序列的状态机
module sequence_detector(
input wire clk, rst_n, data_in,
output reg detected
);
// 状态定义
localparam [2:0] S0 = 3'b000, // 初始状态
S1 = 3'b001, // 检测到"1"
S2 = 3'b010, // 检测到"10"
S3 = 3'b011, // 检测到"101"
S4 = 3'b100; // 检测到"1011"
reg [2:0] current_state, next_state;
// 状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
current_state <= S0;
else
current_state <= next_state;
end
// 次态逻辑
always @(*) begin
case (current_state)
S0: next_state = data_in ? S1 : S0;
S1: next_state = data_in ? S1 : S2;
S2: next_state = data_in ? S3 : S0;
S3: next_state = data_in ? S4 : S2;
S4: next_state = data_in ? S1 : S0;
default: next_state = S0;
endcase
end
// 输出逻辑(Moore型)
always @(*) begin
detected = (current_state == S4);
end
endmodule
8.2 UART控制器
module uart_controller(
input wire clk, rst_n, start_tx, rx_ready,
input wire [7:0] tx_data,
output reg tx_busy, rx_valid,
output reg [7:0] rx_data
);
// 状态定义
localparam [2:0] IDLE = 3'b000,
TX_START = 3'b001,
TX_DATA = 3'b010,
TX_STOP = 3'b011,
RX_WAIT = 3'b100,
RX_DATA = 3'b101;
reg [2:0] current_state, next_state;
reg [3:0] bit_counter;
reg [7:0] shift_reg;
// 状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
current_state <= IDLE;
bit_counter <= 4'd0;
shift_reg <= 8'd0;
end else begin
current_state <= next_state;
// 计数器和移位寄存器控制
case (current_state)
TX_START: begin
bit_counter <= 4'd0;
shift_reg <= tx_data;
end
TX_DATA: begin
if (bit_counter < 8) begin
bit_counter <= bit_counter + 1;
shift_reg <= shift_reg >> 1;
end
end
RX_DATA: begin
if (rx_ready) begin
shift_reg <= {shift_reg[6:0], 1'b1}; // 示例
end
end
endcase
end
end
// 次态逻辑
always @(*) begin
case (current_state)
IDLE: begin
if (start_tx)
next_state = TX_START;
else if (rx_ready)
next_state = RX_WAIT;
else
next_state = IDLE;
end
TX_START: next_state = TX_DATA;
TX_DATA: next_state = (bit_counter == 8) ? TX_STOP : TX_DATA;
TX_STOP: next_state = IDLE;
RX_WAIT: next_state = rx_ready ? RX_DATA : RX_WAIT;
RX_DATA: next_state = IDLE;
default: next_state = IDLE;
endcase
end
// 输出逻辑
always @(*) begin
tx_busy = (current_state == TX_START) ||
(current_state == TX_DATA) ||
(current_state == TX_STOP);
rx_valid = (current_state == RX_DATA);
rx_data = shift_reg;
end
endmodule
9. 最佳实践与注意事项
9.1 设计原则
- ✅ 状态完备性:确保所有状态都有明确的转移条件
- ✅ 复位策略:所有状态机都应有复位到初始状态的机制
- ✅ 默认分支:在case语句中添加default分支
- ✅ 避免锁存器:确保所有输出在所有条件下都有赋值
9.2 常见错误
| 错误类型 | 说明 | 解决方法 |
|---|---|---|
| 状态卡死 | 某些状态无法转出 | 添加超时或错误处理 |
| 竞争冒险 | 输入信号变化导致误触发 | 同步输入信号 |
| 锁存器推断 | 组合逻辑不完整 | 确保所有路径都赋值 |
| 编码冲突 | 状态编码重复 | 使用参数定义避免冲突 |
9.3 调试技巧
// 状态机调试示例
module debug_fsm(
input wire clk, rst_n,
// ... 其他端口
);
// 状态定义
localparam [1:0] IDLE = 2'b00, WORK = 2'b01, DONE = 2'b10;
reg [1:0] current_state, next_state;
// 状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
current_state <= IDLE;
end else begin
current_state <= next_state;
// 调试信息
`ifdef DEBUG
case (next_state)
IDLE: $display("Time %0t: FSM -> IDLE", $time);
WORK: $display("Time %0t: FSM -> WORK", $time);
DONE: $display("Time %0t: FSM -> DONE", $time);
endcase
`endif
end
end
// 状态监控(仅仿真)
`ifdef SIMULATION
always @(current_state) begin
case (current_state)
IDLE: $display("Current state: IDLE");
WORK: $display("Current state: WORK");
DONE: $display("Current state: DONE");
default: $display("Current state: UNKNOWN");
endcase
end
`endif
// ... 其他逻辑
endmodule
9.4 性能优化
// 高效的状态机设计技巧
module optimized_fsm(
input wire clk, rst_n,
input wire [3:0] input_signals,
output reg [7:0] output_signals
);
// 使用独热编码提高速度
localparam [3:0] IDLE = 4'b0001,
STATE1 = 4'b0010,
STATE2 = 4'b0100,
STATE3 = 4'b1000;
reg [3:0] current_state, next_state;
// 状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
current_state <= IDLE;
else
current_state <= next_state;
end
// 优化的次态逻辑
always @(*) begin
next_state = 4'b0000;
// 使用独热编码的并行判断
if (current_state[0]) // IDLE
next_state = input_signals[0] ? STATE1 : IDLE;
else if (current_state[1]) // STATE1
next_state = input_signals[1] ? STATE2 : STATE3;
else if (current_state[2]) // STATE2
next_state = STATE3;
else if (current_state[3]) // STATE3
next_state = IDLE;
else
next_state = IDLE; // 错误恢复
end
// 优化的输出逻辑
always @(*) begin
output_signals = 8'b00000000;
// 直接使用状态位进行输出
output_signals[0] = current_state[0]; // IDLE指示
output_signals[1] = current_state[1]; // STATE1指示
output_signals[2] = current_state[2]; // STATE2指示
output_signals[3] = current_state[3]; // STATE3指示
// 组合输出
output_signals[4] = current_state[1] | current_state[2]; // 工作状态
output_signals[5] = current_state[3]; // 完成状态
end
endmodule
🎯 总结
核心要点
- 设计风格:推荐使用三段式,结构清晰,易维护
- 状态类型:Moore型稳定,Mealy型快速,根据需求选择
- 编码方式:二进制节省资源,独热编码速度快
- 最佳实践:完备性检查、复位策略、默认分支
- 调试技巧:状态监控、仿真验证、性能分析
设计指导
- 🎯 结构化设计:采用三段式,逻辑清晰
- 📊 编码选择:根据性能和资源要求选择编码方式
- 🔍 验证充分:覆盖所有状态转移和边界条件
- 🛡️ 错误处理:添加异常状态处理和恢复机制
💡 重要提醒:状态机是数字设计的核心技能,掌握三段式设计方法和合适的编码选择是关键。记住:结构清晰比代码简洁更重要!

浙公网安备 33010602011771号