状态机设计
状态机设计
状态机概念
-
状态机的本质
对具有逻辑顺序或时序规律事件的一种描述方法。所有具有逻辑顺序和时序规律的事情都适合用状态机描述
-
状态机的应用思路
- 从状态变量入手如果一个电路具有时序规律或者逻辑顺序,我们就可以自然而然地规划出状态,从这些状态入手,分析每个状态的输入,状态转移和输出,从而完成电路功能;
- 明确电路的输出的关系,这些输出相当于状态的输出,回溯规划每个状态和状态转移条件与状态输入,无论那种思路,使用状态机的目的都是要控制某部分电路,完成某种具有逻辑顺序或时序规律的电路设计。
-
状态机的基本描述方式
状态机的基本要素:状态、输出、输入
基本描述方式:状态转移图、状态转移表、HDL语言描述
什么是 RTL级好的FSM 描述?
- FSM要安全,稳定性高。(优先级最高,最需要保证)
- 不会进入死循环,不会进入非预知的状态
- 要求该FSM的综合实现结果,无毛刺等异常扰动
- 要求状态机要完备这可能就会使用”full case”的编码方式,使得所有的情况都有所处理,势必会花费更多的设计资源,有时会影响FSM的频率
- FSM速度快,满足设计的频率要求。
- FSM面积小,满足设计的面积要求。
- FSM设计要清晰易懂、易维护。
“一段式”写法
只利用一个always组合块,把需要写出的此刻的状态的不断变化,输出也糅合进去。

在描述当前状态时要考虑下个状态的输出,整个代码不清晰
- 不利于维护修改
- 不利于附加约束
- 不利于综合器和布局布线器对设计的优化
- 另外,这种描述相对于两段式描述比较冗长。对复杂的状态机来说,一段式要比二段式冗长大约80%到150%左右
module top_module(clk, reset, in, out);
input clk;
input reset; // 同步的复位,高电平有效
input in;
output out;
reg out;
//此时的状态
reg state;
parameter A = 0;
parameter B = 1;
always @(posedge clk) begin
if (reset) begin
state = B;
out <= B;
end else begin
case (state)
B:
if(in) begin
state <= B;
out <= 1'd1;
end
else begin
state <= A;
out <= 1'd0;
end
A:
if(in) begin
state <= A;
out <= 1'd0;
end
else begin
state = B;
out <= 1'd1;
end
default: begin
state = B;
out <= 1'd1;
end
endcase
end
end
endmodule
“二段式”写法
“二段式”描述方法

- 一个always组合块使用组合逻辑电路描述下一个状态的变化
- 一个always时序块使用时序逻辑电路描述此刻状态转移
- 使用assign或者always组合块使用组合逻辑电路描述输出状态的变化
“二段式”缺点以及解决办法
其输出一般使用组合逻辑描述,而组合逻辑易产生毛刺等不稳定因素。
解决办法
- 两段式 FSM 描述方法,如果时序允许插入,一个额外的时钟节拍,可以有效的解决毛刺现象,
- 但有的无法添加节拍,使用三段式的描述方法
代码
module top_module(
input clk,
input reset, // 同步复位,高电平有效
input in,
output out);//
parameter A=0, B=1;
reg state, next_state;
// 此时的状态的变化
always @(posedge clk) begin
if(reset) state <= B;
else state <= next_state;
end
// 使用组合逻辑电路来判断下一个状态的变化
always @(*) begin // This is a combinational always block
// State transition logic
case(state)
A: next_state = in?A:B;
B: next_state = in?B:A;
endcase
end
// 使用组合逻辑电路产生输出
// Output logic
// assign out = (state == ...);
assign out = (state == B);
endmodule
虽然下面这种只用了一个always块,但其基本思想依旧是”二段式”的思想
module top_module(clk, reset, in, out);
input clk;
input reset; // 同步的复位,高电平有效
input in;
output out;
//此时的状态
reg state;
parameter A = 0;
parameter B = 1;
always @(posedge clk) begin
if (reset) begin
state = B;
end else begin
case (state)
B:
if(in)
state = B;
else
state = A;
A:
if(in)
state = A;
else
state = B;
default:
state = B;
endcase
end
end
assign out = state;
endmodule
“三段式”写法
“三段式”的描述方法

