fpga[2]状态机(附源码)

前言

状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作,完成特定操作的控制中心。
状态机分Moore(摩尔)状态机和Mealy(米利)型状态机。若输出只和状态有关而与输入无关,则称为Moore状态机。若输出不仅和状态有关而且和输入有关系,则称为Mealy状态机。
在这里插入图片描述

1.知识准备

1⃣️为什么要使用状态机?
先看一个例子:
“快递员正在送件,假定他在接下来的一个小时内需要按照已经定好的顺序给五个人送件,而这五个人分别住在不同的地方并且各自的快递种类和件数都不同。”
这个例子,因为包含较多的判断条件和不同输出条件,因而比较难用一般的语句进行较为直观的描述。此时,如果我们使用状态机来对上面的例子进行描述,那么代码将会更容易编写更加直观易读。

2⃣️如何描述一个状态机?
状态机具有4要素,即现态、条件、动作、次态。详解如下:①现态:是指当前所处的状态。
②条件:当该条件被满足时,将会触发一个动作或进行一次状态迁移。③动作:当条件满足时所执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。(动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。)④次态:条件满足后要迁往的新状态。次态是相对于现态而言的,次态一旦被激活,则会成为新的现态。
当我们设计状态机时,要依据四要素进行代码编写。

3⃣️状态数较多时应该如何处理?
状态机需要根据控制信号按照预先设定的状态进行状态转移,这就出现了如何对状态进行
有效编码的问题。其中最简单的就是直接使用二进制编码进行表示,除此之外还有格雷码、独热码。假设有八个状态从 A 到 R,利用不同的编码格式分别如表所示。
在这里插入图片描述
不同的编码方式具有不同优势,在本次设计中我们选择独热码。目前我们只需要知道,独热码使用更多的寄存器但是其组合逻辑相对简单,FPGA 中提供较多的时序逻辑较多所以使用独热码。

2.功能设计

**项目目标:**设计一个简易的可乐机,用户可以投币购买,当投入一元硬币数量达到3时,可乐机出可乐。投入一元硬币数量未达到3时,可乐机不输出。
根据我们的需求,画出波形图如下:
在这里插入图片描述
状态转移图:
在这里插入图片描述
根据我们画的图,编写代码如下:

module fsm
(
	input			sys_clk	 	,	
	input			sys_rst_n	,
	input			pi_money	,		//投币信号

	output		reg	po_cola				//输出可乐信号
);
//使用独热码
parameter	IDLE=3'b001;	
parameter	ONE =3'b010;
parameter	TWO =3'b100;

reg [2:0]state;		//使用了独热码,有三个状态则用三位宽的变量

/*状态跳转说明部分*/
always@(posedge sys_clk or negedge sys_rst_n)begin	
	if(!sys_rst_n)		//复位时回到IDLE状态
		state <= IDLE;
	else if(pi_money)begin		//投币信号到来时,根据此时的状态决定下一个状态
		case(state)
			IDLE :	state <= ONE;
			ONE  :	state <= TWO;
			TWO  :	state <= IDLE;
		default  :	state <= IDLE;
		endcase
		end
	else 
		state <= state;			//无复位或者投币信号时保持原有状态
	end
	
/*状态输出说明部分*/
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)				
		po_cola <= 1'b0;
	else if((state == TWO) && (pi_money)) //处于TWO状态且再次收到投币时输出可乐
		po_cola <= 1'b1;
	else
		po_cola <= 1'b0;
end
endmodule

Quartus II中进行一次全编译后,查看RTL视图:
在这里插入图片描述
查看状态转移图:
在这里插入图片描述

3.仿真验证

为了验证我们的设计,需要编写testbench文件进行仿真验证
我们编写testbench文件,是为了产生满足条件的激励信号,同时对模块的输出进行捕捉,测试输出是否满足要求。
编写代码如下:

`timescale 1ns/1ns
module tb_fsm();

reg sys_clk;
reg sys_rst_n;
reg pi_money;

wire po_cola;

initial	begin
	sys_clk = 1'b0; sys_rst_n = 1'b0; pi_money = 1'b0;
	#40
	sys_rst_n = 1'b1;
end

always#10 sys_clk <= ~sys_clk;			//时钟周期为20ns
always#20 pi_money <= {$random} % 2;	//每隔20个时间单位生成一次随机的投币信号

wire [2:0]	state = fsm_inst.state; //将state变量引出,便于波形仿真时观察


fsm fsm_inst		//连接端口
(
	.sys_clk	 	(sys_clk),
	.sys_rst_n		(sys_rst_n),
	.pi_money		(pi_money),

	.po_cola		(po_cola)
);

endmodule

我们需要预先将state设置为二进制,得到的仿真波形如下:
在这里插入图片描述
对局部进行放大,可以看出:
复位信号为高电平时,状态机开始工作。在时钟的上升沿到来时,如果投币信号(pi_money)为高电平,则发生状态转移。状态转移顺序为3’b001➡️3’b010➡️3’b100,即分别对应着IDLE、ONE、TWO三个状态的独热编码。
当状态机为TWO状态,此时如果有投币信号,则状态机跳转到IDLE状态且输出可乐(po_cola)。
在这里插入图片描述
从这里看出来我们的设计是正确的。

4.总结

本次我们设计了简单的状态机,实现了可乐机的功能。通过这个项目我们初步掌握了状态机代码的书写方式,为以后我们深入理解状态机打下基础。状态机的书写方式比较灵活,可以分为一段式、二段式 和三段式,具体三者的差距我们后续再进行学习。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值