如何在Verilog电梯FSM中实现超重检测与状态切换?
电梯有限状态机(FSM)超重检测异常问题排查与修复
问题概述
使用Verilog实现包含IDLE、MOVING、ALERT、ERROR四种状态的电梯FSM,基础楼层调度功能仿真正常,但在测试超重触发ERROR状态场景时出现异常:预期电梯前往3楼过程中在2楼触发超重信号进入ERROR状态,恢复后继续运行至3楼,实际却在3楼才检测到超重,且状态在IDLE与MOVING间反复切换。
现有实现代码
测试模块
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; endmodule
FSM核心模块
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
基础功能仿真结果
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)
超重测试用例与异常结果
测试用例
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
异常仿真结果
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 ... // 后续反复在IDLE/MOVING间切换
问题根因
- 测试用例时序错误:测试中
#40的延时计算偏差,导致超重信号在电梯到达3楼并进入IDLE状态后才被置位,错过2楼的触发时机。 - FSM逻辑优先级问题:
MOVING状态中,current_floor == request_floor的判断优先级高于超重检测,即使超重信号有效,只要楼层到达请求值,就会进入IDLE,忽略异常状态。 - 异常状态退出逻辑缺陷:
ERROR状态解除后回到MOVING,但此时楼层已等于请求楼层,立刻切回IDLE,导致状态反复切换。
修复方案
1. 修正测试用例时序
调整延时,确保超重信号在电梯处于2楼时触发:
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; #20; // 调整延时,对应电梯到达2楼的时序 $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
2. 优化FSM状态转移逻辑
- 调整
MOVING状态判断优先级,优先处理异常信号; - 在
ALERT/ERROR状态中保持告警输出,直到异常解除;
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 door_alert = 1; // 保持告警直到超时解除 if (!over_time) next_state = MOVING; end ERROR: begin weight_alert = 1; // 保持告警直到超重解除 if (!over_weight) next_state = MOVING; end endcase end
修复后预期流程
- 电梯从0楼出发,响应3楼请求,依次到达1楼、2楼;
- 2楼时触发超重信号,进入
ERROR状态,weight_alert持续置1; - 超重信号解除后,回到
MOVING状态,继续运行至3楼; - 到达3楼后进入
IDLE状态,流程结束。
内容的提问来源于stack exchange,提问作者Gr_10
相关产品推荐
相关产品推荐

