一. 实验目的

  1. 掌握有限状态机的设计方法。;

  2. 能够使用 SystemVerilog 进行三段式状态机的建模。

二. 实验环境

  1. 操作系统:Windows 10 或 Ubuntu 16.04

  2. 开发环境:Xilinx Vivado 2018.2

  3. 硬件平台:远程 FPGA 云平台

三. 实验原理

有限状态机分为 Moore 型状态机和 Mealy 型状态机。前者,状态机的输出仅由当前状态决定,如图 4-1 所示,在状态转换图的绘制中,输出信息标在状态(圆圈)中。

image-20240520144102043.png

采用硬件描述语言进行状态机建模时,建议使用 3 段式。第一段描述状态的转换(即对状态机中的寄存器进行建模),采用时序逻辑实现;第二段描述状态转换条件和规律(即对状态机中的次态逻辑进行建模),采用组合逻辑实现;第三段描述输出逻辑,根据实际设计需要可采用组合逻辑或时序逻辑实现。三段式状态机建模的模板如下所示。

//第一段,同步时序 always 模块,描述状态的转换
always_ff (posedge clk) begin //同步复位
if(!rst_n)
current_state <= S0;
else
current_state <= next_state; //注意,使用的是非阻塞赋值
end
//第二段,组合逻辑 always 模块,描述状态转移条件判断
always_comb begin
case(current_state)
S1: if(...)
next_state = S2; //阻塞赋值
...
endcase
end
//第三段,同步时序 always 模块(组合逻辑也可以),描述状态机的输出
always @ (posedge clk) begin
...//初始化
case(next_state)
S1: out1 <= 1'b1; //注意是非阻塞逻辑
S2: out2 <= 1'b1;
default:... //default 的作用是免除综合工具综合出锁存器
endcase
end

四. 实验内容

采用有限状态机,基于 SystemVerilog HDL 设计并实现一个报纸自动贩售机。整个工程的顶层模块如图 4-3 所示,输入/输出端口如表 4-1 所示。使用 4 个七段数码管实时显示已付款和找零情况。其中,两个数码管对应“已付款”,另两个数码管对应“找零”,单位为分。通过 1 个拨动开关对数字钟进行复位控制。使用两个按键模拟投币,其中一个按键对应 5 分,另一个按键对应 1 角。使用 1 个LED 灯标识出售是否成功,灯亮表示出售成功,否则表示已付款不够,出售失败。

假设报纸价格为 15 分,合法的投币组合包括:

⚫ 1 个 5 分的硬币和一个 1 角的硬币,不找零

⚫ 3 个五分的硬币,不找零

⚫ 1 个 1 角的硬币和一个 5 分的硬币,不找零

⚫ 两个 1 角的硬币是合法的,找零 5 分。

当投入硬币的组合为上面 4 种之一时,则购买成功,LED 灯亮。购买成功后,LED灯持续亮 10 秒,然后自动熄灭,同时 4 个数码管也恢复为 0

image-20240520144232012.png

报纸自动贩售机由 4 部分构成。

⚫ 第一部分是计时器模块,该模块又由 3 个子模块构成,分别是计数器电路、使能时钟生成电路和边沿检测电路。

⚫ 第二部分是整个自动贩售机电路的核心——贩售机状态机。状态机根据投币情况产生“已付款”和“找零”输出。此外,如果已付款超过 15 分,则将 LED 灯点亮,表示出售成功。

⚫ 第三部分是两个 8 位二进制转 BCD 模块,分别将二进制的“已付款”和“找零”值转化为 BCD 编码,即 10 进制数。本实验中,该模块不需要实现,由教师直接提供 IP 使用

⚫ 第四部分是 7 段数码管动态扫描显示模块,它实现“已付款”和“找零”值的最终显示。

image-20240520144412271.png

完成上述分秒数字钟的设计,需要有以下几点需要注意:

  1. 7 段数码管动态扫描必须采用使能时钟实现,扫描频率为 1KHz(1ms)。

  2. 必须通过边沿检测电路识别“5 分”和“1 角”按键按下产生的上升沿,以用于后续处理。

  3. 用于计时的时钟频率为 25MHz(40ns)。

  4. 由于 7 段数码管扫描周期是 1ms,购买成功后需要等待 10s,从而造成仿真时间过长。为了加快仿真速度,可以在仿真的时候使用较大的计时单位和扫描速度

五. 实验步骤

第一部分、计时器模块

1.使能时钟生成器

