投币可乐-状态机

可乐售货机–状态机

状态机引入的原因:FPGA 是并行执行的,如果我们想要处理具有前后顺序的事件,这时就需要引入状态机。

  1. 背景:可乐定价为 2.5 元一瓶,可投入 0.5 元、1 元硬币,投币不够 2.5 元需要按复位键退回钱款,投币超过 2.5 元需找零。
  2. 状态转移图的三要素–输入、输出、状态
    三要素

FPGA为什么大多使用独热码?
独热码,每个编码只有 1 比特为 1,其余比特都为 0。对于几个状态则需要几个比特位。
为什么例子中我们使用的是独热码而非二进制码或格雷码呢?那就要从每种编码的特性上说起了,首先独热码因为每个状态只有 1bit 是不同的,因为只有 1 比特为 1,所以综合器会进行智能优化,这就相当于把之前 a比特的比较器变为了 1 比特的比较器,大大节省了组合逻辑资源,但是付出的代价就是状态变量的位宽需要的比较多,而我们 FPGA 中组合逻辑资源相对较少,所以比较宝贵,而寄存器资源较多,所以很完美。
而二进制编码恰恰相反,虽然使用了较少的寄存器,但比较器无法优化,使用的组合逻辑资源较多,而FPGA器件组合逻辑资源多,寄存器资源较少,所以用独热码更合适。
另外,用独热码编码的状态机可以在高速系统上运行,其原因是多比特的比较器每个比特到达比较器的时间可能会因为布局布线的走线长短而导致路径延时的不同,这样在高速系统下,就会导致采集到不稳定的状态,导致比较后的结果产生一个时钟的毛刺,使输出不稳定,而单比特的比较器就不用考虑这种问题。

用独热码编码虽然好处多多,但是如果状态数非常多的话即使是 FPGA 也吃不消独热码对寄存器的消耗,所以当状态数特别多的时候可以使用格雷码对状态进行编码。格雷码虽然也是和二进制编码一样使用的寄存器资源少,组合逻辑资源多,但是其相邻状态转换时只有一个状态发生翻转,这样不仅能消除状态转换时由多条信号线的传输延迟所造成的毛刺,又可以降低功耗,所以要优于二进制码的方式,相当于是独热码和二进制编码的折中。
在这里插入图片描述

  1. 状态转移图(Melay型)
    输入有多少种情况,每个状态的跳转就有多少种情况,这样根据输入来确定状态的跳转就能够保证我们不漏掉任何一种状态跳转。

在这里插入图片描述

有Moore 型状态机和 Mealy 型状态机,其共同点是:状态的跳转都只和输入有关。区别主要
是在最后输出的时候:若最后的输出只和当前状态有关而与输入无关则称为 Moore 型状态机;
若最后的输出不仅和当前状态有关还和输入有关则称为 Mealy 型状态机。

  1. 代码

module complex_fsm
(
	input	wire	sys_clk		,
	input	wire	sys_rst_n	,
	input	wire	one			,
	input	wire	half		,
	
	output	reg		out_money	,
	output	reg		cola_out
);

parameter	IDLE = 5'b00000;
parameter	HALF = 5'b00010;
parameter	ONE = 5'b00100;
parameter	ONE_HALF = 5'b01000;
parameter	TWO = 5'b10000;

reg		[4:0]	state;
wire	[1:0]	in_money;

assign	in_money = {one, half};

always@(posedge	sys_clk or negedge	sys_rst_n)
	if (sys_rst_n == 0 )
		state <= IDLE;
	else	case(state)
		IDLE: 	if (in_money == 2'b01)
					state <= HALF;
				else if (in_money == 2'b10)
					state <= ONE;
				else
					state <= IDLE;
		HALF:	if (in_money == 2'b01)
					state <= ONE;
				else if (in_money == 2'b10)
					state <= ONE_HALF;
				else
					state <= HALF;
		ONE: 	if (in_money == 2'b01)
					state <= ONE_HALF;
				else if (in_money == 2'b10)
					state <= TWO;
				else 
					state <= ONE;
		ONE_HALF: 	if (in_money == 2'b01)
					state <= TWO;
				else if (in_money == 2'b10)
					state <= IDLE;
				else
					state <= ONE_HALF;
		TWO	: 	if (in_money == 2'b01 || in_money == 2'b10)
					state <= IDLE;
				else 
					state <= TWO;
		default:	state <= IDLE;
	endcase
	
always@(posedge	sys_clk or negedge	sys_rst_n)
	if (sys_rst_n == 0 )
		cola_out <= 1'b0;
	else	if ((state == TWO && (in_money == 2'b01 || in_money == 2'b10) )
				|| (state == ONE_HALF && in_money == 2'b10))
		cola_out <= 1'b1;
	else
		cola_out <= 1'b0;
always@(posedge	sys_clk or negedge	sys_rst_n)
	if (sys_rst_n == 0 )
		out_money <= 1'b0;
	else	if (state == TWO &&  in_money == 2'b10 )
		out_money <= 1'b1;
	else		
		out_money <= 1'b0;
		
		
endmodule
  1. 仿真代码与结果
`timescale 1ns / 1ns

// Create Date: 2022/07/16 17:36:16
module tb_complex_fsm();

reg		sys_clk		;
reg		sys_rst_n	;
reg		one			;	
reg		half		;	
wire	out_money	;	
wire	cola_out	;	
	
	

initial 
	begin
		sys_clk = 1'b1;
		sys_rst_n <= 1'b0;
		#20
		sys_rst_n <= 1'b1;
	end

//sys_clk:模拟系统时钟,每 10ns 电平翻转一次,周期为 20ns,频率为 50MHz
always #10 sys_clk = ~sys_clk;

//pi_money:产生输入随机数,模拟投币 1 元的情况
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
	{one, half} <= 2'b0;
	else
	{one, half} <= {$random} % 3; //取模求余数,产生非负随机数 0、1、2
//------------------------complex_fsm_inst------------------------
complex_fsm complex_fsm_inst(
	.sys_clk (sys_clk ), //input sys_clk
	.sys_rst_n (sys_rst_n ), //input sys_rst_n
	.one 		(one ), 
	.half 		(half),
	.out_money	(out_money),
	.cola_out (cola_out ) //output po_cola
);


endmodule

结果:
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值