• 【深入浅出玩转FPGA学习4----漫谈状态机设计】


    状态机的基本概念

    硬件设计很讲究并行设计思想,虽然用Verilog描述的电路大都是并行实现的,但是对于实际的工程应用,往往需要让硬件来实现一些具有一定顺序的工作,这就要用到状态机的思想。简单的说,状态机就是通过不同的状态迁移来完成一些特定的顺序逻辑。硬件的并行性决定了用Verilog描述的硬件实现(譬如不同的always语句)都是并行执行的,那么如果希望分多个时间完成一个任务,怎么办?也许可以用多个使能信号来衔接多个不同的模块,但是这样做多少显得有些繁琐。状态机的提出就会大大简化这一工作。
    下面举一个SRAM控制的例子来说明状态机。如图所示,它表示了一个SRAM控制状态的变化
    在这里插入图片描述
    首先,在系统复位信号rst_n=0(复位有效)后,进入IDLE状态。每当rst_n=0(复位有效)时,都会保持在IDLE状态;当rst_n=1(复位完成),如果wr_req=1就进入WR_S1状态,如果rd_req=1就会进入RD_S1状态,否则保持IDLE状态不变。相应的,只要满足一定条件或者有时不需要任何条件,系统会在这些固定的状态间进行切换。这样做的好处在于每当需要操作SRAM时,其他模块只要发出一个wr_req或者rd_req信号(置高),系统就会进入相应状态并根据不同状态对SRAM的控制总线、地址总线和数据总线进行赋值。
    构成状态机的基本要素是状态机的输入、输出和状态。输入就是一些引发状态变化的条件,比如图中的wr_req和rd_req的变化会引发状态的迁移,那么它们就是输入;输出就是状态变化后引起的变化,图中的控制总线、地址总线和数据总线的输出值就是由状态变化后引起的变化,状态就是IDLE、WR_S1、WR_S2等,它们一般是由一些逻辑值来表示。
    状态机根据其状态变化是否与输入条件相关分为两类,即Moore型状态机和Mealy型状态机。Moore型状态机的状态变化仅和当前状态有关,而与输入条件无关;Mealy型状态机的状态变化不仅与当前的状态有关,还取决于当前的输入条件。
    有些分类还会提出有限状态机(FSM)和无限状态机(ISM),但是实际设计中一般都指的是有限状态机。

    三种不同状态机写法

    状态机一般有三种不同的写法,即一段式、两段式和三段式的状态机写法,它们在速度、面积、代码可维护性等各个方面互有优劣。以下为SRAM控制状态机给出的三种不同的写法以及它们综合出的效果。wr_req和rd_req作为输入,cmd为输出,cstate、nstate为状态寄存器。

    一段式状态机

    一段式状态机代码如下:

    reg[3:0] cstate;
    always @ (posedge clk or negedge rst_n) begin
    		if(!rst_n) begin
    			cstate <= IDLE;
    			cmd <= 3'b111;
    		end
    	else
    		case(cstate)
    			IDLE: if(wr_req) begin
    							cstate <= WR_S1;
    							cmd <= 3'b011;
    					end
    			      else begin
    					cstate <= IDLE;
    					cmd <= 3'b111;
    				  end
    		    WR_S1: begin
    						cstate <= WR_S2;
    						cmd <= 3'b101;
    					end
    			WR_S2: begin
    			           cstate <= IDLE;
    			           cmd <= 3'b111;
    			         end
    			RD_S1: if (wr_req) begin
    						cstate <= WR_S2;
    						cmd <= 3'b101;
    					end
    				else begin
    						cstate <= RD_S2;
    						cmd <= 3'b110;
    					end
    			RD_S2: if (wr_req) begin
    						cstate <= WR_S1;
    						cmd <= 3'b011;
    					end
    				else begin
    						cstate <= IDLE;
    						cmd <= 3'b111;
    					end
    			default: cstate <= IDLE;
    			endcase
    	end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    一段式状态机RTL视图见图:
    在这里插入图片描述
    一段式状态机综合后资源使用报告:
    在这里插入图片描述在这里插入图片描述在这里插入图片描述

    两段式状态机

    两段式状态机代码如下:

    reg[3:0] cstate;
    reg[3:0] nstate;
    always @ (posedge clk or negedge rst_n) 
    		if(!rst_n)  cstate <= IDLE;
    		else cstate <= nstate;
    always @ (cstate or wr_req or rd_req) begin
    		case(cstate)
    				IDLE:	if(wr_req) begin
    										nstate = WR_S1;
    										cmd = 3 'b011;
    								end
    							else if(rd_req) begin
    										nstate = RD_S1;
    										cmd = 3'b011;
    									end
    								 else begin
    								 		nstate = IDLE;
    								 		cmd = 3'b111;
    								 	end
    				WR_S1: begin
    										nstate = WR_S2;
    										cmd = 3'b101;
    								end
    				WR_S2: begin
    										nstate = IDLE;
    										cmd = 3'b111;
    								end
    				RD_S1: if(wr_req) begin
    										nstate = WR_S2;
    										cmd = 3'b101;
    								end
    							else begin
    										nstate = RD_S2;
    										cmd = 3'b110;
    								end
    				RD_S2: if(wr_req) begin
    										nstate = WR_S1;
    										cmd = 3'b011;
    								end
    						    else begin
    						    			nstate = IDLE;
    						    			cmd = 3'b111;
    						    	end
    				default: nstate = IDLE;
    				endcase
    		end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    两段式状态机RTL视图见图:
    在这里插入图片描述
    两段式状态机综合后资源使用报告:
    在这里插入图片描述在这里插入图片描述

    三段式状态机

    三段式状态机代码如下:

    reg[3:0] cstate;
    reg[3:0] nstate;
    always @ (posedge clk or negedge rst_n)
    	if(!rst_n) cstate <= IDLE;
    	else cstate <= nstate;
    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 nastate = RD_S2;
    			RD_S2: if(wr_req) nstate = WR_S1;
    			 			else nstate = IDLE;
    		default: nstate = IDLE;
    		endcase
    end
    always @ (posedge clk or negedge rst_n) begin
    	if(!rst_n) cmd <= 3'b111;
    	else
    			case(nstate)
    						IDLE: 	if(wr_req) cmd <= 3'b011;
    									else if(rd_req) cmd <= 3'b011;
    									else cmd <= 3'b111;
    						WR_S1: cmd <= 3'b101;
    						WR_S2: cmd <= 3'b111;
    						RD_S1: if(wr_req) cmd <= 3'b101;
    									 else cmd <= 3'b110;
    						RD_S2: if(wr_req) cmd <= 3'b011;
    									  else cmd <= 3'b111;
    				default: ;
    				endcase
    	end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    三段式状态机RTL视图见图:
    在这里插入图片描述
    三段式状态机综合后资源使用报告:
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    从上面的三个实例来看,一段式状态机似乎是一锅端,把所有逻辑(包括输入、输出、状态)都在一个always里解决了;这种写法看上去好像很简洁,但是往往不利于维护,也许这个实例中体现得还不那么明显,如果状态复杂一些就很容易出错了;这种写法一般不太推荐,但是在一些简单得状态机中还是可以使用的。两段式状态机是一种常用的写法,他把时序逻辑和组合逻辑划分开来,时序逻辑里进行当前状态和下一状态得切换,组合逻辑实现各个输入、输出以及状态判断;这种写法相对容易维护,不过组合逻辑输出较易出现毛刺等常见问题。三段式状态机写法也是一种比较推荐得写法,代码容易维护,时序逻辑得输出解决了两段式写法种组合逻辑得毛刺问题;但是从资源消耗上来讲,三段式得资源消耗多一些;另外,三段式从输入到输出比一段式和两段式会延时一个时钟周期。

  • 相关阅读:
    LeetCode:2. 两数相加
    Gem5 simpoint 全流程
    Shell编程三剑客之awk
    OpenCV基础(二):绘制直线、绘制几何图形、绘制文字、创建窗口
    设计模式---单例模式
    韩顺平java 515-520即时笔记
    小规模自建 Elasticsearch 的部署及优化
    4.构建高可用的 Eureka-Server 集群(springcloud)
    事件相关电位ERP的皮层溯源分析
    29.添加录入注入信息界面
  • 原文地址:https://blog.csdn.net/qq_38617667/article/details/125528798