【Verilog实战】地铁自助售票机


完整程序:点击下载
脚  本:makefile
工  具:vcs 和 verdi


往 期:



一、Demand

  设计一个地铁自助售票控制器,投币只能为1元、5元和10元;票价额为2—10元整数;具有找零功能。


二、Interface Description

Signal Name Width Direction Description
clk 1 input System clk signal
reset 1 input System reset signal
sel_en 1 input 触发选票程信号
fares 4 input 票程总价
money1 1 input 投1块钱的脉冲信号
money5 1 input 投5块钱的脉冲信号
money10 1 input 投10块钱的脉冲信号
total 4 output 已投入的总钱币数
ticket_out 1 output 出票的脉冲信号
mout 4 output 找钱的信号

三、FSM

在这里插入图片描述

地铁自助售票机非常适合使用状态机实现,这个模块采用一个状态机,分别为空闲状态(IDLE),选票程状态(SEL),投币状态(VOTE),出票状态(TICKET)和找零阶段(CHANGE)。其中在SEL状态,条件reset1是用户选错票程了,返回空闲状态。VOTE阶段,条件reset1是用户不想买票了,进行退钱,然后回到空闲状态。


四、Design and Functional Verification

(1)RTL

module subway
(
  input            clk_i     ,
  input            reset     ,
  input            sel_en    ,
  input     [3:0]  fares     ,
  input            money1    ,
  input            money5    ,
  input            money10   ,

  output reg [3:0] total     ,
  output reg       ticket_out,
  output reg [3:0] mout
);

parameter IDLE   = 5'b00001;
parameter SEL    = 5'b00010;
parameter VOTE   = 5'b00100;
parameter TICKTE = 5'b01000;
parameter CHANGE = 5'b10000;

reg [4:0] cur_state;
reg [4:0] nxt_state;

reg [3:0] num1 ;
reg [1:0] num5 ;
reg       num10;

reg [3:0] fares_buf;

always @ (reset or money1 or money5 or money10 or cur_state or nxt_state) begin
  if (reset) begin
    num1  = 5'b0;
    num5  = 4'b0;
    num10 = 1'b0;
  end
  else if (money1) begin
    num1  = num1 + 1'b1;
  end
  else if(money5)begin
    num5  = num5 + 1'b1;
  end
  else if(money10)begin
    num10 = num10 + 1'b1;
  end
  else if(cur_state == IDLE && nxt_state == IDLE)begin
    num1 = 5'b0;
    num5 = 4'b0;
    num10 = 1'b0;
  end

  else begin
    num1 = num1;
    num5 = num5;
    num10 = num10;
  end
end

//assign total = num1 + num5*5 + num10*10;

always @ (posedge clk_i) begin
  if (reset) begin
    cur_state <= IDLE;
  end
  else begin
    cur_state <= nxt_state;
  end
end

always @ (posedge clk_i) begin
  if(reset)begin
    nxt_state <= IDLE;
  end
  case(cur_state)
    IDLE :if(sel_en && !reset)begin
            nxt_state <= SEL;
          end

    SEL  :begin
            nxt_state <= VOTE;
          end

    VOTE :if((!reset) && (total < fares_buf) )begin
            nxt_state <= VOTE;
          end
          else if((!reset) && (total >= fares_buf))begin
            nxt_state <= TICKTE;
          end

    TICKTE:nxt_state <= CHANGE;

    CHANGE:nxt_state <= IDLE;
    
    //default:nxt_state = IDLE;
  endcase
end


always @ (posedge clk_i) begin
  if(reset)begin
    fares_buf <= 4'b0;
    total <= 4'b0;
    ticket_out <= 1'b0;
    mout <= 3'b0;
  end  
  case(nxt_state)
    IDLE :begin
            fares_buf <= 4'b0;
            total <= 4'b0;
            ticket_out <= 1'b0;
            mout <= 3'b0;
          end

    SEL  :begin
            fares_buf <= fares;
          end

    VOTE :begin
            total <= num1 + num5*5 + num10*10;
          end

    TICKTE:begin
            ticket_out <= 1'b1;
           end

    CHANGE:begin
            mout <= total-fares_buf;
            ticket_out <= 1'b0;
           end
           
    default:begin
              total <= 4'b0;
              ticket_out <= 1'b0;
              mout <= 3'b0;
            end
  endcase
