Verilog单周期CPU开发:Always块等待输入时如何设置延迟?
Verilog多周期CPU指令读取问题分析与修复
你的代码核心问题
- 阻塞赋值误用:时序逻辑
always块里全用了=阻塞赋值,这会导致信号在同一时钟周期内立即更新,引发竞争冒险,硬件时序逻辑必须用<=非阻塞赋值。 - 非法阻塞等待:
repeat(25) @(posedge i_clk);是纯仿真的行为级写法,完全无法综合成实际硬件。硬件里的延迟等待必须用状态机+计数器实现,不能用这种阻塞式循环。 - 复位逻辑缺失:整个
always块没处理negedge i_rst_n复位信号,复位时寄存器(比如ins_next_addr、o_finish_wire)没有初始化,导致上电或复位后状态混乱。 - 终止条件错误:你判断的是
0xFFFFFFFF,但需求是遇到0xEEEEEEEE时终止,条件不匹配。 - 指令内存交互逻辑错误:
o_i_valid_addr_wire一直置1,多数指令内存需要的是脉冲式有效信号(仅在发送地址的周期置1),而非持续高电平;且发送地址后直接阻塞等待,导致后续地址无法正常发送,第一条指令的处理直接卡死。
修复方案:用状态机实现多周期流程
因为指令内存需要5个周期返回指令,这已经不属于严格的单周期CPU架构,必须用多周期状态机控制流程。核心是划分状态流转:
- IDLE:复位后初始状态,准备发送第一条指令地址
- SEND_ADDR:发送指令地址并拉有效信号
- WAIT_INST:计数5个时钟周期,等待指令返回
- DECODE_EXECUTE:接收指令、解码执行,判断是否终止,准备下一条地址
修正后的代码示例:
// 定义状态枚举 localparam IDLE = 2'b00; localparam SEND_ADDR = 2'b01; localparam WAIT_INST = 2'b10; localparam DECODE_EXECUTE= 2'b11; reg [1:0] current_state; reg [2:0] wait_cnt; // 计数5个周期(0-4对应5个时钟沿) always @(posedge i_clk or negedge i_rst_n) begin if(!i_rst_n) begin // 复位初始化所有寄存器 current_state <= IDLE; ins_next_addr <= 32'd0; o_i_addr_wire <= 32'd0; o_i_valid_addr_wire <= 1'b0; o_finish_wire <= 1'b0; wait_cnt <= 3'd0; // 其他解码寄存器初始化 rs1 <= 5'd0; rs2 <= 5'd0; write_reg <= 5'd0; // ... 其余信号同理 end else begin case(current_state) IDLE: begin o_finish_wire <= 1'b0; current_state <= SEND_ADDR; end SEND_ADDR: begin // 发送地址+有效脉冲 o_i_addr_wire <= ins_next_addr; o_i_valid_addr_wire <= 1'b1; current_state <= WAIT_INST; wait_cnt <= 3'd0; end WAIT_INST: begin o_i_valid_addr_wire <= 1'b0; // 拉低有效信号(若内存需要持续高电平则保留1'b1) if(wait_cnt == 3'd4) begin // 完成5个周期等待 current_state <= DECODE_EXECUTE; end else begin wait_cnt <= wait_cnt + 1'b1; current_state <= WAIT_INST; end end DECODE_EXECUTE: begin // 终止判断:匹配需求的0xEEEEEEEE if(i_i_inst_reg == 32'hEEEEEEEE) begin o_finish_wire <= 1'b1; current_state <= IDLE; // 终止后回到空闲状态 end else begin // 解码操作 rs1 <= i_i_inst_reg[19:15]; rs2 <= i_i_inst_reg[24:20]; write_reg <= i_i_inst_reg[11:7]; read_data1 <= register[rs1]; read_data2 <= register[rs2]; imm_val <= i_i_inst_reg[31:20]; funct3 <= i_i_inst_reg[14:12]; opcode <= i_i_inst_reg[6:0]; // 更新PC,准备下一条指令 ins_next_addr <= ins_next_addr + 32'd4; current_state <= SEND_ADDR; end end endcase end end
额外注意事项
- 确保
i_i_inst_reg是同步寄存器:需要单独写一个always块在posedge i_clk下把i_i_inst赋值给i_i_inst_reg,避免亚稳态。 - 指令内存的
o_i_valid_addr_wire接口要匹配:如果指令内存要求有效信号持续高电平直到指令返回,那就在WAIT_INST阶段保持1'b1即可。 - 单周期CPU的定义是一条指令在一个时钟周期内完成,但你的需求中指令内存延迟5个周期,必须改成多周期架构,这是需求决定的。
内容的提问来源于stack exchange,提问作者William Chen
相关产品推荐
相关产品推荐

