状态机设计

状态机设计

状态机概念

  • 状态机的本质

    对具有逻辑顺序时序规律事件的一种描述方法。所有具有逻辑顺序和时序规律的事情都适合用状态机描述

  • 状态机的应用思路

    1. 从状态变量入手如果一个电路具有时序规律或者逻辑顺序,我们就可以自然而然地规划出状态,从这些状态入手,分析每个状态的输入,状态转移和输出,从而完成电路功能;
    2. 明确电路的输出的关系,这些输出相当于状态的输出,回溯规划每个状态和状态转移条件与状态输入,无论那种思路,使用状态机的目的都是要控制某部分电路,完成某种具有逻辑顺序或时序规律的电路设计。
  • 状态机的基本描述方式

    状态机的基本要素:状态、输出、输入

    基本描述方式:状态转移图、状态转移表、HDL语言描述

什么是 RTL级好的FSM 描述?

  1. FSM要安全,稳定性高。(优先级最高,最需要保证)
    1. 不会进入死循环,不会进入非预知的状态
    2. 要求该FSM的综合实现结果,无毛刺等异常扰动
    3. 要求状态机要完备这可能就会使用”full case”的编码方式,使得所有的情况都有所处理,势必会花费更多的设计资源,有时会影响FSM的频率
  2. FSM速度快,满足设计的频率要求。
  3. FSM面积小,满足设计的面积要求。
  4. FSM设计要清晰易懂、易维护。

“一段式”写法

只利用一个always组合块,把需要写出的此刻的状态的不断变化,输出也糅合进去。

img

在描述当前状态时要考虑下个状态的输出,整个代码不清晰

  • 不利于维护修改
  • 不利于附加约束
  • 不利于综合器和布局布线器对设计的优化
  • 另外,这种描述相对于两段式描述比较冗长。对复杂的状态机来说,一段式要比二段式冗长大约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

“二段式”写法

“二段式”描述方法

img

  1. 一个always组合块使用组合逻辑电路描述下一个状态的变化
  2. 一个always时序块使用时序逻辑电路描述此刻状态转移
  3. 使用assign或者always组合块使用组合逻辑电路描述输出状态的变化

“二段式”缺点以及解决办法

其输出一般使用组合逻辑描述,而组合逻辑易产生毛刺等不稳定因素。

解决办法

  1. 两段式 FSM 描述方法,如果时序允许插入,一个额外的时钟节拍,可以有效的解决毛刺现象,
  2. 但有的无法添加节拍,使用三段式的描述方法

代码

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

“三段式”写法

“三段式”的描述方法

img

  1. 一个always时序块使用时序逻辑电路描述此刻状态转移
  2. 一个always组合块使用组合逻辑电路描述下一个状态的变化
  3. 在使用一个always时序块去描述输出

“三段式”的优点

  1. 使FSM做到了同步寄存器输出
  2. 消除了组合逻辑输出的不稳定与毛刺的隐患
  3. 更利于时序路径分组
  4. 在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分支中描述该状态的输出即可,根本不用考虑状态转移条件
  • “二段式”和“三段式”比较

    虽然可以改善输出的时序条件还能避免组合电路的毛刺是更为推荐的描述方式,电路设计不是一成不变地,在某些情况下,两段式结构比三段式结构更有优势。

    • 两段式建模用状态寄存器,分割了两部分组合逻辑(状态转移条件组合逻辑,输出组合逻辑),因此电路时序路径较短,可以获得较高的性能;
    • 三段式建模从输入到寄存器状态输出的路径上,要经过两部分组合逻辑(状态转移条件组合逻辑,输出组合逻辑)从时序上,这两部分组合逻辑完全可以看为一体,这条路径的组合逻辑就比较繁杂,该路径的时序相对紧张。

img

独热码

img

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])&in;
    assign next_state[C] = (state[B]|state[D])&(~in);
    assign next_state[D] =  state[C]&in;

    // Output logic: 
    assign out = state[D];

endmodule

Moore和Mealy

这篇文章介绍的不错,可以看完这篇文章之后再看下面介绍

关于摩尔型状态机与米利型状态机的区别_CrazyUncle的博客-CSDN博客_摩尔状态机与米利状态机

Mealy型与Moore

  1. 摩尔型:输出只与状态寄存器的状态有关(大部分状态机使用此类型,以上三种写法也主要是这种类型)
  2. 米粒型:输出与状态寄存器和输入有关

img

两种状态机的区别:主要是输出是否与输入有关

Moore型状态机

  • 而Mealy型状态机与输入有关,于是在输入变化之时,输出就会立刻发生变化

img

Mealy型状态机

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

img

以上说明moore型状态机和mealy型状态机的区别所用的是两段式状态机,也就是状态输出都是采用组合逻辑输出。如果状态输出采用寄存器输出,则需要在输出端加一个寄存器,这样会多消耗一个时钟周期,但是功能不会发生错误。

  • “三段式”代码需要注意

    三段式状态机中为了不消耗一个额外的时钟周期,采用next_state作为判断条件,但是如果采用mealy型状态机,则此时的输入将会和next_state一起作为判断条件来判断输出,这样就会发生功能错误,如下图所示

    img

    正确的做法是将输出_temp与当前输入再用组合逻辑输出,如下图所示,这样功能不会发生错误。但是其实这种做法是多此一举的,因为这样做输出还是组合逻辑,没有采用寄存器输出,并且这条通路的时序相对紧张,这么一搞的话其实应该用二段式更好一些

    img

例题分析

检测“101”序列,题目要求重叠检测(比如对于序列“101010”,重叠检测会输出两次高电平,不重叠检测会输出一次高电平)

Moore型

img

img
Mealy型

img

img

(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
    
    
posted @ 2024-07-28 21:26  NullBeer  阅读(330)  评论(0)    收藏  举报