- 一个always时序块使用时序逻辑电路描述此刻状态转移
- 一个always组合块使用组合逻辑电路描述下一个状态的变化
- 在使用一个always时序块去描述输出
“三段式”的优点
- 使FSM做到了同步寄存器输出
- 消除了组合逻辑输出的不稳定与毛刺的隐患
- 更利于时序路径分组
- 在FPGA/CPLD 等可编程逻辑器件上的综合与布局布线效果更佳
module top_module(
input clk,
input reset, // 同步复位,高电平有效
input in,
output reg out);
parameter A=0, B=1;
reg state, next_state;
// 此时的状态的变化
always @(posedge clk) begin
if(reset) state <= B;
else state <= next_state;
end
// 使用组合逻辑电路来判断下一个状态的变化
always @(*) begin // This is a combinational always block
// State transition logic
case(state)
A: next_state = in?A:B;
B: next_state = in?B:A;
endcase
end
// 使用组合逻辑电路产生输出
// Output logic
// assign out = (state == ...);
always@(posedge clk) begin
if(reset) out <= 1'b1;
else if(next_state==A) out <= 1'b0;
else out <= 1'b1;
end
endmodule
endmodule
三种写法的优缺点
-
“一段式”和”三段式”比较
- 一段式(不符合思维模式)
- 必须要综合考虑现态在何种状态转移条件下会进入哪些次态
- 然后在每个现态的case分支下分别描述每个次态的输出
- 三段式
- 只需指定case敏感表为次态寄存器
- 然后直接在每个次态的case分支中描述该状态的输出即可,根本不用考虑状态转移条件
- 一段式(不符合思维模式)
-
“二段式”和“三段式”比较
虽然可以改善输出的时序条件还能避免组合电路的毛刺是更为推荐的描述方式,电路设计不是一成不变地,在某些情况下,两段式结构比三段式结构更有优势。
- 两段式建模用状态寄存器,分割了两部分组合逻辑(状态转移条件组合逻辑,输出组合逻辑),因此电路时序路径较短,可以获得较高的性能;
- 三段式建模从输入到寄存器状态输出的路径上,要经过两部分组合逻辑(状态转移条件组合逻辑,输出组合逻辑)从时序上,这两部分组合逻辑完全可以看为一体,这条路径的组合逻辑就比较繁杂,该路径的时序相对紧张。

独热码

parameter IDLE = 3'b000;//初始状态
parameter STATE1=3'b001;//状态1
parameter STATE2=3'b010;//状态2
parameter STATE3=3'b100;//状态3
利用这种编码方式,虽然比起自然二进制编码多使用了触发器,但其使用组合逻辑更简单,所以一般编写状态机更加推荐此种编码方式。
可以使用下面这种编程方法,节省逻辑电路。
module top_module(
input in,
input [3:0] state,
output [3:0] next_state,
output out); //
parameter A=0, B=1, C=2, D=3;
// State transition logic: Derive an equation for each state flip-flop.
//当然组合逻辑电路也可以使用always@(*)
assign next_state[A] = ((state[A]|state[C])&(~in));
assign next_state[B] = (state[A]|state[B]|state[D])∈
assign next_state[C] = (state[B]|state[D])&(~in);
assign next_state[D] = state[C]∈
// Output logic:
assign out = state[D];
endmodule
Moore和Mealy
这篇文章介绍的不错,可以看完这篇文章之后再看下面介绍
关于摩尔型状态机与米利型状态机的区别_CrazyUncle的博客-CSDN博客_摩尔状态机与米利状态机
Mealy型与Moore
- 摩尔型:输出只与状态寄存器的状态有关(大部分状态机使用此类型,以上三种写法也主要是这种类型)
- 米粒型:输出与状态寄存器和输入有关

