赞
踩
本系列为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代码如下:
`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语法练习以及基本的激励编写和调试能力练习也有很大的帮助。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。