FPGA之状态机--基于D触发器的时序逻辑基本类型

Verilog HDL 的语句块都是并行执行的, 但是在很多情况下,我们希望执行按照顺序的方式进行,而状态机就可以很好的实现顺序执行。

状态机组成要素:状态、初态、转移条件、输入符号集、输出符号集
状态机工作要素:现态、次态(下一状态)、输入、输出
有限状态机(Finite State Machine, FSM) 又简称为状态机, 是我们用Verilog HDL 描述数字电路的重要组成方式。状态机分为 mealy 型和 moore 型,其中 mealy 型指的是输出不仅与当前状态有关,还与输入有关,如下面举例的自动售卖机,输出饮料与否不仅与当前状态(你已经投入多少钱了)有关 ,还与输入(当前时刻你是否投入钱)有关。moore 型指的是输出只与现态有关。
我们使用状态机描述一个简单的自动售货机,该售货机中的商品 3 元一件,每次投币只能投入1 元。
在这里插入图片描述
就着这张图还得补充说明一点,就是状态数的确定。状态有0元、1元、2元三种,为什么没有三元呢?三元的时候就类似于钟表的十二点,和0点是一个时刻。在三元这个’状态‘下,输出1,然后等待下一个状态1元,单从状态转移来看,不考虑输出,’状态3元‘和状态0元一样都是等待状态1元。
同样的例子,密码锁的例子,密码是321三位,状态也是三种。
其实上述说法不正确,这里用的mealy型状态机,所以’状态3元‘和状态0元是可以认为1个状态,如果用moore型状态机,输出判定时不看输入,那么状态3元和状态0元就不能看作一个状态了。但是,习惯上对于这道题,还是习惯用3状态的mealy型状态机。

module zhuangtaiji(
input clk,
input rst_n,
input wire money,
output reg drink
    );
	 parameter INIT = 3'b001;
	 parameter STATE1 = 3'b010;
	 parameter STATE2 = 3'b100;