两种状态机的区别:主要是输出是否与输入有关
Moore型状态机
- 而Mealy型状态机与输入有关,于是在输入变化之时,输出就会立刻发生变化

Mealy型状态机
- Moore型状态机由于与输入无关,所以可以但看到,其输出与输入之间总是差一个时钟周期,也就是说,当输入变化时,输出将会在下个周期才会有所体现

以上说明moore型状态机和mealy型状态机的区别所用的是两段式状态机,也就是状态输出都是采用组合逻辑输出。如果状态输出采用寄存器输出,则需要在输出端加一个寄存器,这样会多消耗一个时钟周期,但是功能不会发生错误。
-
“三段式”代码需要注意
三段式状态机中为了不消耗一个额外的时钟周期,采用next_state作为判断条件,但是如果采用mealy型状态机,则此时的输入将会和next_state一起作为判断条件来判断输出,这样就会发生功能错误,如下图所示
![img]()
正确的做法是将输出_temp与当前输入再用组合逻辑输出,如下图所示,这样功能不会发生错误。但是其实这种做法是多此一举的,因为这样做输出还是组合逻辑,没有采用寄存器输出,并且这条通路的时序相对紧张,这么一搞的话其实应该用二段式更好一些
![img]()
例题分析
检测“101”序列,题目要求重叠检测(比如对于序列“101010”,重叠检测会输出两次高电平,不重叠检测会输出一次高电平)
Moore型


Mealy型


