状态机

Moore型状态机和Mealy型状态机

一、状态机的定义

状态机就是能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作、完成特定动作的控制中心。状态机简写为 FSM (Finite State Machine),分为两类:

1:输出只和当前状态有关而与输入无关,则称为摩尔(Moore)状态机;

2:输出不仅和当前状态有关而且和输入有关,则称为米利(Mealy)状态机;

二、两种状态机的区别

1:在波形上区别:以一个序列检测器为例,检测到输入信号11时输出z为1,其他时候为0。用摩尔型FSM实现需要用到三个状态(A,B,C)。而用米利型FSM实现则需要两个状态(A,B)。摩尔型FSM输出函数的输入只由状态变量决定,要想输出z=1,必须C状态形成,即寄存器中的两个1都打进去后才可以。输出z=1会在下一个有效沿到来的时候被赋值。而米利型FSM输出函数是由输入和状态变量共同决定的。状态在B的时候如果输入为1,则直接以组合电路输出z=1,不需要等到下个有效沿到来。从而也就不需要第三个状态C。

2:摩尔状态机更安全:输出在时钟边沿变化(总是在一个周期后)。在Mealy机器中,输入更改可能会在逻辑完成后立即导致输出更改, 当两台机器互连时出现大问题 ,如果不小心,可能会发生异步反馈。

3:Mealy状态机对输入的反应更快:在相同的周期内反应 - 不需要等待时钟。在Moore机器中,可能需要更多逻辑来将状态解码为输出 - 在时钟边沿之后更多的门延迟。并非所有时序电路都可以使用Mealy模型实现。 一些时序电路只能作为摩尔机器实现。

三、经典状态机模板

1、一段式状态机

只有一个always block,把所有的逻辑(输入、输出、状态)都在一个always block的时序逻辑中实现。这种写法看起来很简洁,但是不利于维护,如果状态复杂一些就很容易出错,不推荐这种方法。在简单的状态机可以使用。

verilog 复制代码
    //时序逻辑电路
	always @(posedge clk or negedge rst_n)
		begin
			if(!rst_n)begin
			    cstate <= IDLE;
	            cmd <= 3'b000;
			end
			else
				case(cstate)
					IDLE:
						if(wr_req)begin
                            cstate <= WR_S1;
							cmd <= 3'b001;
						end
						else if(rd_req)begin
							cstate <= RD_S1;
							cmd <= 3'b011;
						end
						else begin
                            cstate <= IDLE;
							cmd <= 3'b000;
						end
					WR_S1:	begin
					    cstate <= WR_S2;
						cmd <= 3'b010;
					end
					WR_S2:	begin
					    cstate <= IDLE;
						cmd <= 3'b000;
					end
					RD_S1:
						if(wr_req)begin
							cstate <= WR_S2;
							cmd <= 3'b010;
						end
						else begin
							cstate <= RD_S2;
							cmd <= 3'b100;
						end
					RD_S2:
						if(wr_req) begin
							cstate <= WR_S1;
							cmd <= 3'b001;
						end
						else begin
							cstate <= IDLE;
							cmd <= 3'b000;
							end
					default:cstate <= IDLE;
				endcase
		end

2、两段式状态机

verilog 复制代码
//两段式状态机代码
reg[:0]  cur_state;
reg[:0]  nxt_state;
/**************** 第一段:描述状态跳转(时序逻辑)****************/
always@(posedge clk or negedge rst_n)
	if(!rst_n)
        cur_state<=IDLE;
	else 
        cur_state<=nxt_state;