end

(2)Test Bench

module tb_subway;
  reg        clk_i;
  reg        reset;
  reg        sel_en;
  reg [3:0]  fares;
  reg        money1;
  reg        money5;
  reg        money10;

  wire [3:0] total;
  wire       ticket_out;
  wire       mout;

initial begin
  clk_i   = 0;
  reset   = 1;
  sel_en  = 0;
  fares   = 4'b0;
  money1  = 1'b0;
  money5  = 1'b0;
  money10 = 1'b0;
  #15 reset  = 0;


  f2m1m1;

  #130 f2m5;
  #130 f2m10;

  #130 f5m1m1m1m1m1;
  #130 f5m5;
  #130 f5m10;

  #130 f10m5m5;
  #130 f10m10;

end

task f2m1m1;
  fares = 4'd2;
  #30;
  sel_en = 1;
  #15 sel_en = 0;
  #15 money1 = 1;
  #10 money1 = 0;
  #10 money1 = 1;
  #10 money1 = 0;
endtask

task f2m5;
  fares = 4'd2;
  #30;
  sel_en = 1;
  #15 sel_en = 0;
  #15 money5 = 1;
  #10 money5 = 0;
endtask

task f2m10;
  fares = 4'd2;
  #30;
  sel_en = 1;
  #15 sel_en = 0;
  #15 money10 = 1;
  #10 money10 = 0;
endtask

task f5m1m1m1m1m1;
  fares = 4'd5;
  #30;
  sel_en = 1;
  #15 sel_en = 0;

  #15 money1 = 1;
  #10 money1 = 0;

  #10 money1 = 1;
  #10 money1 = 0;

  #10 money1 = 1;
  #10 money1 = 0;

  #10 money1 = 1;
  #10 money1 = 0;

  #10 money1 = 1;
  #10 money1 = 0;
endtask

task f5m5;
  fares = 4'd5;
  #30;
  sel_en = 1;
  #15 sel_en = 0;
  #15 money5 = 1;
  #10 money5 = 0;
endtask

task f5m10;
  fares = 4'd5;
  #30;
  sel_en = 1;
  #15 sel_en = 0;
  #15 money10 = 1;
  #10 money10 = 0;
endtask

task f10m5m5;
  fares = 4'd10;
  #30;
  sel_en = 1;
  #15 sel_en = 0;
  #15 money5 = 1;
  #10 money5 = 0;
  #10 money5 = 1;
  #10 money5 = 0;

endtask

task f10m10;
  fares = 4'd10;
  #30;
  sel_en = 1;
  #15 sel_en = 0;
  #15 money10 = 1;
  #10 money10 = 0;
endtask


initial begin
   #2000 $finish;
end
always #10 clk_i = ~clk_i;

subway tb_subway(
                 .clk_i(clk_i),
                 .reset(reset),
                 .sel_en(sel_en),
                 .fares(fares),
                 .money1(money1),
                 .money5(money5),
                 .money10(money10),
                 .total(total),
                 .ticket_out(ticket_out),
                 .mout(mout)
                );
initial begin  
  $fsdbDumpfile("subway.fsdb");
  $fsdbDumpvars            ;
  $fsdbDumpMDA             ;
end
endmodule

五、Result

在这里插入图片描述
结果:测试用例分别测试了

票价2元,投两次1元,找了0元;票价2元,投一次5元,找了3元;票价2元,投1次10元,找了8元
票价5元,投5次1元,找了0元;票价5元,投一次5元,找了0元;票价5元,投一次10元,找了5元
票价10元,投两次5元,找了0元;票价10元,投一次10元,找了0元

结论:功能正常,但这个设计中,找钱功能是一次输出,这是不合要求的,起码是能找某几种货币,比如找钱3元,总不能找一张3元的,可以是找三个1元。还有就是没有考虑到用户可能投出1、5、10块面值的货币外的一个错误处理。


作者:xlinxdu
版权:本文是作者原创,版权归作者所有。
转载:未经作者允许,禁止转载,转载必须保留此段声明,必须在文章中给出原文连接。

猜你喜欢

转载自blog.csdn.net/qq_43244515/article/details/125039757