(1)Moore型状态机代码
-
例题代码——三段式
//三段式FSM module top_module ( input clk, input aresetn, // Asynchronous active-low reset input x, output reg z ); parameter S0 = 2'd0, S1 = 2'd1, S2 = 2'd2, S3 = 2'd3; reg [1:0] current_state; reg [1:0] next_state; always@(posedge clk or negedge aresetn)begin if(aresetn == 1'b0)begin current_state <= S0; end else begin current_state <= next_state; end end always@(*)begin case(current_state) S0:begin next_state = x ? S1 : S0; end S1:begin next_state = x ? S1 : S2; end S2:begin next_state = x ? S3 : S0; end S3:begin next_state = x ? S1 : S2; end default:begin next_state = S0; end endcase end always@(posedge clk or negedge aresetn)begin if(aresetn == 1'b0)begin z <= 1'b0; end else begin if(next_state == S3)begin z <= 1'b1; end else begin z <= 1'b0; end end end endmodule -
设计模板
设计方法:就上面的三段式
*//Moore型状态机的Verilog HDL一般结构:* **module** state_moore (reset,clk,High,Low,Cold,Heat); **input** reset,clk,High,Low; **output** **<**output1**>**, **<**output2**>**, **<**output3**>**; *//定义状态和名称* **parameter** st1_**<**name_state**>** **=** st1_**<**encode**>**; **parameter** st2_**<**name_state**>** **=** st2_**<**encode**>**; **parameter** st3_**<**name_state**>** **=** st3_**<**encode**>**; *//定义状态寄存器* **reg** Cold,Heat; **reg**[1**:**0] state, next_state; *//状态机的第一段采用同步时序描述状态转移* **always** @(**posedge** clk) **begin:** SYNC_PROC **if**(reset **==** 1) state **<=** st1_**<**name_state**>**; **else**state **<=** next_state; **end** *//状态机的第二段采用组合逻辑判断状态转移条件* **always** @(state **or** **<**input_1**>** **or** **<**input_2**>**) **begin:** NEXT_STATE_DECODE next_state **<=** state; **case** (state) st1: **beginif** (**<**input_1**>** **==** 1) next_state **<=** st2; **end** st2: **beginif** (**<**input_2**>** **==** 1) next_state **<=** st1; **end** st3: **begin** ...... **end endcaseend** *//状态机的第三段描述状态输出,只与当前状态有关 //但真实编写代码时,应该使用 //always@(posedge clk)去延时一个时钟来表示状态的转移 //并使用寄存器类型的out_temp在内部当成<output>的缓冲 //并使用assign output = out_temp;来输出* always @(state) begin: OUTPUT_DECODE if(state == st1_<name_state>) begin<output1><=1’b1; <output2><=1’b0; end else if(state == st2_<name_state>) begin <output1><=1’b0; <output2><=1’b1; end end //而如果使用三段式的话,这一部分可以这么写 always @(posedge clk) begin if(next_state == st1_<name_state>) begin<output1><=1’b1; <output2><=1’b0; end else if(next_state == st2_<name_state>) begin <output1><=1’b0; <output2><=1’b1; end end
(2)Mealy型状态机代码
-
例题代码——二段式
module top_module ( input clk, input aresetn, // Asynchronous active-low reset input x, output z ); parameter S0 = 2'd0, S1 = 2'd1, S2 = 2'd2; reg [1:0] current_state; reg [1:0] next_state; always@(posedge clk or negedge aresetn)begin if(aresetn == 1'b0)begin current_state <= S0; end else begin current_state <= next_state; end end always@(*)begin case(current_state) S0:begin next_state = x ? S1 : S0; end S1:begin next_state = x ? S1 : S2; end S2:begin next_state = x ? S1 : S0; end default:begin next_state = S0; end endcase end assign z = ((current_state == S2) && (x == 1'b1)) ? 1'b1 : 1'b0; /* //second way always@(*)begin case(current_state) S0:begin z = 1'b0; end S1:begin z = 1'b0; end S2:begin z = x; end endcase end */ endmodule -
例题代码——三段式
module top_module ( input clk, input aresetn, // Asynchronous active-low reset input x, output z ); parameter S0 = 2'd0, S1 = 2'd1, S2 = 2'd2; reg [1:0] current_state; reg [1:0] next_state; reg z_temp; always@(posedge clk or negedge aresetn)begin if(aresetn == 1'b0)begin current_state <= S0; end else begin current_state <= next_state; end end always@(*)begin case(current_state) S0:begin next_state = x ? S1 : S0; end S1:begin next_state = x ? S1 : S2; end S2:begin next_state = x ? S1 : S0; end default:begin next_state = S0; end endcase end always@(posedge clk or negedge aresetn)begin if(aresetn == 1'b0)begin z_temp <= 1'b0; end else begin if(next_state == S2)begin z_temp <= 1'b1; end else begin z_temp <= 1'b0; end end end assign z = z_temp == 1'b1 && x == 1'b1; endmodule -
设计模板
//Mealy型状态机的Verilog HDL一般结构:* module state_moore (reset,clk,High,Low,Cold,Heat); input reset,clk,High,Low; output <output1>, <output2>, <output3>; //定义状态和名称 parameter st1_<name_state> = st1_<encode>; parameter st2_<name_state> = st2_<encode>; parameter st3_<name_state> = st3_<encode>; //定义状态寄存器 reg Cold,Heat; reg [1:0] state, next_state; //状态机的第一段采用同步时序描述状态转移 always @(posedge clk) begin: SYNC_PROC if(reset == 1) state <= st1_<name_state>; else state <= next_state; end //状态机的第二段采用组合逻辑判断状态转移条件 always @(state or <input_1> or <input_2>) begin: NEXT_STATE_DECODE next_state <= state; case (state) st1: begin if (<input_1> == 1) next_state <= st2; end st2: begin if (<input_2> == 1) next_state <= st1; end st3: begin ...... end endcase end //状态机的第三段描述状态输出,与当前状态和输入信号都有关 always @(state or t1 or t2 or t3) begin: OUTPUT_DECODE if (state == st3_<name> and <input1> == 1) <output>_i <= 1’b1; else<output>_i <=1’b0; end //三段式的描述方法 always @(posegde clk) begin: OUTPUT_DECODE if(next_state == st3_<name> and <input1> == 1) <output>_i <=** 1’b1; else <output>_i <=1’b0; end


浙公网安备 33010602011771号