/**************** 第二段:状态判断及输出(组合逻辑)****************/
always@(*)
	begin
        case(cur_state)
		IDLE:if()
				begin
					nxt_state=;
					out=; 
                end 
             else if()
                 begin 
                     nxt_state=; 
                     out=;
				end
			else 
				begin
					 nxt_state=;
					 out=; 
                end 
        WR_S1:
               begin 
                     nxt_state=;
                     out=;
               end
		WR_S2:begin 
            		nxt_state=;
                     out=;
        	  end 
        RD_S1:
            if(wr_req) 
                begin 
                    nxt_state=;
                     out=;
                end
			else begin 
                nxt_state=;
                     out=;
            end 
        RD_S2:if(wr_req)
            	begin 
                	nxt_state=;
                     out=;
           		end
			  else 
                  begin 
                     nxt_state=;
                     out=;
                  end
		default:begin
            		nxt_state=;
                     out=;
        end
		endcase
	end

3、三段式状态机模板

verilog 复制代码
reg        [:]            current_state           ;
reg        [:]            next_state              ;

wire        [:0]            IDLE             ;
wire        [:0]            S0                ;
wire        [:0]            S1                ;
wire        [:0]            S2                ;

//=============================================================================\
//****************************     State Machine    *******************************//
//=============================================================================\
/**************** 第一段:描述状态跳转(时序逻辑)****************/
always @(posedge sclk or negedge s_rst_n) begin
    if(!s_rst_n)
        current_state <= IDLE;
    else
        current_state <= next_state;
