Verilog设计实例(一):自动售货机设计实例

前言

本系列为FPGA设计实例,基于Verilog HDL,题目一般是我在网上看到的一些FPGA相关的实验题目,基本会是一个实际场景的系统实现,而不是简单单元的设计,这是为了能更全面的练习,这些实例一般是可以基于FPGA进行实现的,因为正好手里有一块zynq板子,所以想把这个东西用起来,之前做一个卷积核,但是把ip集成到zynq上和arm核协同验证时一直不成功,所以希望也可以学习一下zynq的软硬件协同使用。

以上是本系列的目的,OK,废话不多说,让我们直接开始第一个开发实例:自动售货机系统的设计。来源:哈工大MOOC。

用状态机设计一个自动售货机

它的投币口每次只能投入一枚五角或一元的硬币。投入一元五角钱硬币后机器自动给出一杯饮料,投入2元钱(2枚一元硬币)后,在给出硬币的同时找零一枚五角的硬币,投币时仅支持一枚一枚投。

信号定义

clk				# 时钟信号输入
rst				# 系统复位信号
half_yuan		# 投入五角硬币信号,信号为“1”代表投入五角
one_yuan		# 投入一元硬币信号,信号为“1”代表投入一元
half_out		# 找零信号,"1"代表找零五角
dispense		# 售出信号,“1“代表售出一杯饮料
collect			# 取走饮料信号,“1”代表购买者取走饮料

状态机

规定三种状态:IDLE——未投币,half——投币五角,one——投币一元。

状态转移图:
状态转移信号为——one_yuan half_yuan/dispense half_out,示例:01/10
自动售货机状态转移图

自动售货机的Verilog描述:

在这里插入图片描述
售货机模块的Verilog代码如下:

`timescale 1ns / 1ps

module vending_machine(
                        clk,
                        one_yuan,
                        half_yuan,
                        collect,
                        half_out,
                        dispense,
                        rst
    );

//定义端口
parameter idle=2'b00,half=2'b01,one=2'b10;
input one_yuan,half_yuan,rst,clk;
output reg collect,half_out,dispense;
reg[1:0] ST; //当前状态

//控制逻辑实现
always @(posedge clk)   begin
if(rst)
    begin 
        dispense <= 0;
        collect <= 0;
        half_out <= 0;
        ST <= idle;
    end
else
    case(ST)
    
        idle:
        if(half_yuan)
        begin
        dispense <= 0;
        collect <= 0;
        half_out <= 0;
        ST <= half;
        end
        else if(one_yuan)
        begin
        dispense <= 0;
        collect <= 0;
        half_out <= 0;
        ST <= one;
        end
        else
        begin
        dispense <= 0;
        collect <= 0;
        half_out <= 0;
        ST <= idle;
        end
            
            
        half:
        if(half_yuan)
        begin
        dispense <= 0;
        collect <= 0;
        half_out <= 0;
        ST <= one;
        end
        else if(one_yuan)
        begin
        dispense <= 1;
        collect <= 1;
        half_out <= 0;
        ST <= idle;        
        end
        else
        begin
        dispense <= 0;
        collect <= 0;
        half_out <= 0;
        ST <= half;
        end
        
        
        one:
        if(half_yuan)
        begin
        dispense <= 1;
        collect <= 1;
        half_out <= 0;
        ST <= idle;
        end
        else if(one_yuan)
        begin
        dispense <= 1;
        collect <= 1;
        half_out <= 1;
        ST <= idle;
        end
        else
        begin
        dispense <= 0;
        collect <= 0;
        half_out <= 0;
        ST <= one;
        end
        
        
        default:
        begin
        dispense <= 0;
        collect <= 0;
        half_out <= 0;
        ST <= idle;
        end
        
    endcase
end
//
endmodule

同时编写testbench激励:

`timescale 1ns / 1ps

module VM_tb();
    reg clk;
    reg rst;
    reg half_yuan;
    reg one_yuan;
    wire collect;
    wire half_out;
    wire dispense;
    
    vending_machine vm1(
                        .one_yuan(one_yuan),
                        .half_yuan(half_yuan),
                        .collect(collect),
                        .half_out(half_out),
                        .dispense(dispense),
                        .clk(clk),
                        .rst(rst)
    );
    
    always #10 clk = ~clk;
    
    always@(posedge clk)
    begin
    one_yuan = {$random}%2;
    half_yuan = ~one_yuan;//连续投币,每次投入一枚硬币
    end
    
    
    
    initial begin
    clk = 0;
    rst = 0;
    one_yuan = 0;
    half_yuan = 0;
    #20
    rst = 1;
    #40
    rst = 0;
    #1000
    $stop;
    end
    
