状态机详解(一段式、二段式、三段式)

一、有限状态机FSM(Finite State Machine)

  • 组成元素

        输入、状态、状态转移条件、输出。

 

  • 可以分为两类

        Mealy状态机:时序逻辑的输出不仅取决于当前状态,还与输入有关;

        Moore状态机:时序逻辑的输出只与当前状态有关。

 

  • 描述方式

        ① 状态转移图:设计分析时使用,工具自动翻译的代码效率不高,适合规模小的设计;对于大规模设计,HDL更好;

        ② 状态转移表;

        ③ HDL描述。

 

  • 设计步骤

        ① 逻辑抽象,得到状态转移图:确定输入、输出、状态变量、画状态转移图;

        ② 状态简化,得到最简的状态转移图:合并等价状态;

        ③ 状态编码:binary、gray、one-hot编码方式;

        ④ 用HDL描述。

 

二、Coding Style

  • 一段式

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

        在简单的状态机可以使用。

 

  • 二段式

        有两个always block,把时序逻辑和组合逻辑分隔开来。时序逻辑里进行当前状态和下一状态的切换,组合逻辑实现各个输入、输出以及状态判断。这种写法不仅便于阅读、理解、维护,而且利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。在两段式描述中,当前状态的输出用组合逻辑实现,可能存在竞争和冒险,产生毛刺

        要求对状态机的输出用寄存器打一拍,但很多情况不允许插入寄存器节拍,此时使用三段式描述。其优势在于能够根据状态转移规律,在上一状态根据输入条件判断出当前状态的输出,从而不需要额外插入时钟节拍。

 

  • 三段式

        有三个always block,一个时序逻辑采用同步时序的方式描述状态转移,一个采用组合逻辑的方式判断状态转移条件、描述状态转移规律,第三个模块使用同步时序的方式描述每个状态的输出。代码容易维护,时序逻辑的输出解决了两段式组合逻辑的毛刺问题,但是从资源消耗的角度上看,三段式的资源消耗多一些。

        模板如下:

always @ ( posedge clk or negedge rst_n ) 
    begin  
        if ( !rst_n )  
            CS <= IDLE;  
        else  
            CS <= NS;  
    end  
  
always @*  
    begin  
        NS = 'bx;                   //初始化寄存器,避免生成latch  
        case (CS)                   //注意为CS  
            IDLE: begin  
                  end;  
            S1: begin  
                end;  
            default:  
                NS = 'bx;           //与硬件电路一致  
        endcase  
    end  
  
always @ (posedge clk or negedge rst_n) 
    begin  
        if ( !rst_n ) 
            begin  
            end  
        else 
            begin  
                ...                 //初始化一组值,避免latch  
                case (CS/NS)        //这里有2种写法,推荐NS写法(moore型写法) 
                    ...  
                    default: ;  
                endcase  
            end  
    end  

 

下面,举例说明:

        状态转换图如下所示:

状态机详解(一段式、二段式、三段式)_第1张图片

 

  • 一段式:
    //时序逻辑电路
	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

testbench如下: 

`timescale 1 ns/ 100 ps

module fsm1_vlg_tst();

	reg clk;
	reg rd_req;
	reg rst_n;
	reg wr_req;                                             
	wire [2:0]  cmd;
	wire [2:0]  cstate;
                          
	fsm1 i1 (   
		.clk(clk),
		.cmd(cmd),
		.cstate(cstate),
		.rd_req(rd_req),
		.rst_n(rst_n),
		.wr_req(wr_req)
	);
	always #10 clk = ~clk;
	
	initial                                                
		begin
			clk = 0;rst_n = 1;
			wr_req = 0;rd_req = 0;
			#2 rst_n = 0;
			#10 rst_n = 1;
			repeat(100)
				begin
					#20 wr_req = {$random}%2;
						rd_req = {$random}%2;
				end
			#100 $stop;
		end                                                    
                                                    
endmodule

功能仿真波形图:

 

 三段式:

always block①:时序逻辑

//1st always block, sequential logic, store current state
always @(posedge clk or negedge rst_n)
		if(!rst_n)
			cstate <= IDLE;
		else
			cstate <= nstate;

 

always block②:组合逻辑 

//2nd always block, combinational logic, decide next state
always @(cstate or wr_req or rd_req)
		begin
			case(cstate)
				IDLE:	if(wr_req)
							nstate = WR_S1;
						else if(rd_req)
							nstate = RD_S1;
						else
							nstate = IDLE;
				WR_S1:		nstate = WR_S2;
				WR_S2:		nstate = IDLE;
				RD_S1:	if(wr_req)
							nstate = WR_S2;
						else
							nstate = RD_S2;
				RD_S2:	if(wr_req)
							nstate = WR_S1;
						else
							nstate = IDLE;
				default:	nstate = 3'bx;
			endcase
		end
  •  注意

        always @(敏感电平信号)需要列举完全,可以用“@*”或者“@(*)”代替;

        case(表达式)中的表达式为“cstate”,即现态

        阻塞赋值“=”;

        default项中“'bx”必须设置,与实际电路一致。

 

always block③:时序逻辑 

//3rd always block, FSM sequential output

  • Mealy型写法
always @(posedge clk or negedge rst_n)
		if(!rst_n)
			cmd <= 3'b000;
		else
			case(cstate)
				IDLE:	if(wr_req)
							cmd <= 3'b001;
						else if(rd_req)
							cmd <= 3'b011;
						else
							cmd <= 3'b000;
				WR_S1:		cmd <= 3'b010;
				WR_S2:		cmd <= 3'b000;
				RD_S1:	if(wr_req)
							cmd <= 3'b010;
						else
							cmd <= 3'b100;
				RD_S2:	if(wr_req)
							cmd <= 3'b001;
						else
							cmd <= 3'b000;
				default:;
			endcase
  • 注意

        case(表达式)中的表达式为“cstate”,即现态

        非阻塞赋值“<=”; 

        default项必须设置。

 

  • Moore型写法
always @(posedge clk or negedge rst_n)
		if(!rst_n)
			cmd <= 3'b000;
		else
			case(nstate)
				IDLE:	cmd <= 3'b000;
				WR_S1:	cmd <= 3'b001;
				WR_S2:	cmd <= 3'b010;
				RD_S1:	cmd <= 3'b011;
				RD_S2:	cmd <= 3'b100;
				default:;
			endcase

endmodule
  • 注意

        case(表达式)中的表达式为“nstate”,即次态,这里使用nextstate和state的区别在于,当状态跳转时,基于nextstate的输   出是立刻变化的,而基于state输出会延迟一个周期,其他情况都一样,应该根据自己的时序要求,选择用nextstate还是state。

        非阻塞赋值“<=”; 

        default项必须设置。

 

 

参考状态机详解,以及三段式状态机的思维陷阱。

你可能感兴趣的:(FPGA)