end
/**************** 第二段:下一状态判断(组合逻辑)****************/
always @(*) begin
    next_state = IDLE;
    case(current_state)
        IDLE:begin
            if(idle2s0 == 1'b1)
                next_state = S0;
            else
                next_state = current_state;
        end

        S0:begin
            if(s02s1 == 1'b1)
                next_state = S1;
            else
                next_state =current_state;
        end

        S1:begin
            if(s12s2 == 1'b1)
                next_state = S2;
            else
                next_state = current_state;
        end

        S2:begin
            if(s22idle == 1'b1)
                next_state = IDLE;
            else
                next_state = current_state;
        end

        default:begin
            next_state = IDLE;
        end
    endcase
end
/**************** 第三段:当前状态输出(可以是组合逻辑也可以是时序逻辑)****************/
always @(*) begin
    case(current_state):
        IDLE:
        S0:
        S1:
        S2:
        S3:
        deafult:
end

4、摩尔型状态机

(1)非重叠检测 1101 1101

状态转移图:

verilog 复制代码
//采用了三段式,moore状态机

module state_test(
    input                    sclk                ,
    input                    s_rst_n            ,

    input                    din                ,
    output    reg            dout
);

//========================================================================\
// =========== Define Parameter and Internal signals ===========
//========================================================================/

reg            [4:0]        current_state        ;
reg            [4:0]        next_state           ;

parameter   S0          =        5'b00001        ;
parameter   S1          =        5'b00010        ;
parameter   S2          =        5'b00100        ;
parameter   S3          =        5'b01000        ;
parameter   S4          =        5'b10000        ;
/*
	这里五个状态用了5bit,为什么要用独热码呢?通常状态变量还可以通过二进制码或格雷码的方式对状态进行编码,为什么例子中我们使用的是独热码而非二进制码或格雷码呢?那就要从每种编码的特性上说起了,首先独热码因为每个状态只有1bit是不同的,所以在执行到55行时的(state == TWO)这条语句时,综合器会识别出这是一个比较器,而因为只有1比特为1,所以综合器会进行智能优化为(state[2] == 1' b1),这就相当于把之前3比特的比较器变为了1比特的比较器,大大节省了组合逻辑资源,但是付出的代价就是状态变量的位宽需要的比较多,而我们FPGA中组合逻辑资源相对较少,所以比较宝贵,而寄存器资源较多,所以很完美。而二进制编码的情况和独热码刚好相反,他因为使用了较少的状态变量,使之在减少了寄存器状态的 同时无法进行比较器部分的优化,所以使用的寄存器资源较少,而使用的组合逻辑资源较多,我们还知道CPLD就是一个组合逻辑资源多而寄存器逻辑资源少的器件,因为这里我们使用的是FPGA器件,所以使用独热码进行编码。就因为这个比较部分的优化,还使得使用独热码编码的状态机可以在高速系统上运行,其原因是多比特的比 较器每个比特到达比较器的时间可能会因为布局布线的走线长短而导致路径延时的不同,这样在高速系统下,就会导致采集到不稳定的状态,导致比较后的结果产生一个时钟的毛刺,使输出不稳定,而单比特的比较器就不用考虑这种问题。
	用独热码编码虽然好处多多,但是如果状态数非常多的话即使是FPGA也吃不消独热码对寄存器的消耗,所以当状态数特别多的时候可以使用格雷码对状态进行编码。格雷码虽然也是和二进制编码一样使用的寄存器资源少,组合逻辑资源多,但是其相邻状态转换时只有一个状态发生翻转,这样不仅能消除状态转换时由多条信号线的传输延 迟所造成的毛刺,又可以降低功耗,所以要优于二进制码的方式,相当于是独热码和二进制编码的折中。
通常,小于4个状态,使用独热码,4-24个使用二进制编码,大于24个使用格雷码。
*/
//=============================================================================
//****************************     Main Code    *******************************
//=============================================================================
/********************** 第一段 状态转移 (时序逻辑)**********************/
always @(posedge sclk or negedge s_rst_n) begin
    if(!s_rst_n)
        current_state <= S0;
    else
        current_state <= next_state;
end
/********************** 第二段 下一状态判断 (组合逻辑)**********************/
always @(*) begin
    next_state = S0;
    case(current_state)
        S0:begin
            if(din == 1'b1)  // 1
                next_state = S1;
            else
                next_state = current_state;  // 0
        end

        S1:begin
            if(din == 1'b1)
                next_state = S2; // 11
            else
                next_state = S0; // 10
        end

        S2:begin
            if(din == 1'b0)  // 110
                next_state = S3;
            else
                next_state = current_state; // 111
        end

        S3:begin
            if(din == 1'b1)
                next_state = S4;  // 1101
            else
                next_state = S0;  // 1100
        end

        S4:begin
            if(din == 1'b1)  // 1101 1
                next_state = S1;
            else
                next_state = S0; // 1101 0
        end

        default:begin
            next_state = S0;
        end
    endcase
end
/********************** 第三段 不同状态的输出 (组合逻辑)**********************/
//输出只与当前状态有关,与输入无关
always @(posedge sclk or negedge s_rst_n) begin
    if(!s_rst_n) begin
        dout <= 0;
    end
    else if(current_state == S4)
        dout <= 1;
    else
        dout <= 0;
end
endmodule

5、米勒型状态机1101 序列检测

verilog 复制代码
module mealy_state(
    input                sclk        ,
    input                s_rst_n    ,

    input                din        ,
    output    reg        dout
    );

//========================================================================\
// =========== Define Parameter and Internal signals ===========
//========================================================================/


reg            [3:0]            current_state    ;
reg            [3:0]            next_state        ;

parameter            IDLE        =    4'b0001    ;
parameter            S0        =    4'b0010    ;
parameter            S1        =    4'b0100    ;
parameter            S2        =    4'b1000    ;

//=============================================================================\
//****************************     State Machine    *******************************
//=============================================================================\
/********************** 第一段 状态转移 (时序逻辑)**********************/
always @(posedge sclk or negedge s_rst_n) begin
    if(!s_rst_n)
        current_state <= IDLE;
    else
        current_state <= next_state;
end
/********************** 第二段 下一状态判断 (组合逻辑)**********************/
always @(*) begin
    next_state = IDLE;
    case(current_state)
        IDLE:begin
            if(din == 1'b1)
                next_state = S0;
            else
                next_state = current_state;
        end

        S0:begin
            if(din == 1'b1)
                next_state = S1;
            else
                next_state = IDLE;
        end

        S1:begin
            if(din == 1'b0)
                next_state = S2;
            else
                next_state = current_state;
        end

        S2:begin
            if(din == 1'b1)
                next_state = IDLE;
            else
                next_state = IDLE;
        end

        default:begin
            next_state = IDLE;
        end
    endcase
end
/********************** 第三段 输出不仅与当前状态有关还与当前输入有关 (时序逻辑)**********************/
always @(posedge sclk or negedge s_rst_n) begin
    if(!s_rst_n)
        dout <= 1'b0;
    else if(current_state == S2 && din == 1'b1)
        dout <= 1'b1;
    else
        dout <= 1'b0;
end

endmodule

四、总结

​ 老的一段式、二段式、三段式各有优缺点,其中一段式在描述大型状态机时会比较困难,会使整个系统显得十分臃肿,不够清晰;二段式状态机的好处是其结构和理想的理论模型完全吻合,即不会有附加的结构存在,比较精简,但是由于二段状态机的第二段是组合逻辑描述数据的输出,所以有一些情况是无法描述的,比如输出时需要类似计 数的累加情况,这种情况在组合逻辑中会产生自迭代,自迭代在组合逻辑电路中是严格禁止的,而且第二段状态机主要是描述数据的输出,输出时使用组合逻辑往往会产生更多的毛刺,所以并不推荐。所以衍生出三段式状态机,三段状态机的输出就可是时序逻辑了,但是其结构并不是最精简的了。三段式状态机的第一段状态机是用时序逻辑 描述当前状态,第二段状态机是用组合逻辑描述下一状态,如果把这两个部分进行合并而第三段状态机保持不变,就是我们现在最新的二段式状态机了。这种新的写法在现在不同综合器中都可以被识别出来,这样既消除了组合逻辑可能产生的毛刺,又减小了代码量,还更加容易上手,不必再去关心理论模型是怎样的,仅仅根据状态转移图就 非常容易实现,对初学者来说十分友好。所以我们习惯性的使用两个均采用时序逻辑的 always 块,第一个 always 块描述状态的转移为第一段状态机,第二个 always 块描述数据的输出为第二段状态机(如果我们遵循一个always块只描述一个变量的原则,如果有多个输出时第二段状态机就可以分为多个always块来表达,但理论上仍属于新二段状态机,所以几段式状态机并不是由always块的数量简单决定的)。

verilog 复制代码
module simple_fsm
(
input wire sys_clk , //系统时钟50MHz
input wire sys_rst_n , //全局复位
input wire pi_money , //投币方式可以为:不投币(0)、投1元(1)

output reg po_cola //po_cola为1时出可乐,po_cola为0时不出可乐

);

 ////
 //\* Parameter and Internal Signal \//
 ////

 //parameter define
 //只有三种状态,使用独热码
 parameter IDLE = 3'b001;
 parameter ONE = 3'b010;
 parameter TWO = 3'b100;

 //reg define
 reg [2:0] state ;

 ////
 //\* Main Code \//
 ////

 //第一段状态机,描述当前状态state如何根据输入跳转到下一状态
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 	state <= IDLE; //任何情况下只要按复位就回到初始状态
 else case(state)
 IDLE : if(pi_money == 1'b1) //判断输入情况
 	state <= ONE;
 else
 	state <= IDLE;

 ONE : if(pi_money == 1'b1)
 	state <= TWO;
 else
 	state <= ONE;

 TWO : if(pi_money == 1'b1)
 	state <= IDLE;
 else
 	state <= TWO;
 //如果状态机跳转到编码的状态之外也回到初始状态
 default: state <= IDLE;
 endcase

 //第二段状态机,描述当前状态state和输入pi_money如何影响po_cola输出
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 	po_cola <= 1'b0;
 else if((state == TWO) && (pi_money == 1'b1))
 	po_cola <= 1'b1;
 else
 	po_cola <= 1'b0;
 endmodule

五、参考

1、 野火]FPGA Verilog开发实战指南------基于Altera EP4CE10 征途Pro开发板 文档

2、Moore型状态机和Mealy型状态机 - 青河 - 博客园