endmodule

testbench激励中假设我们是连续投币,每次只能投一枚一元或五角硬币,硬币足够时就给出饮料,需要找零时即找零。

测试波形

在Vivado中跑仿真,波形截图如下:
在这里插入图片描述

从图中可以看到,黄色线前的时钟周期,先投了一元,然后黄色线处又接着投了一元,下一时钟周期给出饮料并且找零五角;给饮料的时钟周期也投了一元,下一周期再投了一元,然后再下一周期给出饮料并且找零,符合我们对售货机的行为描述。

改进之处

对于售货机来说,如果检测到投币后隔了较久才投下一个币,当前代码检测后one_yuan/half_yuan信号不归零,信号在下一个时钟沿可能会重复捕获,所以需要设置一次投币信号检测,检测完成后复原,避免投币信号未发生变化时被多次捕获。
当然也有别的方法来避免重复捕获,比如及时复原投币信号。
还可以为售货机添加其他功能,比如选择货物,补货提醒等更加实用的功能,模拟更多的需求场景。

总结

本实例是一个经典的开发实例,作为练手,可以用来熟悉状态机的设计与实现,对基本hdl语法练习以及基本的激励编写和调试能力练习也有很大的帮助。

  • 17
    点赞
  • 115
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
好的,我可以为你提供一个自动售货机Verilog设计。 首先,我们需要定义输入和输出端口。对于这个自动售货机,我们需要一些输入端口,如选择的商品、投入的硬币等,以及一些输出端口,如商品价格、找零等。以下是输入和输出端口的声明: ``` module vending_machine( input clk, // 时钟信号 input [1:0] selection, // 商品选择信号 input [3:0] coin, // 投入的硬币信号 output reg [3:0] price, // 商品价格信号 output reg [3:0] change, // 找零信号 output reg dispense // 商品出售信号 ); ``` 接下来,我们需要定义一些内部的信号和变量。我们需要一些变量来存储商品价格、投入的硬币总数、是否需要找零等信息。以下是用于存储商品价格、投入的硬币总数、是否需要找零等信息的变量的声明: ``` reg [3:0] price_list[2:0] = {4'd5, 4'd10, 4'd15}; // 商品价格列表 reg [3:0] coin_total; // 投入的硬币总数 reg need_change; // 是否需要找零 reg [3:0] change_total; // 找零总数 ``` 接下来,我们需要实现自动售货机的逻辑。我们需要使用if语句来判断所需的操作,然后根据输入执行相应的操作。以下是自动售货机逻辑的实现: ``` always @(posedge clk) // 时序逻辑 begin if (selection == 2'b00) // 如果选择第一个商品 price <= price_list[0]; // 设置商品价格为第一个商品的价格 else if (selection == 2'b01) // 如果选择第二个商品 price <= price_list[1]; // 设置商品价格为第二个商品的价格 else if (selection == 2'b10) // 如果选择第三个商品 price <= price_list[2]; // 设置商品价格为第三个商品的价格 else price <= 4'b0000; // 如果没有选择商品,则价格为0 if (coin != 4'b0000) // 如果有硬币投入 coin_total <= coin_total + coin; // 增加硬币总数 if (coin_total >= price) // 如果硬币总数大于等于商品价格 begin need_change <= 1'b1; // 设置需要找零 change_total <= coin_total - price; // 计算找零总数 end else need_change <= 1'b0; // 不需要找零 if (need_change) // 如果需要找零 begin change <= change_total; // 输出找零总数 dispense <= 1'b1; // 出售商品 coin_total <= 4'b0000; // 重置硬币总数 end else begin change <= 4'b0000; // 不需要找零,找零总数为0 dispense <= 1'b0; // 不出售商品 end end ``` 最后,我们需要结束模块并保存代码: ``` endmodule ``` 这是一个简单的自动售货机Verilog设计。你可以将这个代码保存到一个文件中,然后使用Verilog编译器进行编译和仿真。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值