I've been doing a finite state machine of an elevator using Verilog. The elevator contains four states:
IDLE: When the elevator is stopped.ERROR: When the elevator's weight limit is exceeded.MOVING: When the elevator leaves the current floor and start moving across the floors.ALERT: When the elevator's time of activation runs out.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?
The
over_weightis 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.