首先系统的时钟主频为25MHz,所以时钟周期为1/25MHz = 40ns

我们要产生1ms的使能信号,只需经过1ms/40ns = 25000个时钟周期

因此可以定义一个计时器,每当counter == 24999时,就产生一个使能信号,并把counter重置为0

module enable_clock(
    input logic sys_clk,
    input logic sys_rst_n,
    output logic enable_1ms
    );
    
    logic [14:0] counter;
    //为了加快仿真速度,从25000调整到24
    parameter integer ONE_MS = 25000;       //输入时钟频率为25MHz
    
    always_ff @(posedge sys_clk)
        if(~sys_rst_n)
            counter <= 15'd0;
        else if(counter == ONE_MS -1)
            counter <= 15'd0;
        else
            counter <= counter + 1;
            
    assign enable_1ms = (counter == ONE_MS -1);
endmodule

2.边沿检测电路

在头歌中已经联系过了边沿检测的书写,主要的原理就是保存上一时刻的信号值,如果上一时刻的信号为0,下一时刻的信号为1,那么就可以检测到一个上升沿。

module edge_detection(
    input logic sys_clk,
    input logic sys_rst_n,
    input logic signal_in,
    output logic edge_detection
    );
    
    logic signal_in_d;          //上一时刻的signal_in
    
    always_ff @(posedge sys_clk)
        if(~sys_rst_n)
            signal_in_d <= 0;
        else
            signal_in_d <= signal_in;
     
     assign edge_detection = ~signal_in_d & signal_in;          //上升沿
endmodule

通过边沿检测电路,我们可以检测到硬币被投入的时机,并且防止投币被多次计算

3.计数器

这一部分主要是为了在售出成功后保持10s的LED灯亮起,所以要设计一个10s的计数器。我们可以借鉴上面的1ms使能信号的产生,这里无非是250000000个时钟周期。

module timer_10s(
    input logic sys_clk,
    input logic sys_rst_n,
    input logic start,
    output logic done
    );
    
    logic[31:0] count;
    parameter integer TEN_SECONDS = 250000000;
    
    always_ff @(posedge sys_clk)
        if(~sys_rst_n)
            count <= 0;
        else if(start)
            count <= (count == TEN_SECONDS-1) ? 0 : count + 1;
        else
            count <= 0;
    
    assign done = (count == TEN_SECONDS-1);
endmodule

第二部分、贩售状态机

我们首先定义如下几个状态

typedef enum logic [2:0] {
        IDLE = 3'b000,      //空闲
        PAID5 = 3'b001,
        PAID10 = 3'b010,
        PAID15 = 3'b011,
        PAID20 = 3'b100,
        WAIT_RESET = 3'b101
    } state_t;

首先我们根据题目的描述,给出贩售状态机的状态转换图

image-20240520145948080.png

状态转移比较简单,每一次都是从次态变为现态,代码如下

// State transition
always_ff @(posedge sys_clk) begin
    if (~sys_rst_n)
        state <= IDLE;
    else
        state <= next_state;        //次态变为现态
end

次态逻辑里面定义了每种状态下的下一时刻的状态,代码如下

其中timer_done表示10s已经到了,那么就把PAID15和PAID20下一时刻的状态设置为WAIT_RESET

// Next state logic
always_comb begin
    case (state)
        IDLE: begin
            if (edge_detected_coin5)
                next_state = PAID5;
            else if (edge_detected_coin10)
                next_state = PAID10;
            else
                next_state = state;
        end
        PAID5: begin
            if (edge_detected_coin5)
                next_state = PAID10;
            else if (edge_detected_coin10)
                next_state = PAID15;
            else
                next_state = state;
        end
        PAID10: begin
            if (edge_detected_coin5)
                next_state = PAID15;
            else if (edge_detected_coin10)
                next_state = PAID20;
            else
                next_state = state;
        end
        PAID15: begin
            // 保持住现在的状态
            if(timer_done)
                next_state = WAIT_RESET;
            else
                next_state = state;
        end
        PAID20: begin
            // 保持住现在的状态
            if(timer_done)
                next_state = WAIT_RESET;
            else
                next_state = state;
        end
        WAIT_RESET: next_state = IDLE;
        default: next_state = IDLE;
    endcase
end

输出逻辑分别定义了price,change,open(表示LED灯是否亮起),这里我多定义了一个LED状态,用led表示

