FPGA学习笔记 -- 状态机

同步指状态机与系统时钟同步,有限指状态是有限个状态

两类状态机共同点是状态跳转只和输入有关,若输出只和状态有关与输入无关为Moore型,

若输出与状态和输入均有关为Mealy型。

状态机的具体理论可以参考“信息论”中的部分内容 

状态机工作过程:0(条件)/0(结果)

可乐贩卖机系统

 输入:投入1元硬币

输出:出可乐,不出可乐

状态:投入0元,投入1元,投入2元,投入3元

条件为是否投入硬币,结果为是否出可乐,显然不利用three的状态是可以完成任务的

此为Mealy状态机(在最后吐可乐时与输入条件有关了)

当然我们也可以加上three这个状态绘图如下,但是意义不大,对板上资源有所消耗,不提倡

此为Moore状态机

参照其上第一个Mealy型绘制波形图:

 

module simple_fsm
(
	input wire sys_clk,
	input wire res_n,
	input wire po_money,
	
	output reg po_cola
);

// 一般0~4个状态采用二进制码,4~24个采用1位热码,24个以上采用格雷码编码。
parameter IDLE = 3'b001; //采用1位热码的形式表示状态,当然也可以采用二进制编码或者格雷码
parameter ONE = 3'b010; //读热码在综合时会简化比较器的位数,但是在存储上会占用3个位宽
parameter TWO = 3'b100; //FPGA中组合逻辑资源较多,综合布线的资源相对较少。在状态较多的情况下采用格雷码对资源的利用比较均衡


reg [2:0] state;

// 一段式状态机:使用时序逻辑即描述状态的转移也描述状态的输出
// 二段式状态机:第一段状态机描述状态的转移,第二段描述逻辑的输出
// 三段式状态机:第一段采用时序逻辑描述状态转移,第二段采用组合逻辑判断状态转移条件描述状态转移规律,第三段描述状态输出可以使用时序或组合逻辑

