(* fsm_safe_state = “default_state“ *)

FSM安全状态设计与应用

一、(* fsm_safe_state = "default_state" *)

这个属性是用于确保有限状态机(FSM)在异常情况下能够恢复到安全状态的重要指令。

1、作用:

指定当状态机发生异常或未定义行为时,强制恢复到指定的安全状态。

2、语法:

(* fsm_safe_state = "state_name" *)

二、使用示例

1、基础用法:

module safe_fsm (
    input wire clk,
    input wire rst_n,
    input wire trigger,
    output reg [1:0] status
);

// 状态定义
localparam IDLE    = 2'b00;
localparam WORKING = 2'b01;
localparam DONE    = 2'b10;
// 注意:2'b11 是未定义状态

(* fsm_safe_state = "IDLE" *)
reg [1:0] current_state, next_state;

// 状态寄存器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        current_state <= IDLE;
    end else begin
        current_state <= next_state;
    end
end

// 状态转移逻辑
always @(*) begin
    case (current_state)
        IDLE: begin
            status = 2'b00;
            next_state = trigger ? WORKING : IDLE;
        end
        WORKING: begin
            status = 2'b01;
            next_state = DONE;
        end
        DONE: begin
            status = 2'b10;
            next_state = IDLE;
        end
        default: begin
            status = 2'b00;
            next_state = IDLE;  // 安全恢复
        end
    endcase
end

endmodule

2、更复杂的安全状态机:

module traffic_light_fsm (
    input wire clk,
    input wire rst_n,
    input wire emergency,
    output reg [2:0] light  // [red, yellow, green]
);

// 状态定义
localparam RED    = 3'b001;
localparam YELLOW = 3'b010;
localparam GREEN  = 3'b100;
localparam ALL_RED = 3'b001;  // 安全状态

(* fsm_safe_state = "ALL_RED" *)
reg [2:0] state, next_state;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        state <= RED;
    end else begin
        state <= next_state;
    end
end

always @(*) begin
    case (state)
        RED: begin
            light = 3'b001;
            next_state = emergency ? RED : GREEN;
        end
        GREEN: begin
            light = 3'b100;
            next_state = emergency ? RED : YELLOW;
        end
        YELLOW: begin
            light = 3'b010;
            next_state = emergency ? RED : RED;
        end
        default: begin
            light = 3'b001;  // 强制红灯
            next_state = RED;
        end
    endcase
end

endmodule

三、综合效果

1、硬件实现

工具会生成额外的逻辑来检测非法状态并恢复:

  • 状态寄存器编码验证

  • 自动恢复电路

  • 安全状态强制逻辑

2.、可靠性提升

  • 抗辐照能力:防止宇宙射线等导致的位翻转

  • 电源噪声免疫:电压波动时的状态保护

  • 时序违例恢复:时钟偏移时的安全恢复


四、最佳实践

1、选择合适的安全状态

// 好的安全状态:停止、空闲、复位状态
(* fsm_safe_state = "IDLE" *)
(* fsm_safe_state = "STOP" *)
(* fsm_safe_state = "SAFE_MODE" *)

// 避免的安全状态:可能引起危险操作的状态
// (* fsm_safe_state = "MOTOR_ON" *)  // 危险!
// (* fsm_safe_state = "HEATER_ON" *) // 危险!

2、结合其他安全属性

(* fsm_safe_state = "IDLE" *)
(* fsm_encoding = "safe" *)           // 使用安全编码
(* fsm_extract = "yes" *)             // 明确提取为FSM
reg [2:0] state;

3、完整的安全FSM模板

module safe_fsm_template (
    input wire clk,
    input wire rst_n
);

localparam SAFE_STATE = 3'b000;
localparam STATE_A = 3'b001;
localparam STATE_B = 3'b010;
localparam STATE_C = 3'b100;

(* fsm_safe_state = "SAFE_STATE" *)
(* fsm_encoding = "gray" *)  // 使用格雷码减少位翻转影响
reg [2:0] current_state;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        current_state <= SAFE_STATE;
    end else begin
        case (current_state)
            SAFE_STATE: current_state <= STATE_A;
            STATE_A:    current_state <= STATE_B;
            STATE_B:    current_state <= STATE_C;
            STATE_C:    current_state <= SAFE_STATE;
            default:    current_state <= SAFE_STATE;  // 硬件安全网
        endcase
    end
end

endmodule

五、适用场景

  1. 安全关键系统

    • 汽车电子

    • 医疗设备

    • 工业控制

  2. 高可靠性应用

    • 航空航天

    • 通信基础设施

    • 金融系统

  3. 恶劣环境

    • 高辐射环境

    • 极端温度

    • 电源不稳定的场合