// Output logic
assign price =  (state == WAIT_RESET) ? 8'd0 : total_paid; // Price represents the total paid amount
assign change = (state == WAIT_RESET) ? 8'd0 : change_amount;
assign open = (state == PAID15 || state == PAID20);
assign led = (state == PAID15 || state == PAID20); // LED on for 10 seconds after successful purchase

然后再分别定义total_paid和change_amount

// Total paid logic
always_ff @(posedge sys_clk) begin
    if (~sys_rst_n)
        total_paid <= 8'd0;
    else if(state == WAIT_RESET)      //这句话保证了在出售成功后10s能够自动清零
        total_paid <= 8'd0;
    else if (edge_detected_coin5)
        total_paid <= total_paid + 8'd5;
    else if (edge_detected_coin10)
        total_paid <= total_paid + 8'd10;
end
​
// Change calculation logic
always_ff @(posedge sys_clk) begin
    if (~sys_rst_n)
        change_amount <= 8'd0;
    else if (state == PAID20)     //因为只有PAID20状态为合法状态,所以只需要定义固定的找零值为5
        change_amount <= 8'd5;
    else
        change_amount <= 8'd0;
end

vending_machine_fsm模块代码如下

module vending_machine_fsm(
    input logic sys_clk,
    input logic sys_rst_n,
    input logic coin5,
    input logic coin10,
    output logic [7:0] change,
    output logic [7:0] price,
    output logic open,
    output logic led
    );
    
    // 定义枚举类——状态
    typedef enum logic [2:0] {
        IDLE = 3'b000,      //空闲
        PAID5 = 3'b001,
        PAID10 = 3'b010,
        PAID15 = 3'b011,
        PAID20 = 3'b100,
        WAIT_RESET = 3'b101
    } state_t;
​
    state_t state, next_state;
    logic [7:0] total_paid;
    logic [7:0] change_amount;
    logic timer_done;
    logic enable_1ms;
    logic edge_detected_coin5, edge_detected_coin10;
    
    timer_10s timer_10s_inst(
        .sys_clk(sys_clk),
        .sys_rst_n(sys_rst_n),
        .start(state == PAID15 || state == PAID20),
        .done(timer_done)
    );
    
    // Instantiate timer module for edge detection and 1ms enable signal
    timer_module timer_module_inst(
        .sys_clk(sys_clk),
        .sys_rst_n(sys_rst_n),
        .coin5(coin5),
        .coin10(coin10),
        .enable_1ms(enable_1ms),
        .edge_detected_coin5(edge_detected_coin5),
        .edge_detected_coin10(edge_detected_coin10)
    );
    
    // Output logic
    assign price =  (state == WAIT_RESET) ? 8'd0 : total_paid; // Price represents the total paid amount
    assign change = (state == WAIT_RESET) ? 8'd0 : change_amount;
    assign open = (state == PAID15 || state == PAID20);
    assign led = (state == PAID15 || state == PAID20); // LED on for 10 seconds after successful purchase
​
    // State transition
    always_ff @(posedge sys_clk) begin
        if (~sys_rst_n)
            state <= IDLE;
        else
            state <= next_state;        //次态变为现态
    end
​
    // Next state logic
    always_comb begin
        case (state)
            IDLE: begin
                if (edge_detected_coin5)
                    next_state = PAID5;
                else if (edge_detected_coin10)
                    next_state = PAID10;
                else
                    next_state = state;
            end
            PAID5: begin
                if (edge_detected_coin5)
                    next_state = PAID10;
                else if (edge_detected_coin10)
                    next_state = PAID15;
                else
                    next_state = state;
            end
            PAID10: begin
                if (edge_detected_coin5)
                    next_state = PAID15;
                else if (edge_detected_coin10)
                    next_state = PAID20;
                else
                    next_state = state;
            end
            PAID15: begin
                // 保持住现在的状态
                if(timer_done)
                    next_state = WAIT_RESET;
                else
                    next_state = state;
            end
            PAID20: begin
                // 保持住现在的状态
                if(timer_done)
                    next_state = WAIT_RESET;
                else
                    next_state = state;
            end
            WAIT_RESET: next_state = IDLE;
            default: next_state = IDLE;
        endcase
    end
