14_复杂状态机

1. 实验目标

可乐定价为 2.5 元一瓶,可投入 0.5 元、1 元硬币,投币不够 2.5 元需要按复位键退回钱款,投币超过 2.5 元需找零。

2. 状态转移图

在这里插入图片描述
在这里插入图片描述

3. 波形图绘制

在这里插入图片描述

4. RTL

module complex_fsm
(
	input wire sys_clk , //系统时钟 50Mhz
	input wire sys_rst_n , //全局复位
	input wire pi_money_one , //投币 1 元
	input wire pi_money_half , //投币 0.5 元
	
	output reg po_money , //po_money 为 1 时表示找零
						  //po_money 为 0 时表示不找零
	
	output reg po_cola //po_cola 为 1 时出可乐,
					   //po_cola 为 0 时不出可乐
);

//parameter define
//只有5种状态,使用独热码
//告诉运行的都是独热码
// 2.5元价格
parameter IDLE = 5'b00001;
parameter HALF = 5'b00010;
parameter ONE  = 5'b00100;
parameter ONE_HALF = 5'b01000;
parameter TWO = 5'b10000;

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

//pi_money:为了减少变量的个数,我们用位拼接把输入的两个 1bit 信号拼接成 1 个 2bit 信号
//投币方式可以为:
//不投币(00)、投 0.5 元(01)、投 1 元(10),每次只投一个币
assign pi_money = {
    
    pi_money_one,pi_money_half};


//第一段状态机,描述当前状态 state 如何根据输入跳转到下一状态
//对状态的描述
always@(posedge sys_clk or negedge sys_rst_n)
	begin
		if(sys_rst_n == 1'b0)
			begin
				state <= IDLE;
			end
		else
			begin
				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					
				
			end
	end

//第二段状态机,描述当前状态 state 和输入 pi_money 如何影响 po_cola 输出
always@(posedge sys_clk or negedge sys_rst_n)
	begin
		if(sys_rst_n == 1'b0)
			begin
				po_cola <= 1'b0;
			end
		else if((state == TWO && pi_money == 2'b01) || 
				(state == TWO && pi_money == 2'b10) || 
				(state == ONE_HALF && pi_money == 2'b10))
			begin
				po_cola <= 1'b1;
			end
		else
			begin
				po_cola <= 1'b0;
			end
	end

//描述当前状态 state 和输入 pi_money 如何影响 po_money 输出
always@(posedge sys_clk or negedge sys_rst_n)
	begin
		if(sys_rst_n == 1'b0)
			begin
				po_money <= 1'b0;
			end
		else if((state == TWO) && (pi_money == 2'b10))
			begin
				po_money <= 1'b1;
			end
		else
			begin
				po_money <= 1'b0;
			end
	end

endmodule 

5. testbench

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


//因为 testbench 不对外进行信号的输入输出,只是自己产生
//激励信号提供给内部实例化待测 RTL 模块使用,所以端口列表
//中没有内容,只是列出“()”,当然可以将“()”省略,括号
//后有个“;”不要忘记
//要在 initial 块和 always 块中被赋值的变量一定要是 reg 型
//在 testbench 中待测试 RTL 模块的输入永远是 reg 型变量



 //输出信号,我们直接观察,也不用在任何地方进行赋值
 //所以是 wire 型变量(在 testbench 中待测试 RTL 模块的输出永远是 wire 型变量)

reg sys_clk;
reg sys_rst_n;
reg pi_money_one;
reg pi_money_half;
reg random_data_gen;



 //输出信号,我们直接观察,也不用在任何地方进行赋值
 //所以是 wire 型变量(在 testbench 中待测试 RTL 模块的输出永远是 wire 型变量)
wire po_cola;
wire po_money;

//初始化值在没有特殊要求的情况下给 0 或 1 都可以。如果不赋初值,仿真时信号
//会显示为不定态(ModelSim 中的波形显示红色)

initial
//initial 只在通电执行一次
//在仿真中 begin...end 块中的内容都是顺序执行的,
//在没有延时的情况下几乎没有差别,看上去是同时执行的,
//如果有延时才能表达的比较明了;
//而在 rtl 代码中 begin...end 相当于括号的作用, begin...end 在 Testbench 中的用法及意义(区别   -----------------------------------------------------)
//在同一个 always 块中给多个变量赋值的时候要加上
	begin 
		sys_clk = 1'b1; //时钟信号的初始化为 1,且使用“=”赋值,
                        //其他信号的赋值都是用“<=”
		sys_rst_n <= 1'b0; //因为低电平复位,所以复位信号的初始化为 0
		

		#20 //延时20ns
		sys_rst_n <= 1'b1; //初始化 20ns 后,复位释放,因为是低电平复位	
		
		//都是顺序执行的
	end
// always语句 一直在执行
sys_clk:模拟系统时钟,每 10ns 电平翻转一次,周期为 20ns,频率为 50Mhz
always #10 sys_clk = ~sys_clk;//取模求余数,产生随机数 1'b0、1'b1//每隔 10ns 产生一次随机数

//pi_money:产生输入随机数,模拟投币 1 元的情况
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		random_data_gen <= 1'b0;
	else
		random_data_gen <= {
    
    $random} % 2; //取模求余数,产生非负随机数 0、1

// 0.5 和 1 不能同时出现
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		pi_money_one <= 1'b0;
	else
		pi_money_one <= random_data_gen; //取模求余数,产生非负随机数 0、1


always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		pi_money_half <= 1'b0;
	else
		pi_money_half <= ~random_data_gen; //取模求余数,产生非负随机数 0、1

		
//将 RTL 模块中的内部信号引入到 Testbench 模块中进行观察
wire [4:0] state = complex_fsm_inst.state;
wire [1:0] pi_money = complex_fsm_inst.pi_money;

initial 
	begin
		$timeformat(-9, 0, "ns", 6);
		$monitor("@time %t: pi_money_one=%b pi_money_half=%b pi_money=%b state=%b po_cola=%b po_money=%b", $time, pi_money_one,pi_money_half, pi_money, state, po_cola, po_money);	
	end
	
complex_fsm complex_fsm_inst
(
	//前面的“in1”表示被实例化模块中的信号,后面的“in1”表示实例化该模块并要和这个
	//模块的该信号相连接的信号(可以取名不同,一般取名相同,方便连接和观察)
	//“.”可以理解为将这两个信号连接在一起
	.sys_clk(sys_clk),
	.sys_rst_n(sys_rst_n), 
	.pi_money_one(pi_money_one ), 
	.pi_money_half(pi_money_half ),
	
	.po_cola (po_cola),
	.po_money (po_money ) 
);

endmodule						 

猜你喜欢

转载自blog.csdn.net/HeElLose/article/details/131285816