当前位置:   article > 正文

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

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”代表购买者取走饮料
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

状态机

规定三种状态: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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114

同时编写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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

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

测试波形

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

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

改进之处

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

总结

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/641301
推荐阅读
相关标签
  

闽ICP备14008679号