​
    // Total paid logic
    always_ff @(posedge sys_clk) begin
        if (~sys_rst_n)
            total_paid <= 8'd0;
        else if(state == WAIT_RESET)
            total_paid <= 8'd0;
        else if (edge_detected_coin5)
            total_paid <= total_paid + 8'd5;
        else if (edge_detected_coin10)
            total_paid <= total_paid + 8'd10;
    end
    
    // Change calculation logic
    always_ff @(posedge sys_clk) begin
        if (~sys_rst_n)
            change_amount <= 8'd0;
        else if (state == PAID20)
            change_amount <= 8'd5;
        else
            change_amount <= 8'd0;
    end
    
endmodule

第三部分、8为二进制转BCD模块

本实验中,该模块不需要实现,由教师直接提供 IP 使用

IP核实例化后结构如下

image-20240520152501255.png

第四部分、7段数码管动态扫描显示模块

为了实现动态扫描,还是要利用到之前定义的使能时钟生成器。

此外,还需要实现七段数码管的译码器,即把十进制0~9翻译成对应的8位数码管电路(本实验是8位,最高位始终无效),注意是共阳极低电平有效,代码如下

module seven_segment_decoder(
    input logic [3:0] digit,
    output logic [7:0] seg
    );
    
    always_comb begin
        case (digit)
            4'd0: seg = 8'b11000000; // 0
            4'd1: seg = 8'b11111001; // 1
            4'd2: seg = 8'b10100100; // 2
            4'd3: seg = 8'b10110000; // 3
            4'd4: seg = 8'b10011001; // 4
            4'd5: seg = 8'b10010010; // 5
            4'd6: seg = 8'b10000010; // 6
            4'd7: seg = 8'b11111000; // 7
            4'd8: seg = 8'b10000000; // 8
            4'd9: seg = 8'b10010000; // 9
            default: seg = 8'b11111111; // Blank
        endcase
    end
    
endmodule

在实现4位七段数码管显示模块时,应当实例化上面的两个模块。由于是4位需要来回扫描,因此可以定义一个scan_state来表示扫描到的位置,每次扫描到对应的位置,都使能相应的数码管,从而显示不同位置上的数字。这些数字从左到右分别是change_bcd[7:4], change_bcd[3:0], paid_bcd[7:4], paid_bcd[3:0]。

模块代码如下:

module seven_segment_display(
    input logic sys_clk,
    input logic sys_rst_n,
    input logic [7:0] paid_bcd,
    input logic [7:0] change_bcd,
    output logic [7:0] a_to_g,
    output logic [3:0] an
    );
    
    // Internal signals
    logic [1:0] scan_state;
    logic [3:0] digit;
    logic [7:0] seg;
    logic enable_1ms;
    
    // Instantiate the enable clock module to generate 1ms enable signal
    enable_clock enable_clock_inst (
        .sys_clk(sys_clk),
        .sys_rst_n(sys_rst_n),
        .enable_1ms(enable_1ms)
    );
​
    // Instantiate the segment decoder
    seven_segment_decoder decoder (
        .digit(digit),
        .seg(seg)
    );
