1. 实验目标
可乐机每次只能投入 1 枚 1 元硬币,且每瓶可乐卖 3 元钱,即投入 3 个硬币就可以让
可乐机出可乐,如果投币不够 3 元想放弃投币需要按复位键,否则之前投入的钱不能退
回。
2. 状态转移图
3. 波形图绘制
4. RTL
module simple_fsm
(
input wire sys_clk , //系统时钟 50Mhz
input wire sys_rst_n , //全局复位
input wire pi_money , //投币方式可以为:不投币(0)、投 1 元(1)
output reg po_cola //po_cola 为 1 时出可乐,po_cola 为 0 时不出可乐
);
//parameter define
//只有三种状态,使用独热码
//告诉运行的都是独热码
parameter IDLE = 3'b001;
parameter ONE = 3'b010;
parameter TWO = 3'b100;
// 状态机的状态
reg [2:0] state ;
//第一段状态机,描述当前状态 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 == 1'b1)
state <= ONE;
else
state <= IDLE;
ONE : if(pi_money == 1'b1)
state <= TWO;
else
state <= ONE;
TWO : if(pi_money == 1'b1)
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 == 1'b1))
begin
po_cola <= 1'b1;
end
else
begin
po_cola <= 1'b0;
end
end
endmodule
5. testbench
`timescale 1ns/1ns
module tb_simple_fsm();
//因为 testbench 不对外进行信号的输入输出,只是自己产生
//激励信号提供给内部实例化待测 RTL 模块使用,所以端口列表
//中没有内容,只是列出“()”,当然可以将“()”省略,括号
//后有个“;”不要忘记
//要在 initial 块和 always 块中被赋值的变量一定要是 reg 型
//在 testbench 中待测试 RTL 模块的输入永远是 reg 型变量
//输出信号,我们直接观察,也不用在任何地方进行赋值
//所以是 wire 型变量(在 testbench 中待测试 RTL 模块的输出永远是 wire 型变量)
reg sys_clk;
reg sys_rst_n;
reg pi_money ;
//输出信号,我们直接观察,也不用在任何地方进行赋值
//所以是 wire 型变量(在 testbench 中待测试 RTL 模块的输出永远是 wire 型变量)
wire po_cola;
//初始化值在没有特殊要求的情况下给 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)
pi_money <= 1'b0;
else
pi_money <= {$random} % 2; //取模求余数,产生非负随机数 0、1
//将 RTL 模块中的内部信号引入到 Testbench 模块中进行观察
wire [2:0] state = simple_fsm_inst.state;
initial
begin
$timeformat(-9, 0, "ns", 6);
$monitor("@time %t: pi_money=%b state=%b po_cola=%b",$time, pi_money, state, po_cola);
end
simple_fsm simple_fsm_inst
(
//前面的“in1”表示被实例化模块中的信号,后面的“in1”表示实例化该模块并要和这个
//模块的该信号相连接的信号(可以取名不同,一般取名相同,方便连接和观察)
//“.”可以理解为将这两个信号连接在一起
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.pi_money(pi_money),
.po_cola (po_cola)
);
endmodule