Verilog中的三段式状态机(也叫做三段式时序设计,或三阶段状态机)是一种常见的有限状态机(FSM)设计方法,广泛用于数字电路中,特别是在需要处理多个状态和转移的场景下。
机分为三个主要阶段:
- 状态寄存器阶段(State Register):这个状阶段没有什么含金量,简单说就是只干了一件事,让当前状态变成下一个状态。
- 状态转移逻辑阶段(State Transition Logic):这一阶段其实就是再说我们的下一状态,是切换到哪个状态。代码内容就是如果这个条件来了,就切换到这个状态,如果哪个条件来了,就切换到那个状态。
- 输出逻辑阶段(Output Logic):这一阶段是状态机的核心,内容是说明状态机的输出,讲明白每个状态是干嘛的。
接下来,我们逐一解析这三个阶段。
1. 状态寄存器阶段(State Register)
在这一阶段,状态机的当前状态由一个寄存器(通常是一个寄存器数组)来保存。状态机的状态会根据时钟信号更新。状态寄存器阶段的作用就是存储当前的状态,使得状态机可以在每个时钟周期内保持自己的状态。
状态寄存器通常是在一个时钟上升沿(或下降沿)触发的,通常使用always
块来描述。
例如:
reg [1:0] state, next_state; // 定义当前状态和下一个状态
always @(posedge clk or posedge reset) begin
if (reset)
state <= S0; // 复位时,状态机回到初始状态 S0
else
state <= next_state; // 否则,状态机更新为下一个状态
end
在上面的代码中,state
寄存器存储当前的状态。它会在时钟上升沿时根据next_state
来更新。如果有复位信号,则会将状态设置为初始状态(S0
)。
2. 状态转移逻辑阶段(State Transition Logic)
这个阶段的功能是根据当前状态和输入信号来计算下一个状态。这里,我们可以通过一个组合逻辑块(通常使用case
语句)来实现状态转移逻辑。
例如,假设有三个状态:S0
、S1
和S2
,并且状态之间的转移依赖于某些输入信号(比如input_signal
):
always @(*) begin
case(state)
S0: begin
if (input_signal)
next_state = S1;
else
next_state = S0;
end
S1: begin
if (input_signal)
next_state = S2;
else
next_state = S0;
end
S2: begin
next_state = S0; // 回到 S0
end
default: next_state = S0; // 默认回到 S0
endcase
end
在这个逻辑块中,根据state
(当前状态)和input_signal
(输入信号),决定了下一个状态 next_state
。
3. 输出逻辑阶段(Output Logic)
最后,在输出逻辑阶段,状态机的输出信号会根据当前状态或者某些条件来生成。在一些设计中,输出可能仅仅依赖于当前状态(称为“Mealy”状态机),或者可能依赖于当前状态和输入信号的组合(称为“Moore”状态机)。
例如,如果输出只依赖于当前状态,我们可以这样写:
always @(state) begin
case(state)
S0: output_signal = 1'b0;
S1: output_signal = 1'b1;
S2: output_signal = 1'b0;
default: output_signal = 1'b0;
endcase
end
如果输出依赖于输入信号和当前状态,那么可能需要修改上述代码,将输入信号也作为条件之一。
完整的三段式状态机示例
假设我们要设计一个简单的状态机,具有三个状态:S0
、S1
、S2
,并且状态机的状态转移依赖于输入信号input_signal
,输出信号output_signal
根据当前状态生成。
module fsm (
input clk, // 时钟信号
input reset, // 复位信号
input input_signal, // 输入信号
output reg output_signal // 输出信号
);
// 定义状态
typedef enum logic [1:0] {
S0 = 2'b00, // 初始状态
S1 = 2'b01, // 状态 S1
S2 = 2'b10 // 状态 S2
} state_t;
state_t state, next_state; // 当前状态和下一个状态
// 状态寄存器阶段
always @(posedge clk or posedge reset) begin
if (reset)
state <= S0; // 复位时,回到初始状态
else
state <= next_state; // 否则,更新为下一个状态
end
// 状态转移逻辑阶段
always @(*) begin
case(state)
S0: next_state = (input_signal) ? S1 : S0;
S1: next_state = (input_signal) ? S2 : S0;
S2: next_state = S0;
default: next_state = S0; // 默认回到 S0
endcase
end
// 输出逻辑阶段
always @(state) begin
case(state)
S0: output_signal = 1'b0;
S1: output_signal = 1'b1;
S2: output_signal = 1'b0;
default: output_signal = 1'b0;
endcase
end
endmodule
关键点总结:
- 状态寄存器阶段:保存当前的状态。
- 状态转移逻辑阶段:根据当前状态和输入信号,计算下一个状态。
- 输出逻辑阶段:根据当前状态,生成输出信号。
通过这种三段式设计方法,状态机的设计可以更清晰地分开各个功能区域,使得代码更加模块化,易于理解和维护。