reg [2:0] state;
	always@(posedge clk or negedge rst_n) begin
	if (rst_n==0)
	state = INIT;
	else case(state) 
	INIT:if(money==1'b1)state = STATE1;
	     else  state = INIT;
	STATE1:if(money==1'b1)state = STATE2;
	     else  state = STATE1;
	STATE2:if(money==1'b1)state = INIT;
	     else  state = STATE2;	  
  default:state = INIT;
		  endcase
		  end
		  
	always@(posedge clk or negedge rst_n) begin
	if (rst_n==0)  drink=0;
	else if(state == STATE2&&money==1'b1)
	drink=1;
	else drink=0;
	end

endmodule

tb文件

`timescale 1ns / 1ps

module tb;

	// Inputs
	reg clk;
	reg rst_n;
	reg money;

	// Outputs
	wire drink;

	// Instantiate the Unit Under Test (UUT)
	zhuangtaiji uut (
		.clk(clk), 
		.rst_n(rst_n), 
		.money(money), 
		.drink(drink)
	);

	initial begin
		// Initialize Inputs
		clk = 1;
		rst_n <= 0;
		money <= 0;

		// Wait 100 ns for global reset to finish
		#100 rst_n<= 1;
        
		// Add stimulus here

	end
	always #10 clk=~clk;
	always #20 money={$random};
      
endmodule

上述状态机表示为两段式。若改为三段式,则一个always块中只实现状态的转移,设置一个now_state,next_state,该always块中只实现now_state<=next_state。第二个always块中实现next_state的判定,即什么条件下next_state是什么。第三个always块中实现输出。
第一段状态,时序电路的always模块,用同步电路描述状态跳转的过程,这样可利用触发器消除不稳定状态。

第二段状态,组合逻辑always模块,用以描述状态转移的判断条件,利用组合逻辑实现,达到立即判断的效果,在下一时钟边沿同步变化
第三段状态,时序电路always模块,用以描述每一段状态的结果输出,使用时序电路实现,消除不稳定状态的变化,达到同步跳转的效果
三段分别是:
状态计算逻辑
输出逻辑
状态寄存器

`timescale 1ns / 1ps

module zhuangtaiji(
input clk,
input rst_n,
input wire money,
output reg drink
    );
	 parameter INIT = 3'b001;
	 parameter STATE1 = 3'b010;
	 parameter STATE2 = 3'b100;
reg [2:0] now_state,next_state;
always@(posedge clk or negedge rst_n) 
	if (rst_n==0)  now_state<=INIT;
	else  now_state <= next_state;
	
	
	
	always@(*) begin
	if (rst_n==0)
	next_state = INIT;
	else case(now_state) 
	INIT:if(money==1'b1)next_state = STATE1;
	     else  next_state = INIT;
	STATE1:if(money==1'b1)next_state = STATE2;
	     else  next_state = STATE1;
	STATE2:if(money==1'b1)next_state = INIT;
	     else  next_state = STATE2;	  
  default:next_state = INIT;
		  endcase
		  end
		  
	always@(posedge clk or negedge rst_n) begin
	if (rst_n==0)  drink=0;
	else if(now_state == STATE2&&money==1'b1)
	drink=1;
	else drink=0;
	end

endmodule

上述三段式状态机,第三段描述输出时,使用的是时序逻辑电路,但是看下图貌似是组合逻辑电路。其实从时序逻辑的基本类型触发用组合逻辑即可,而且仿真也可以。
第三段改为:

always@(*) begin
	if (rst_n==0)  drink=0;
	else if(now_state == STATE2&&money==1'b1)
	drink=1;
	else drink=0;
	end

但是为什么还要用时序逻辑呢?原因是为了消除毛刺,由于传输延迟的不一致,now_state == STATE2&&money==1’b1这两个条件可能不同时到达,所以用时序逻辑,可以消除毛刺。
底下说的是最基本的,可以在此基础上更改。
具体毛刺消除可以看竞争冒险
2020.8.1补充
三段状态机的实现其实是基于 基于D触发器的时序逻辑电路的基本类型 的。
在这里插入图片描述
两段状态机其实就是组合逻辑电路1和D触发器合并写到一起的结果。
在这里插入图片描述

密码锁栗子:
在这里插入图片描述
为仿真方便,我把密码改为了321.
几个第一遍做题中中出现的问题:
1.状态转移图画的还不是很流畅,应该做法从state0开始,什么条件进入state1,什么条件返回state0.然后进入state1,什么条件进入state2,什么条件返回state0(state1)…
2.重复放出这张图
在这里插入图片描述
注意在时序逻辑中,是next_state赋值给current_state,毕竟在时序逻辑中是真实度过时间了嘛,所以上一时刻的次态就成为了现在时刻的现态。
3.组合逻辑的next_state确定时,还是忘了default。还有这个题中,在进入状态2后,不管你的输入是什么都将会进入状态0.然后你会发现,状态2可以和default合并。
还有一点确定的是,在case分支有多条语句时,可以不用begin-end.

module aaa(input clk,input rst_n,input [3:0] din,output reg out);

reg [1:0] next_state,current_state;

always@(posedge clk or negedge rst_n)
if(!rst_n) current_state <= 2'b00;
else current_state <= next_state;

always@(*) begin
if(~rst_n) next_state =2'b00;
else case (current_state)
2'b00:if(din == 2'd3) next_state =2'b01;
		else next_state <=2'b00;
2'b01:if(din == 2'd2) next_state =2'b10;
		else next_state =2'b00;
default: next_state =2'b00;
endcase
end

always@(*) begin
if(!rst_n) out = 0;
else if(current_state == 2'b10 && din == 4'd1 )
out = 1'b1;
else out = 1'b0;
end
endmodule

tb文件

`timescale 1ns / 1ps

module tb;
reg clk,rst_n;
reg [3:0] din;
wire out;

aaa aaa( clk, rst_n, din, out);

initial begin 
clk = 0;
din = 0;
forever 
#10 clk = ~clk;
end

initial begin 
rst_n = 1;
#70 rst_n = 0;
#50 rst_n = 1;
#1000 $finfish;
end


always @(posedge clk)
din = din - 1;

endmodule

人家的做题中的画电路图的步骤在这里插入图片描述

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值