verilogsystem-verilogfsm

How can I simulate a simple elevator FSM where it can detect overweight?


I've been doing a finite state machine of an elevator using Verilog. The elevator contains four states:

This is the design that I came up with:

module Elevator_FSM2;

    // Input signals 
    reg clk;
    reg reset;
    reg [3:0] request_floor;
    reg over_time;
    reg over_weight;

    // Output signals
    wire [3:0] current_floor;
    wire door_alert;
    wire weight_alert;

  
    Elevator_FSM_inst uut (
        .clk(clk),
        .reset(reset),
        .request_floor(request_floor),
        .over_time(over_time),
        .over_weight(over_weight),
        .current_floor(current_floor),
        .door_alert(door_alert),
        .weight_alert(weight_alert)
    );

    
    initial clk = 0;
    always #5 clk = ~clk;  


    initial begin
        // Monitor the signals
        $monitor("Time=%0t | State=%b | Floor=%d | Request=%d | Door=%b | Weight=%b", 
                  $time, uut.current_state, current_floor, request_floor, door_alert, weight_alert);

        // Initialize inputs
        reset = 1; request_floor = 4'd0; over_time = 0; over_weight = 0;
        #0 reset = 0;

        // Request to go to floor 3
        #10 request_floor = 4'd3;

        // Wait to observe movement
        #50;

        // End simulation
        $finish;
    end

    
    always @(posedge clk) begin
        if (uut.current_floor != current_floor)  // Print only if floor changes
            $display("Current Floor: %d at Time=%0t", current_floor, $time);
    end

endmodule


module Elevator_FSM_inst (
    input        clk,
    input        reset,
    input  [3:0] request_floor,
    input        over_time,
    input        over_weight,
    output reg [3:0] current_floor,
    output reg       door_alert,
    output reg       weight_alert
);
    localparam IDLE   = 2'b00,
               MOVING = 2'b01,
               ALERT  = 2'b10,
               ERROR  = 2'b11;

    reg [1:0] current_state, next_state;

    always @(posedge clk or posedge reset) begin
        if (reset)
            current_state <= IDLE;
        else
            current_state <= next_state;
    end

    always @(*) begin
        next_state = current_state;
        door_alert = 0;
        weight_alert = 0;

        case (current_state)
            IDLE: begin
                if (request_floor != 4'b0000)
                    next_state = MOVING;
            end

            MOVING: begin
                if (over_time) begin
                    door_alert = 1;
                    next_state = ALERT;
                end else if (over_weight) begin
                    weight_alert = 1;
                    next_state = ERROR;
                end else if (current_floor == request_floor) begin
                    next_state = IDLE;
                end
            end

            ALERT: begin
                if (!over_time)
                    next_state = MOVING;
            end

            ERROR: begin
                if (!over_weight)
                    next_state = MOVING;
            end
        endcase
    end

    always @(posedge clk or posedge reset) begin
        if (reset)
            current_floor <= 0;
        else if (current_state == MOVING) begin
            if (current_floor < request_floor)
                current_floor <= current_floor + 1;
            else if (current_floor > request_floor)
                current_floor <= current_floor - 1;
        end
    end
endmodule

This is the log I got from the simulation, and the output waves were as expected:

Time=0 | State=00 | Floor= 0 | Request= 0 | Door=0 | Weight=0
Time=10 | State=00 | Floor= 0 | Request= 3 | Door=0 | Weight=0
Time=15 | State=01 | Floor= 0 | Request= 3 | Door=0 | Weight=0
Time=25 | State=01 | Floor= 1 | Request= 3 | Door=0 | Weight=0
Time=35 | State=01 | Floor= 2 | Request= 3 | Door=0 | Weight=0
Time=45 | State=01 | Floor= 3 | Request= 3 | Door=0 | Weight=0
Time=55 | State=00 | Floor= 3 | Request= 3 | Door=0 | Weight=0
design.sv:48: $finish called at 60 (1s)

In the testbench, the elevator moves from floor 1 to floor 3. While the elevator is moving, the FSM is in state 01 (MOVING). When the elevator reaches the requested floor, it transitions to state 00 (IDLE).All good so far.

However, when I tested the state that stop (IDLE) the elevator when the weight is overloaded (ERROR):

initial clk = 0;
    always #5 clk = ~clk; 

   
    initial begin

        $monitor("Time=%0t | State=%b | Current Floor=%d | Request Floor=%d | Door Alert=%b | Weight Alert=%b", 
                  $time, uut.current_state, current_floor, request_floor, door_alert, weight_alert);
        
        
        reset = 1; 
        request_floor = 4'd1; 
        over_time = 0; 
        over_weight = 0;

        #10 reset = 0;  

        #10 request_floor = 4'd3;

        #40; 
        $display("Elevator reached Floor 2. Activating over_weight signal.");
        
        over_weight = 1;
        #20; 

        $display("Elevator in ERROR state due to over_weight.");
        over_weight = 0;  

        #40;
      
        #100 $finish;
    end

Here's what I've tried is when the elevator goes to floor 3, stop in floor 2 and trigger the ERROR state when over weight is 1, and later resume its usual move to the requested floor which is 3. However, this is what I've got:

Time=0 | State=00 | Current Floor= 0 | Request Floor= 1 | Door Alert=0 | Weight Alert=0
Time=15 | State=01 | Current Floor= 0 | Request Floor= 1 | Door Alert=0 | Weight Alert=0
Time=20 | State=01 | Current Floor= 0 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=25 | State=01 | Current Floor= 1 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=35 | State=01 | Current Floor= 2 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=45 | State=01 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=55 | State=00 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Elevator reached Floor 2. Activating over_weight signal.
Time=65 | State=01 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=1
Time=75 | State=11 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Elevator in ERROR state due to over_weight.
Time=85 | State=01 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=95 | State=00 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=105 | State=01 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=115 | State=00 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=125 | State=01 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=135 | State=00 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=145 | State=01 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=155 | State=00 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=165 | State=01 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=175 | State=00 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=185 | State=01 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=195 | State=00 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=205 | State=01 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
Time=215 | State=00 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
design.sv:65: $finish called at 220 (1s)

As you can see in the log, the FSM doesn't detect when and where the ALERT state should be triggered. The over_weight is activated in floor 3 rather than floor 2.

How can I successfully detect the overweight with is associated state (ERROR) and make a transition of state?


Solution

  • The over_weight is activated in floor 3 rather than floor 2.

    That is incorrect. When I run the simulation, I see this output:

    Time=0 | State=00 | Current Floor= 0 | Request Floor= 1 | Door Alert=0 | Weight Alert=0
    Time=15 | State=01 | Current Floor= 0 | Request Floor= 1 | Door Alert=0 | Weight Alert=0
    Time=20 | State=01 | Current Floor= 0 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
    Time=25 | State=01 | Current Floor= 1 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
    Time=35 | State=01 | Current Floor= 2 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
    Time=45 | State=01 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
    Time=55 | State=00 | Current Floor= 3 | Request Floor= 3 | Door Alert=0 | Weight Alert=0
    Elevator reached Floor 2. Activating over_weight signal.
    

    This clearly shows that you activated over_weight too late. You did it after you reached Floor 3, not when you reached Floor 2. It is also clear when you view waveforms.

    You need a smaller delay before you set over_weight=1:

        #10; // smaller delay
        $display("Elevator reached Floor 2. Activating over_weight signal.");
    
        over_weight = 1;
        #20;
    

    This sets over_weight=1 while on Floor 2.

    You probably want the weight_alert signal to be sequential logic instead of combinational logic because I think you want it to retain its state.