~ruther/verilog-riscv-semestral-project

e3c95ad31853db8d3df0f933168a2f8ad6dd370c — Rutherther 1 year, 5 months ago 51a684d
feat: add instruction decoder
1 files changed, 171 insertions(+), 0 deletions(-)

A src/instruction_decoder.sv
A src/instruction_decoder.sv => src/instruction_decoder.sv +171 -0
@@ 0,0 1,171 @@
module instruction_decoder(
  input [31:0]     instruction,

  // whether to use memory as a source, instead of alu
  output reg       load_memory,
  output reg       store_memory,

  // TODO: implement
  // mask to use for halfwords, bits, etc.
  // used when loading memory, storing memory,
  // registers...
  output [31:0]    mask,

  // put alu_jump to alu if conditional_jump
  //
  // if unconditional_jump, source of pd is alu
  // elsif conditional_jump, source of pd is pd + imm
  // else source of pd is pd + 4
  //
  // if store pd => rd = pd + 4

  // inputs for alu, in case instruction is not conditional_jump
  output [2:0]     alu_reg_op, // the operation selection for alu
  output           alu_reg_add_one, //  whether to add one to rs2 (may be used for two's complement)
  output           alu_reg_negate, // whether to negate rs2 (may be used for two's complement)
  output           alu_reg_signed, // whether the operation for alu is signed
  output reg       load_pc, // should load pc to alu #1
  output reg       store_pc, // should store pc + 4

  output reg       unconditional_jump, // jump, always. To alu output.

  output reg       conditional_jump, // should jump if alu zero_flag correct
  output reg       alu_jump_op, // operation for alu for conditional jumps
  output reg       alu_jump_add_one, // add one for conditional jumps
  output reg       jump_negate_zero, // whether to negate zero flag from alu

  // whether to use immediate instead of rs2.
  // if false, immediate still may be added to second operand
  output           use_immediate,
  output [31:0]    immediate,

  // inputs to register file
  output [4:0]     reg_rs1,
  output reg [4:0] reg_rs2,
  output [4:0]     reg_rd,
  output           reg_we
);
  typedef enum bit[2:0] {Unknown, R, I, S, SB, U, UJ} instruction_type_type;
  instruction_type_type instruction_type;

  wire [2:0]    funct3;
  wire [6:0]    funct7;
  wire [6:0]    opcode;

  assign funct3 = instruction[14:12];
  assign funct7 = instruction[31:25];
  assign opcode = instruction[6:0];

  assign reg_rs1 = instruction[19:15];
  assign reg_rd = instruction[11:7];

  always_comb begin
    case (instruction_type)
      I : immediate = {20'b0, instruction[31:20]};
      S : immediate = {20'b0, instruction[31:25], instruction[11:7]};
      SB : immediate = {19'b0, instruction[31], instruction[7], instruction[30:25], instruction[11:8], 1'b0};
      U : immediate = {instruction[31:12], 12'b0};
      UJ : immediate = {11'b0, instruction[31], instruction[19:12], instruction[20], instruction[30:21], 1'b0};
      default: immediate = 32'b0;
    endcase
  end

  always_comb begin
    alu_reg_add_one = 1'b0;
    alu_reg_negate = 1'b0;
    if (funct3 == 0 && funct7[5] == 1) begin
      // subtraction
      alu_reg_add_one = 1'b1;
      alu_reg_negate = 1'b1;
    end
  end

  always_comb begin
    if (instruction_type == I ||
        instruction_type == U ||
        instruction_type == UJ ||
        instruction_type == S ||
        instruction_type == SB) begin
      // TODO: does this make sense? is putting 0s to rs2 really needed? imm should
      // be used always in this case
      reg_rs2 = 5'b00000;
      use_immediate = 1;
    end
    else begin
      reg_rs2 = instruction[24:20];
      use_immediate = 0;
    end
  end

  assign alu_reg_op = funct3;
  assign alu_reg_signed = funct7[5];

  assign mask = 32'b1;   // TODO (word, bit instructions, etc.)

  always_comb begin
     case (opcode[6:2])
      5'b00000 : instruction_type = I;
      5'b00011 : instruction_type = I; // fence
      5'b00100 : instruction_type = I;
      5'b00101 : instruction_type = U; // auipc
      5'b00110 : instruction_type = I;
      5'b01000 : instruction_type = S;
      5'b01100 : instruction_type = R;
      5'b01101 : instruction_type = U; // lui
      5'b01110 : instruction_type = R;
      5'b11000 : instruction_type = SB;
      5'b11001 : instruction_type = I; // jalr
      5'b11011 : instruction_type = UJ; // jal
      5'b11100 : instruction_type = I;
      default  : instruction_type = Unknown;
    endcase;
  end;

  always_comb begin
    store_memory = 1'b0;
    load_memory = 1'b0;
    load_pc = 1'b0;
    store_pc = 1'b0;
    reg_we = 1'b1;
    conditional_jump = 1'b0;
    unconditional_jump = 1'b0;

    // TODO: support mask
    // TODO: multiplication
    // NOTE: ecall, ebreak, CSRRW, CSRRS, SCRRC, CSRRWI, CSRRSI, CSRRCI unsupported
    case (opcode[6:2])
      5'b01100 : reg_we = 1'b1;
      5'b00100 : reg_we = 1'b1;
      5'b00000 : load_memory = 1'b1;
      5'b01000 : begin
        store_memory = 1'b1;
        reg_we = 1'b0;
      end
      5'b11000 : begin // branches
        conditional_jump = 1'b1;
        reg_we = 1'b0;
      end
      5'b11011 : begin // jump and link
        load_pc = 1'b1; // relative to pc
        unconditional_jump = 1'b1; // jump
        store_pc = 1'b1; // link #1
        reg_we = 1'b1; // link #2
      end
      5'b11001 : begin // jump and link register
        load_pc = 1'b1; // relative to pc
        unconditional_jump = 1'b1; // jump
        store_pc = 1'b1; // link #1
        reg_we = 1'b1; // link #2
      end
      5'b01101 : begin // load upper imm
        reg_we = 1'b1;
      end
      5'b00101 : begin // add upper imm to PC
        load_pc = 1'b1;
        reg_we = 1'b1;
      end
      default : ;
    endcase;
  end;

endmodule

Do not follow this link