Day 85: ChipX core: single-cycle RV32I (part 2 — control)
ChipX core: single-cycle control (part 2)
Add the control unit (Stage 1, Day 41): a combinational block that decodes opcode/funct3/funct7 into reg_write, alu_src, alu_op, mem_read/mem_write, mem_to_reg, and branch/jump selects. This is the control table you filled in on paper, now a case statement — with defaults assigned first (Day 59) so no path leaves a signal unset.
always @(*) begin
// defaults (no inferred latch, safe fall-through)
{reg_write, alu_src, mem_read, mem_write, mem_to_reg, branch, jump} = 0;
alu_op = ALU_ADD;
case (opcode)
7'b0110011: begin reg_write=1; alu_op=decode_r(funct3,funct7); end // R
7'b0010011: begin reg_write=1; alu_src=1; alu_op=decode_i(funct3); end // I
7'b0000011: begin reg_write=1; alu_src=1; mem_read=1; mem_to_reg=1; end // load
7'b0100011: begin alu_src=1; mem_write=1; end // store
7'b1100011: begin branch=1; alu_op=ALU_SUB; end // branch
7'b1101111: begin reg_write=1; jump=1; end // jal
// ... jalr, lui, auipc ...
endcase
endDefaults first, always
Because control is a wide combinational block with many outputs, the assign-all-defaults-first habit (Day 59) is essential — miss one signal on one opcode and you get an inferred latch that corrupts control for that instruction. Set every signal to a safe default at the top, then let the case override only what changes.
Progress for Day 85
In the control unit, why assign all control signals default values before the opcode case statement?