~ruther/verilog-riscv-semestral-project

ref: e3c95ad31853db8d3df0f933168a2f8ad6dd370c verilog-riscv-semestral-project/src/instruction_decoder.sv -rwxr-xr-x 5.3 KiB
e3c95ad3 — Rutherther feat: add instruction decoder 1 year, 5 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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