一、有限状态机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 = 'b0;                   //初始化寄存器,避免生成latch  
        case (CS)                   //注意为CS  
            IDLE: begin  
            end;  
            S1: begin  
            end;  
            default:  
                NS = 'b0;           //与硬件电路一致  
        endcase  
    end  
  
always @ (posedge clk or negedge rst_n) 
    begin  
        if(!rst_n) begin  
        end  
        else begin 
            case (CS/NS)        //这里有2种写法,推荐NS写法(moore型写法) 
                ...  
                default: ;  
            endcase  
        end  
    end  

下面,举例说明:

        状态转换图如下所示:

  • 一段式:
    //时序逻辑电路
	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
            nstate = 3'b0;
			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'b0;
			endcase
		end
  •  注意

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

       初始值设置避免组合逻辑条件不全生成latch,但不一定符合设计。

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

        阻塞赋值“=”;

        default项必须设置,与实际电路一致。

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项必须设置。

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

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