这个属性是提高数字系统可靠性的重要手段,特别是在对安全性要求高的应用中。

no gpu, how to speed up? """ FSM State Implementations Concrete implementations of different FSM states """ from typing import Dict import numpy as np import onnxruntime as ort from FSM.fsm_base import FSMState, FSMStateName from common.joystick import XboxFlag from common.robot_data import RobotData import math import os import yaml class FSMStateMLP(FSMState): """MLP策略状态实现 - 与C++版本完全一致""" def __init__(self, robot_data: RobotData): super().__init__(robot_data) # 获取包路径 current_dir = os.path.dirname(os.path.abspath(__file__)) config_path = os.path.join(current_dir, "config", "mlp.yaml") with open(config_path, 'r') as f: policy_config = yaml.safe_load(f) # Load configuration exactly like C++ self.action_num_ = policy_config.get('actions_size', 12) self.motor_num_ = policy_config.get('motor_num', 29) self.dt_ = policy_config.get('dt', 0.002) # Size configuration size_config = policy_config.get('size', {}) self.num_hist_ = size_config.get('num_hist', 15) self.obs_size_ = size_config.get('observations_size', 47) # Control configuration control_config = policy_config.get('control', {}) self.action_scale_ = control_config.get('action_scale', 0.5) self.gait_cycle_period_ = control_config.get('gait_cycle_period', 0.9) self.decimation_ = control_config.get('decimation', 5) # Normalization configuration norm_config = policy_config.get('normalization', {}) clip_config = norm_config.get('clip_scales', {}) obs_config = norm_config.get('obs_scales', {}) self.clip_obs_ = clip_config.get('clip_observations', 100.0) self.clip_act_ = clip_config.get('clip_actions', 100.0) self.lin_vel_scale_ = obs_config.get('lin_vel', 2.0) self.ang_vel_scale_ = obs_config.get('ang_vel', 1.0) self.dof_pos_scale_ = obs_config.get('dof_pos', 1.0) self.dof_vel_scale_ = obs_config.get('dof_vel', 0.05) # Read default joint angles (only action_num_ elements like C++) init_config = policy_config.get('init_state', {}) default_angles_list = init_config.get('default_joint_angles', [0.0] * self.action_num_) self.default_joint_angles_ = np.array(default_angles_list[:self.action_num_], dtype=np.float32) # Read kp/kd gains gains_config = policy_config.get('gains', {}) self.kp = np.array(gains_config.get('kp', [300.0] * self.motor_num_)) self.kd = np.array(gains_config.get('kd', [10.0] * self.motor_num_)) # Initialize buffers and actions self.observations_ = np.zeros(self.obs_size_ * self.num_hist_, dtype=np.float32) self.proprio_hist_buf_ = np.zeros(self.obs_size_ * self.num_hist_, dtype=np.float32) self.last_actions_ = np.zeros(self.action_num_, dtype=np.float32) self.actions_ = np.zeros(self.action_num_, dtype=np.float32) # Flags matching C++ self.is_first_obs_ = True self.is_first_action_ = True self.phase_locked = False # Initialize ONNX session self.model_path = os.path.join(current_dir, "model", policy_config["model_path"]) self._init_onnx_session() def _init_onnx_session(self): """初始化ONNX推理会话""" try: self.ort_session_ = ort.InferenceSession(self.model_path) print(f"[FSMStateMLP-ONNX] ONNX model loaded successfully: {self.model_path}") except Exception as e: print(f"[FSMStateMLP] Failed to load ONNX model: {e}") self.ort_session_ = None def on_enter(self): """进入MLP状态""" print("[FSMStateMLP] enter") self.is_first_obs_ = True self.is_first_action_ = True def run(self, flag: XboxFlag): """运行MLP状态 - 与C++版本完全一致""" print("[FSMStateMLP] run") # Only run policy inference every decimation_ steps if int(self.robot_data_.time_now_ / self.dt_) % self.decimation_ == 0: self.compute_observation(flag) self.compute_actions() # Set joint commands exactly like C++ for i in range(self.action_num_): # C++: robot_data_->q_d_(35 - motor_num_ + i) joint_idx = 35 - self.motor_num_ + i self.robot_data_.q_d_[joint_idx] = ( self.actions_[i] * self.action_scale_ + self.default_joint_angles_[i] ) self.robot_data_.q_dot_d_[joint_idx] = 0.0 self.robot_data_.tau_d_[joint_idx] = 0.0 self.last_actions_[i] = self.actions_[i] # Set kp/kd gains self.robot_data_.joint_kp_p_[:self.motor_num_] = self.kp self.robot_data_.joint_kd_p_[:self.motor_num_] = self.kd def compute_observation(self, flag: XboxFlag): """计算观测量 - 与C++版本完全一致""" t_now = float(self.robot_data_.time_now_) # Phase calculation exactly like C++ phase = math.fmod(t_now, self.gait_cycle_period_) cmd_norm = math.sqrt( flag.x_speed_command * flag.x_speed_command + flag.y_speed_command * flag.y_speed_command + flag.yaw_speed_command * flag.yaw_speed_command ) if cmd_norm >= 0.05: self.phase_locked = False tolerance = 0.1 if cmd_norm < 0.05 and abs(phase - self.gait_cycle_period_) < tolerance: self.phase_locked = True if self.phase_locked: phase = 0 # Command vector exactly like C++ command = np.array([ math.sin(2 * math.pi * phase), math.cos(2 * math.pi * phase), flag.x_speed_command, flag.y_speed_command, flag.yaw_speed_command ], dtype=np.float32) print(f'Input command: {command}') # IMU data exactly like C++ rpy = np.array([ self.robot_data_.imu_data_[2], # roll self.robot_data_.imu_data_[1], # pitch self.robot_data_.imu_data_[0] # yaw ], dtype=np.float32) * 1.0 gyro = np.array([ self.robot_data_.imu_data_[3], self.robot_data_.imu_data_[4], self.robot_data_.imu_data_[5] ], dtype=np.float32) * self.ang_vel_scale_ # Construct proprio observation exactly like C++ # proprio << command, (joint_pos - default) * scale, joint_vel * scale, last_actions, gyro, rpy joint_start_idx = 35 - self.motor_num_ # Same as C++ joint_pos = ( self.robot_data_.q_a_[joint_start_idx:joint_start_idx + 12].astype(np.float32) - self.default_joint_angles_ ) * self.dof_pos_scale_ joint_vel = ( self.robot_data_.q_dot_a_[joint_start_idx:joint_start_idx + 12].astype(np.float32) ) * self.dof_vel_scale_ # Concatenate exactly like C++ proprio = np.concatenate([ command, # 5 elements joint_pos, # 12 elements joint_vel, # 12 elements self.last_actions_, # 12 elements gyro, # 3 elements rpy # 3 elements ]) # Total: 47 elements # History buffer management exactly like C++ if self.is_first_obs_: for i in range(self.num_hist_): start_idx = i * self.obs_size_ end_idx = start_idx + self.obs_size_ self.proprio_hist_buf_[start_idx:end_idx] = proprio self.is_first_obs_ = False else: # Shift history: head((num_hist-1)*obs_size) = tail((num_hist-1)*obs_size) shift_size = (self.num_hist_ - 1) * self.obs_size_ self.proprio_hist_buf_[:shift_size] = self.proprio_hist_buf_[self.obs_size_:] self.proprio_hist_buf_[shift_size:] = proprio # Clip observations exactly like C++ self.observations_ = np.clip(self.proprio_hist_buf_, -self.clip_obs_, self.clip_obs_) def compute_actions(self): """使用ONNX模型计算动作 - 与C++版本完全一致""" if self.ort_session_ is None: return try: # Prepare input tensor input_data = self.observations_.reshape(1, -1).astype(np.float32) # ONNX inference input_name = self.ort_session_.get_inputs()[0].name outputs = self.ort_session_.run(None, {input_name: input_data}) # Extract and clip actions exactly like C++ output_data = outputs[0][0] for i in range(self.action_num_): self.actions_[i] = np.clip(output_data[i], -self.clip_act_, self.clip_act_) if self.is_first_action_: print("[FSMStateMLP-ONNX] First Observation:") for i in range(self.obs_size_): print(f"{self.observations_[i]:.6f} ", end="") print() self.is_first_action_ = False except Exception as e: print(f"[FSMStateMLP] ONNX Runtime inference error: {e}") def on_exit(self): """退出MLP状态""" print("[FSMStateMLP] exit") def check_transition(self, flag: XboxFlag) -> FSMStateName: """检查状态转换""" if flag.fsm_state_command == "gotoSTOP": return FSMStateName.STOP elif flag.fsm_state_command == "gotoMLP": return FSMStateName.MLP # elif flag.fsm_state_command == "gotoMLPH": # return FSMStateName.MLPH # elif flag.fsm_state_command == "gotoMLPREF": # return FSMStateName.MLPREF # elif flag.fsm_state_command == "gotoMLP1": # return FSMStateName.MLP1 elif flag.fsm_state_command == "gotoZERO": return FSMStateName.ZERO # elif flag.fsm_state_command == "gotoSTANDUP": # return FSMStateName.STANDUP # elif flag.fsm_state_command == "gotoGETUP": # return FSMStateName.GETUP # elif flag.fsm_state_command == "gotoAMP": # return FSMStateName.AMP # elif flag.fsm_state_command == "gotoMLPHA": # return FSMStateName.MLPHA else: return None # 无状态转换
10-21
改写一下这段代码,使得寄存器地址支持16bit读写,现在这段是只支持8bit读写://FSM always @ (posedge clk or negedge rst) if (~rst) i2c_state<=3'b000;//idle else i2c_state<= next_i2c_state; //////////Modified on 25 november.write Address is 30H; Read Address is 31H///// always @(i2c_state or stopf or startf or cnt or sft or sadr or hf or scl_neg or cnt) case(i2c_state) 3'b000: //This state is the initial state,idle state begin if (startf)next_i2c_state<= 3 b001;//start else next_i2c_state <= i2c_state; end 3b001://This state is the device address detect & trigger begin if(stopf)next_i2c_state<=3'b000; else begin if((cnt==4'h9)&&({sft[0],hf} ==2'b00) && (scl_neg ==1'b1)&&(sadr ==sft[7:1])) next i2c_ state<=3'b010;//write: i2c adderss is 00110000 and ACK is sampled //so {sft[0],hf} is 2'b00 else if ((cnt==4'h9)&&({sft[0],hf} ==2'b10) && (scl_neg ==1'b1)&&(sadr ==sft[7:1])) next i2c_ state<=3'b011;//read:i2c adderss is 00110001 and ACK is sampled //so {sft[0],hf} is 2'b10 else if((cnt ==4'h9) && (scl_neg == 1'b1)) next_ i2c_state<=3 'b000;//when the address accepted does not match the SADR, //the state comes back else next_i2c_state<=i2c_state; end end 3'b010: //This state is the register address detect &&trigger begin if (stopf)next_i2c_state<=3'b000; else if (startf)next_i2c_state<=3'b001; else if ((cnt ==4'h9) && (scl_neg == 1'b1)) next_i2c _state<=3'b10 else next i2c_state<=i2c_state; end 3'b011: //This state is the register data read begin if (stopf)next_i2c _state<=3'b000; else if (startf) next_i2c _state<=3'b001; else next_12c_state<=i2c_state; end 3'b100: //This state is the register data write begin if (stopf)next_i2c _state<=3'b000; else if (startf) next_i2c _state<=3b001; else next_i2c_state<=i2c_state; end default://safe mode control next_i2c_state <= 3'b000; endcase
06-09
您好帮我用verilog改下一下这段状态机的代码,现在支持寄存器8bit读写,改写后使得支持寄存器16bit读写,分为高八位低八位,需要用代码加一段状态机还有高八位完了之后有一个ACK响应位: //FSM always @ (posedge clk or negedge rst) if (~rst) i2c_state<=3'b000;//idle else i2c_state<= next_i2c_state; //////////Modified on 25 november.write Address is 30H; Read Address is 31H///// always @(i2c_state or stopf or startf or cnt or sft or sadr or hf or scl_neg or cnt) case(i2c_state) 3'b000: //This state is the initial state,idle state begin if (startf)next_i2c_state<= 3 b001;//start else next_i2c_state <= i2c_state; end 3b001://This state is the device address detect & trigger begin if(stopf)next_i2c_state<=3'b000; else begin if((cnt==4'h9)&&({sft[0],hf} ==2'b00) && (scl_neg ==1'b1)&&(sadr ==sft[7:1])) next i2c_ state<=3'b010;//write: i2c adderss is 00110000 and ACK is sampled //so {sft[0],hf} is 2'b00 else if ((cnt==4'h9)&&({sft[0],hf} ==2'b10) && (scl_neg ==1'b1)&&(sadr ==sft[7:1])) next i2c_ state<=3'b011;//read:i2c adderss is 00110001 and ACK is sampled //so {sft[0],hf} is 2'b10 else if((cnt ==4'h9) && (scl_neg == 1'b1)) next_ i2c_state<=3 'b000;//when the address accepted does not match the SADR, //the state comes back else next_i2c_state<=i2c_state; end end 3'b010: //This state is the register address detect &&trigger begin if (stopf)next_i2c_state<=3'b000; else if (startf)next_i2c_state<=3'b001; else if ((cnt ==4'h9) && (scl_neg == 1'b1)) next_i2c _state<=3'b10 else next i2c_state<=i2c_state; end 3'b011: //This state is the register data read begin if (stopf)next_i2c _state<=3'b000; else if (startf) next_i2c _state<=3'b001; else next_12c_state<=i2c_state; end 3'b100: //This state is the register data write begin if (stopf)next_i2c _state<=3'b000; else if (startf) next_i2c _state<=3b001; else next_i2c_state<=i2c_state; end default://safe mode control next_i2c_state <= 3'b000; endcase
06-13
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值