状态机架构
状态机的适用场景
计数器结构适用与,状态只会按顺序执行的情况,如1->2->3->4这种情况。当出现1->3->1->4这状态跳转顺序不固定,要处理的输出信号复杂时的情况,就需要使用状态机了。
状态机规则
- 使用四段式写法,明德扬其所谓四段式,只是在moore摩尔型(输出与输入无关的类型)三段式的基础上加上了,一段规定状态转移信号的模板。
具体模板如下,
verilog
//以下是FSM的状态机模板
module FSM (
input clk,
input rst_n,
input [3:0] data_in,
output reg [3:0] data_out,
output reg [3:0] data_out_2
);
//------------<状态机参数定义>------------------------------------------
//这里使用独热码编码节省组合逻辑资源
//此外还可以使用格雷码 、二进制码
localparam IDLE = 4'b0001,
s1 = 4'b0010,
s2 = 4'b0100,
DONE = 4'b1000;
reg [3:0] cur_state; // 当前状态
reg [3:0] next_state; // 下一状态
// 第一段,状态的转移,时序逻辑
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
cur_state <= IDLE;
else
cur_state <= next_state;
end
// 第二段,新状态的生成,组合逻辑
always @(*) begin
case (cur_state)
IDLE: begin
if(IDLE2s1_start)
next_state = s1;
else
next_state = cur_state;
end
s1: begin
if (s12s2_start)
next_state = s2;
else
next_state = cur_state;
end
s2: begin
if (s22DONE_start)
next_state = DONE;
else
next_state = cur_state;
end
DONE:begin
if(DONE2IDLE_start)
next_state = IDLE;
else
next_state = cur_state;
end
default:
next_state = IDLE;
endcase
end
//第三段,状态机转移信号的赋值,组合逻辑
//模板:
//assign 状态机转移信号 = (cur_state == 当前状态) && (data_in == 当前状态的输入条件);
assign IDLE2s1_start = (cur_state == IDLE) && (data_in == 4'b0001);
assign s12s2_start = (cur_state == s1) && (data_in == 4'b0010);
assign s22DONE_start = (cur_state == s2) && (data_in == 4'b0100);
assign DONE2IDLE_start = (cur_state == DONE) && (data_in == 4'b1000);
// 第四段,各状态的输出逻辑,组合逻辑
// 对初学者,最好将所有输出分成单独的always块写,这样可以更清楚的看到各个状态的输出
always @(posedge clk or negedge rst_n) begin
case (cur_state)
IDLE:
data_out = 0;
s1:
data_out = data_in;
s2:
data_out = data_in;
DONE:
data_out = 0;
endcase
end
always @(posedge clk or negedge rst_n) begin
case (cur_state)
IDLE:
data_out_2 = 0;
s1:
data_out_2 = data_in;
s2:
data_out_2 = data_in;
DONE:
data_out_2 = 0;
endcase
end
//对于复杂工程,熟练工程师也可以将所有输出信号写到一个always块中,这样可以更清晰的看到各个状态的输出
//模板:
//always @(*) begin
always @(posedge clk or negedge rst_n) begin
case (cur_state)
IDLE: begin
data_out = 0;
data_out_2 = data_in;
end
s1: begin
data_out = data_in;
data_out_2 = data_in;
end
s2: begin
data_out = data_in;
data_out_2 = data_in;
end
DONE: begin
data_out = 0;
data_out_2 = data_in;
end
endcase
end
endmodule
- 状态转移信号的命名规则:
verilog
(上一个状态名)2(下一个状态名)_start
- 用assign赋值状态转移条件时,必须与上当前状态。(跟计数器赋值结束条件时,必须与上+1条件类似)
- 状态不变时,要用
next_state = cur_state。不要用具体的状态名如next_state=IDLE这种
状态机八步法
-
明确功能
- 分析并明确功能
- 每个状态机都应该写成一个单独的模块,便于复用和维护
- 画出该模块框图,明确有哪些输入输出信号
- 写出该模块的输入输出信号列表,其包括方向,位宽,功能。
-
输出分析
- 明德扬认为应该对状态机的各输出信号,进行逐个的状态分析。即以输出信号为分析基础,这种做法跟普遍的以状态作为分析基础不同。
- 将不同信号,在不同状态下的输出画图列出

-
状态合并
- 原来的单个输出信号的状态分析,现在将所有的输出信号合并分析,状态能合在一起的就合在一起。

- 原来的单个输出信号的状态分析,现在将所有的输出信号合并分析,状态能合在一起的就合在一起。
-
状态转移
- 写出状态合并后,各状态的转移信号

- 写出状态合并后,各状态的转移信号
-
转移条件
- 将状态转移的条件用明确的信号表示

- 将状态转移的条件用明确的信号表示
其表示也可以用更高的抽象级表示如下图,可以方便后续代码的编写。

- 完整性检查
- 将所有的信号进行分析考虑(前面几步,主要分析了状态机信号,除状态机的信号的没有仔细考虑)
- 状态机代码
- 套前面的状态机四段式
- 功能代码
- 将其余的代码补全,主要是状态机转移信号那个部分