为何我的FSM无法生成组合逻辑?Latch与非组合逻辑错误排查
FSM模块锁存器与非组合逻辑问题排查及修正
核心问题分析
你的FSM代码出现锁存器和非组合逻辑错误,主要源于以下三点:
- 输出信号未全路径赋值:部分状态分支仅给少数信号赋值,其余信号未明确设置,综合器会生成锁存器保持之前的值,违反组合逻辑要求。
- 信号多重驱动冲突:
CU_INT_TAKEN_FSM同时被assign语句和always_comb块赋值,属于非法多重驱动。 - case分支匹配逻辑错误:
ST_EXEC内的多值匹配用了按位或运算(|),实际会生成单一值而非匹配多个OP类型。
修正方案
1. 组合逻辑块添加默认赋值
在always_comb块开头,先给所有输出信号和NS赋默认值,确保每个信号在所有执行路径都有明确赋值,彻底消除锁存器。
2. 解决多重驱动问题
移除多余的assign语句,将CU_INT_TAKEN_FSM的赋值统一放在always_comb块的默认值中,根据需求在分支中覆盖。
3. 修正case多值匹配逻辑
将多OP类型的匹配用逗号分隔多个条目,替代错误的按位或运算。
修正后的完整代码
module ControlUnit( input CU_RST, input CU_INTR, input [6:0] CU_OP_TYPE, input [2:0] CU_FUNCT3, input CU_OP3, input CU_CLK, input CU_INT_TAKEN_DCDR, input CU_BReq, input CU_BRlt, input CU_ltu, output logic CU_PCWrite, output logic CU_regWrite, output logic CU_memWE2, output logic CU_memRDEN1, output logic CU_memRDEN2, output logic CU_reset, output logic CU_csr_WE, output logic CU_INT_TAKEN_FSM, output logic CU_mret_exec, output logic [3:0] CU_alu_fun, output logic [1:0] CU_alu_srcA, output logic [2:0] CU_alu_srcB, output logic [2:0] CU_pcSource, output logic [1:0] CU_rf_wr_sel ); // FSM状态定义 typedef enum{ST_INIT, ST_FETCH, ST_EXEC, ST_WB} state_type; state_type PS, NS; // 状态寄存器更新 always_ff@(posedge CU_CLK) begin if(CU_RST) PS <= ST_INIT; else PS <= NS; end always_comb begin // 所有信号先赋默认值,避免锁存器生成 CU_PCWrite = 0; CU_regWrite = 0; CU_memWE2 = 0; CU_memRDEN1 = 0; CU_memRDEN2 = 0; CU_reset = 0; CU_csr_WE = 0; CU_INT_TAKEN_FSM = CU_INT_TAKEN_DCDR; // 统一默认赋值,避免多重驱动 CU_mret_exec = 0; NS = ST_FETCH; // 默认下一状态 case(PS) ST_INIT: // 初始化状态 begin CU_reset = 1; CU_PCWrite = 0; NS = ST_FETCH; end ST_FETCH: // 取指状态 begin CU_memRDEN1 = 1; CU_PCWrite = 0; CU_regWrite = 0; NS = ST_EXEC; end ST_EXEC: // 执行状态 begin NS = ST_FETCH; case(CU_OP_TYPE) // 匹配多种OP类型,用逗号分隔 7'b0110011, 7'b0010011, 7'b1100111, 7'b1101111, 7'b0110111, 7'b0010111: begin CU_PCWrite = 1; CU_regWrite = 1; end 7'b0000011: // 加载指令 begin CU_memRDEN2 = 1; NS = ST_WB; end 7'b0100011: // 存储指令 begin CU_PCWrite = 1; CU_memWE2 = 1; end 7'b1100011: // 分支指令 begin CU_PCWrite = 1; end default: begin CU_PCWrite = 1; end endcase end ST_WB: // 写回状态 begin CU_PCWrite = 1; CU_regWrite = 1; NS = ST_FETCH; end default: begin CU_PCWrite = 1; CU_regWrite = 1; NS = ST_FETCH; end endcase end // 译码器模块 always_comb begin CU_alu_fun = 4'b0000; CU_alu_srcA = 2'b00; CU_alu_srcB = 3'b000; CU_pcSource = 3'b000; CU_rf_wr_sel = 2'b00; case(CU_OP_TYPE) 7'b0110011: // R类型指令 begin CU_alu_srcA = 2'b00; CU_alu_srcB = 3'b000; CU_pcSource = 3'b000; CU_rf_wr_sel = 2'b11; case(CU_FUNCT3) 3'b010: CU_alu_fun = 4'b0010; // slt 3'b100: CU_alu_fun = 4'b0001; // xor(修正为4位规范值) default: CU_alu_fun = 4'b0000; // add endcase end 7'b0010011: // 类R型I指令 begin CU_alu_srcA = 2'b00; CU_alu_srcB = 3'b001; CU_pcSource = 3'b000; CU_rf_wr_sel = 2'b11; case(CU_FUNCT3) 3'b000: CU_alu_fun = 4'b0000; // addi 3'b001: CU_alu_fun = 4'b0001; // slli endcase end 7'b0110111: // lui指令 begin CU_alu_fun = 4'b1001; CU_alu_srcA = 2'b01; CU_alu_srcB = 3'b000; CU_pcSource = 3'b000; CU_rf_wr_sel = 2'b11; end 7'b1100011: // B类型分支指令 begin CU_pcSource = 3'b010; end default: begin CU_alu_fun = 4'b0000; CU_alu_srcA = 2'b00; CU_alu_srcB = 3'b000; CU_pcSource = 3'b000; CU_rf_wr_sel = 2'b00; end endcase end endmodule
额外细节修正
- 修正了解码器中
xor操作的CU_alu_fun赋值为4位规范值(原代码写4'b000,语法允许但不规范)。 - 所有状态分支仅覆盖需要修改的信号,默认值保证其余信号的正确性。
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