//新二段式:将原三段式中第一二段合并为一段,第三段作为一段
always@(posedge sys_clk or negedge res_n)
	if(res_n == 1'b0)
		state <= IDLE;
	else case(state)
	
		IDLE:if(po_money == 1'b1)
					state <= ONE;
				else
					state <= IDLE;
		ONE:if(po_money == 1'b1)
					state <= TWO;
				else
					state <= ONE;
		TWO:if(po_money == 1'b1)
					state <= IDLE;
				else
					state <= TWO;
					
		default:state <= IDLE;
	endcase 
				
always@(posedge sys_clk or negedge res_n)
	if(res_n == 1'b0)
		po_cola <= 1'b0;
	else if((state == TWO)&&(po_money == 1'b1))
		po_cola <= 1'b1;
	else
		po_cola <= 1'b0;
		
endmodule
`timescale 1ns/1ns

module tb_simple_fsm();

reg sys_clk;
reg res_n;
reg po_money;

wire po_cola;

initial
	begin 
		sys_clk = 1'b0;
		res_n <= 1'b0;
		#30
		res_n <= 1'b1;
	end

always #10 sys_clk = ~sys_clk;

always@(posedge sys_clk or negedge res_n)
	if(res_n == 1'b0)
		po_money <= 1'b0;
	else
		po_money <= {$random} % 2;

wire [2:0] state = siple_fsm_1.state; //将模块实例化后内部的变量调用出来方便进行内部的状态监测
		
initial
	begin
		$timeformat(-9,0,"ns",6);
		$monitor("@time %t: po_money = %b,state = %b,po_cola = %b",$time,po_money,state,po_cola);		
	end
	


simple_fsm siple_fsm_1
(
	.sys_clk(sys_clk),
	.res_n(res_n),
	.po_money(po_money),
	
	.po_cola(po_cola)
);	

endmodule

以上为简单的状态机组成

这里再次设计为当有零钱输入时可乐状态机的情况,即有不足一元硬币或需要找零的情况下可乐状态机的设计

 共有7种情况

设置编码为 输入00:没投钱;01:投入0.5;10:投入1;

输出 00:不出可乐;10:出可乐,不找零;11:出可乐,找零;

由于状态较多(人懒)假定2.5就出可乐,设计mealy(输出与输入和状态都有关系)状态机如下图所示:

 

 

在输入上采用组合逻辑电路,所以不会有时钟延时现象,但是在状态改变和输出为时序逻辑电路(与输入和电路原来的状态有关,结构上为组合逻辑电路+存储电路),所以电平会延时一个时钟周期

module complex_fsm
(
	input wire sys_clk,
	input wire sys_res,
	input wire pi_money_half,
	input wire pi_money_one,
	
	output reg po_cola,
	output reg po_money
);
	/* 可以更改为简单的写法
	parameter IDLE = 5'b00001;
	parameter HALF = 5'b00010;
	parameter ONE = 5'b00100;
	parameter ONE_HALF = 5'b01000;
	parameter TWO = 5'b10000;
	*/
	
	parameter IDLE = 5'b00001,
				 HALF = 5'b00010,
				 ONE = 5'b00100,
				 ONE_HALF = 5'b01000,
				 TWO = 5'b10000;
				 
	
	wire [1:0] pi_money;
	reg [5:0] state;
	
	assign pi_money = {pi_money_one,pi_money_half}; //观察波形可知pi_money为两个投币信号电平的拼接
	
	always@(posedge sys_clk or negedge sys_res)
		if(sys_res == 1'b0)
			state <= IDLE;
		else case(state)
			IDLE : if(pi_money == 2'b01)
						 state <= HALF;
					 else if(pi_money == 2'b10)
						 state <= ONE;
					 else 
						 state <= IDLE;
			HALF : if(pi_money == 2'b01)
						 state <= ONE;
					 else if(pi_money == 2'b10)
						 state <= ONE_HALF;
					 else 
						 state <= HALF;
			ONE : if(pi_money == 2'b01)
						 state <= ONE_HALF;
					 else if(pi_money == 2'b10)
						 state <= TWO;
					 else 
						 state <= ONE;
			ONE_HALF : if(pi_money == 2'b01)
						 state <= TWO;
					 else if(pi_money == 2'b10)
						 state <= IDLE;
					 else 
						 state <= ONE_HALF;
			TWO : if((pi_money == 2'b01)||(pi_money == 2'b10))
						 state <= IDLE;
					 else 
						 state <= TWO;
			default:state <= IDLE;
		endcase
			
	always@(posedge sys_clk or negedge sys_res)
		if(sys_res == 1'b0)
			po_cola <= 1'b0;
		else if((state == ONE_HALF && pi_money == 2'b10)
					||(state == TWO && pi_money == 2'b01)
					||(state == TWO && pi_money == 2'b10))
			po_cola <= 1'b1;
		else
			po_cola <= 1'b0;
	
	always@(posedge sys_clk or negedge sys_res)
		if(sys_res == 1'b0)
			po_money <= 1'b0;
		else if(state == TWO && pi_money == 2'b10)
			po_money <= 1'b1;
		else
			po_money <= 1'b0;

endmodule
`timescale 1ns/1ns

module tb_complex_fsm();

reg sys_clk;
reg sys_res;
reg pi_money_half;
reg pi_money_one;

reg random_data; // 随机数

wire po_cola;
wire po_money;

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

always #10 sys_clk = ~sys_clk;

always@(posedge sys_clk or negedge sys_res) //随机电平的产生
	if(sys_clk == 1'b0)
		random_data <= 1'b0;
	else 
		random_data <= {$random} % 2;
	
always@(posedge sys_clk or negedge sys_res)
	if(sys_clk == 1'b0) // 因为低电平时不检测,所以模拟同样保持
		pi_money_half <= 1'b0;
	else
		pi_money_half <= random_data;

always@(posedge sys_clk or negedge sys_res)
	if(sys_clk == 1'b0)
		pi_money_one <= 1'b0;
	else 
	pi_money_one <= ~random_data;  // 因为一次只能投一个币,所以one和half不可能同时为高电平

wire [1:0] pi_money = cfm1.pi_money;
wire [4:0] state = cfm1.state;  // 将模块内部的变量以此种形式引出,引出名为实例化后的名称,方便后边的监视函数进行具体监视
	
	
initial
	begin
		$timeformat(-9,0,"ns",6);  // 打印时间格式
		// 变量监视,记得后边赋值
		$monitor("@time %t:pi_money_half = %b,pi_money_one = %b,pi_money = %b,state = %b,po_cola = %b,po_money = %b",$time,pi_money_half,pi_money_one,pi_money,state,po_cola,po_money);
	end

complex_fsm cfm1
(
	.sys_clk(sys_clk),
	.sys_res(sys_res),
	.pi_money_half(pi_money_half),
	.pi_money_one(pi_money_one),
	
	.po_cola(po_cola),
	.po_money(po_money)
);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值