​
    // Scan state machine
    always_ff @(posedge sys_clk) begin
        if (~sys_rst_n)
            scan_state <= 2'd0;
            //为了加快仿真速度,这里调整为50个时钟周期(1/10 时间)
        else if (enable_1ms) // 25MHz clock / 1KHz = 25000   如果计数器 counter 达到24999(即1ms),则将 scan_state 加1。
            scan_state <= (scan_state == 2'd3) ? 2'd0 : scan_state + 2'd1;//该变量用于跟踪当前正在扫描的数码管,2'd0到2'd3分别表示4个数码管。
    end
​
    // Digit and anode control
    always_comb begin
        case (scan_state)
            2'd0: begin
                digit = paid_bcd[3:0];
                an = 4'b0001; // Enable AN0
            end
            2'd1: begin
                digit = paid_bcd[7:4];
                an = 4'b0010; // Enable AN1
            end
            2'd2: begin
                digit = change_bcd[3:0];
                an = 4'b0100; // Enable AN2
            end
            2'd3: begin
                digit = change_bcd[7:4];
                an = 4'b1000; // Enable AN3
            end
            default: begin
                digit = 4'd0;
                an = 4'b0000;
            end
        endcase
    end
​
    // Assign segment output
    assign a_to_g = seg;
​
endmodule

以上,便完成了所有子模块的实现,现在可以构造顶层模块vend

第五部分、顶层模块

module vend(
    input sys_clk, sys_rst_n,
    input coin5, coin10,
    output [3 : 0] an,
    output [7 : 0] a_to_g,
    output open
    );
    
    logic led;
    logic [7:0] paid_bin, change_bin;
    logic [7:0] paid_bcd, change_bcd;
​
    // Instantiate FSM
    vending_machine_fsm fsm (
        .sys_clk(sys_clk),
        .sys_rst_n(sys_rst_n),
        .coin5(coin5),
        .coin10(coin10),
        .change(change_bin),
        .price(paid_bin),
        .open(open),
        .led(led)
    );
​
    // Instantiate bin2bcd for paid and change
    bin2bcd bin2bcd_paid (
        .bin(paid_bin),
        .bcd(paid_bcd)
    );
​
    bin2bcd bin2bcd_change (
        .bin(change_bin),
        .bcd(change_bcd)
    );
​
    // Instantiate 7-segment display
    seven_segment_display display (
        .sys_clk(sys_clk),
        .sys_rst_n(sys_rst_n),
        .paid_bcd(paid_bcd),
        .change_bcd(change_bcd),
        .a_to_g(a_to_g),
        .an(an)
    );
    
endmodule

第六部分、仿真测试

由于本实验的模块过多,所以我采用了单元测试的方法,分别测试了timer_module, vending_machine_fsm, seven_segment_display, vend模块,以下便写出测试的代码和仿真结果

1.timer_module

为了加快仿真速度,我们把SIMULATION_CLK_PERIOD设置为4,仿真等待时间设置为20(总仿真时间仅有1000ns),把使能时钟中的25000改为24

`timescale 1ns / 1ps
​
module timer_module_tb;
​
    // Parameters for simulation
    localparam integer SIMULATION_CLK_PERIOD = 4; // 4ns for 250MHz clock to speed up simulation
    localparam integer SIMULATION_WAIT_TIME = 20; // 20ns for each test case observation
​
    // Inputs
    logic sys_clk;
    logic sys_rst_n;
    logic coin5;
    logic coin10;
​
    // Outputs
    logic enable_1ms;
    logic edge_detected_coin5;
    logic edge_detected_coin10;
​
    // Instantiate the timer module
    timer_module uut (
        .sys_clk(sys_clk),
        .sys_rst_n(sys_rst_n),
        .coin5(coin5),
        .coin10(coin10),
        .enable_1ms(enable_1ms),
        .edge_detected_coin5(edge_detected_coin5),
        .edge_detected_coin10(edge_detected_coin10)
    );
​
    // Clock generation
    initial begin
        sys_clk = 0;
        forever #(SIMULATION_CLK_PERIOD / 2) sys_clk = ~sys_clk;
    end
​
    // Stimulus process
    initial begin
        // Initialize Inputs
        sys_rst_n = 0;
        coin5 = 0;
        coin10 = 0;
​
        // Apply reset
        #(SIMULATION_WAIT_TIME);
        sys_rst_n = 1;
​
        // Test edge detection for coin5
        #(SIMULATION_WAIT_TIME);
        coin5 = 1;
        #(SIMULATION_WAIT_TIME);
        coin5 = 0;
        #(SIMULATION_WAIT_TIME * 5); // Wait to observe edge detection
​
        // Test edge detection for coin10
        #(SIMULATION_WAIT_TIME);
        coin10 = 1;
        #(SIMULATION_WAIT_TIME);
        coin10 = 0;
        #(SIMULATION_WAIT_TIME * 5); // Wait to observe edge detection
​
        // Observe enable_1ms signal (accelerated to observe in short time)
        #(SIMULATION_WAIT_TIME * 20); // Short wait to observe 1ms signal
​
        // End of simulation
        $stop;
    end
​
endmodule

仿真结果

image-20240520154650111.png

可以看到,在检测到coin5和coin10上升沿的时候,边沿检测的值为1

2.vending_machine_fsm

这里可以分四种情况来测,我这里只列举出一种情况(5+5+10)

`timescale 1ns / 1ps
​
module vending_machine_fsm_tb;
​
    // Parameters for simulation
    localparam integer SIMULATION_CLK_PERIOD = 4; // 4ns for 250MHz clock to speed up simulation
    localparam integer SIMULATION_WAIT_TIME = 20; // 20ns for each test case observation
​
    // Inputs
    logic sys_clk;
    logic sys_rst_n;
    logic coin5;
    logic coin10;
​
    // Outputs
    logic [7:0] change;
    logic [7:0] price;
    logic open;
    logic led;
​
    // Instantiate the vending machine FSM module
    vending_machine_fsm uut (
        .sys_clk(sys_clk),
        .sys_rst_n(sys_rst_n),
        .coin5(coin5),
        .coin10(coin10),
        .change(change),
        .price(price),
        .open(open),
        .led(led)
    );
​
    // Clock generation
    initial begin
        sys_clk = 0;
        forever #(SIMULATION_CLK_PERIOD / 2) sys_clk = ~sys_clk;
    end
​
    // Stimulus process
    initial begin
        // Initialize Inputs
        sys_rst_n = 0;
        coin5 = 0;
        coin10 = 0;
​
        // Apply reset
        #(SIMULATION_WAIT_TIME);
        sys_rst_n = 1;
​
        // Test case 4: Insert 5 cents, then another 5 cents, and then 10 cents to reach 20 cents
        #(SIMULATION_WAIT_TIME);
        coin5 = 1;
        #(SIMULATION_WAIT_TIME);
        coin5 = 0;
        #(SIMULATION_WAIT_TIME * 2); // Short wait to observe state change
​
        // Insert second 5 cents
        #(SIMULATION_WAIT_TIME);
        coin5 = 1;
        #(SIMULATION_WAIT_TIME);
        coin5 = 0;
        #(SIMULATION_WAIT_TIME * 2); // Short wait to observe state change
​
        // Insert 10 cents
        #(SIMULATION_WAIT_TIME);
        coin10 = 1;
        #(SIMULATION_WAIT_TIME);
        coin10 = 0;
        #(SIMULATION_WAIT_TIME * 2); // Short wait to observe state change
​
        // End of simulation
        $stop;
    end
​
endmodule

仿真结果

image-20240520155139873.png

3.seven_segment_display

我分别测试了paid_bcd = 15, change_bcd = 5 和 paid_bcd = 20, change_bcd = 10,两种情况,这两种情况真实中并不能出现,该测试仅仅是为了观察动态扫描的每一位是否正常显示

`timescale 1ns / 1ps
​
module seven_segment_display_tb;
​
    // Parameters for simulation
    localparam integer SIMULATION_CLK_PERIOD = 4; // 1ns for 1GHz clock to speed up simulation
    localparam integer SIMULATION_WAIT_TIME = 20; // 10ns for each test case observation
​
    // Inputs
    logic sys_clk;
    logic sys_rst_n;
    logic [7:0] paid_bcd;
    logic [7:0] change_bcd;
​
    // Outputs
    logic [7:0] a_to_g;
    logic [3:0] an;
​
    // Instantiate the seven segment display module
    seven_segment_display uut (
        .sys_clk(sys_clk),
        .sys_rst_n(sys_rst_n),
        .paid_bcd(paid_bcd),
        .change_bcd(change_bcd),
        .a_to_g(a_to_g),
        .an(an)
    );
​
    // Clock generation
    initial begin
        sys_clk = 0;
        forever #(SIMULATION_CLK_PERIOD / 2) sys_clk = ~sys_clk;
    end
​
    // Stimulus process
    initial begin
        // Initialize Inputs
        sys_rst_n = 0;
        paid_bcd = 8'h00;
        change_bcd = 8'h00;
​
        // Apply reset
        #(SIMULATION_WAIT_TIME);
        sys_rst_n = 1;
​
        // Test case: Display paid amount = 15, change amount = 5
        #(SIMULATION_WAIT_TIME);
        paid_bcd = 8'h15; // 15 in BCD
        change_bcd = 8'h05; // 5 in BCD
        #(SIMULATION_WAIT_TIME * 10); // Observe for a while
​
        // Test case: Display paid amount = 20, change amount = 10
        #(SIMULATION_WAIT_TIME);
        paid_bcd = 8'h20; // 20 in BCD
        change_bcd = 8'h10; // 10 in BCD
        #(SIMULATION_WAIT_TIME * 10); // Observe for a while
​
        // End of simulation
        $stop;
    end
​
endmodule

仿真结果

image-20240520155531944.png

将对应的a_to_g还原,可以发现这里的实现逻辑是正确的

4.vend

这里我测试了两种情况,投入15分和投入20分,两种情况都能实现动态扫描(an[3:0]),且每位动态扫描的过程中都能够正确的显示,并且实现了投入15分后等待10s后(仿真时将时间加快了)会自动清零重新开始计数。

仿真结果

image-20240520160354874.png

至此,所有模块的仿真测试均通过。

六. 实验结果

image-20240520161341223.png

image-20240520161355079.png

image-20240520161402310.png

image-20240520161411064.png