状态机的设计(Verilog)

状态机的基本概念

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

状态机写法

状态机一般有2种不同的写法,即两段式和三段式的状态机写法。
下面就前面提出的SRAM控制状态机给出2种不同的写法以及它们综合出的效果,供大家学习参考。wr_req和rd_req作为输入,cmd为输出,cstate、nstate为状态寄存器。
两段式状态机

//两段式状态机
 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

三段式状态机

//两段式状态机
 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 			 nstate = 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

两段式状态机是一种常用的写法,它把时序逻辑和组合逻辑划分开来,时序逻辑里进行当前状态和下一状态的切换,组合逻辑里实现各个输入输出以及状态判断。这种写法相对容易维护,不过组合逻辑输出较易出现毛刺等常见问题。三段式状态机写法也是一种比较推荐的写法,代码容易维护,时序逻辑的输出解决了两段式写法中组合逻辑的毛刺问题。但是从资源消耗上比较,三段式的资源消耗多一些。

注:以上文章来源于《深入浅出玩转FPGA》特权同学。谢谢原作者的付出。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很抱歉,我是一个文本AI模型,无法提供图形化的设计图。但我可以帮助你理解如何使用Verilog设计状态机。 在Verilog中,可以使用有限状态机(FSM)来描述系统的行为。以下是一个简单的Verilog代码示例,展示了一个基本的状态机设计: ```verilog module fsm ( input wire clk, input wire reset, input wire input_signal, output wire output_signal ); // 状态定义 typedef enum logic [1:0] { STATE_A, STATE_B, STATE_C } state_t; // 状态寄存器 reg state_t current_state; always @(posedge clk, posedge reset) begin if (reset) begin current_state <= STATE_A; // 初始化为初始状态 end else begin case (current_state) STATE_A: begin // 在状态A时的逻辑 if (input_signal) begin current_state <= STATE_B; // 转移到状态B end end STATE_B: begin // 在状态B时的逻辑 if (!input_signal) begin current_state <= STATE_C; // 转移到状态C end end STATE_C: begin // 在状态C时的逻辑 if (input_signal) begin current_state <= STATE_A; // 转移到状态A end end default: current_state <= STATE_A; endcase end end // 输出逻辑 assign output_signal = (current_state == STATE_C); endmodule ``` 在这个例子中,我们使用了一个时钟信号 `clk` 和一个复位信号 `reset`。然后定义了一个输入信号 `input_signal` 和一个输出信号 `output_signal`。状态定义使用了一个 `typedef` 枚举类型 `state_t` 来表示状态,其中包含了三个状态:STATE_A、STATE_B和STATE_C。状态寄存器 `current_state` 用于存储当前的状态。 在 `always` 块中,我们使用 `case` 语句根据当前状态执行相应的逻辑。通过改变状态寄存器 `current_state` 的值,可以实现状态之间的转移。在每个状态下,可以根据输入信号 `input_signal` 的值来决定是否进行状态转移。 最后,通过 `assign` 语句将输出信号 `output_signal` 与状态C进行关联。 这只是一个简单的示例,你可以根据需要扩展和修改它适应你的具体设计要求。希望以上信息能对你有所帮助!如果你有更多问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值