指令
RISC-V 的基础指令分为RISU四种类型。基于对立即数的处理,还有两个指令格式的变体BJ。
R-Type 寄存器类型
R-type 的 OPCODE 均为0010011
。指令分类,根据funct3
先进行一次区分,再根据funct7
进行一次判断。
比如,对于ADD/SUB/MUL
,其funct3
均为3'h0
。因此,还需要再对funct7
进行判断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| `OPCODE_RTYPE: begin branch_cancel <= 0; jalr_cancel <= 0; rd_ptr_out <= rd; imm_val_out <= 32'b0; using_imm_out <= 'b0; case (funct3) `FUNCT3_ADD_SUB_MUL: alu_op_out <= inst_in[25]?`ALU_MUL:inst_in[30] ? `ALU_SUB : `ALU_ADD; `FUNCT3_OR_REM: alu_op_out <= inst_in[25]?`ALU_REM:`ALU_OR; `FUNCT3_XOR_DIV: alu_op_out <= inst_in[25]?`ALU_DIV:`ALU_XOR; `FUNCT3_SLL_MULH: alu_op_out <= inst_in[25]?`ALU_MULH:`ALU_SLL; `FUNCT3_SRL_SRA_DIVU: alu_op_out <= inst_in[25]?`ALU_DIVU:inst_in[30] ? `ALU_SRA : `ALU_SRL; `FUNCT3_SLT_MULHSU: alu_op_out <= inst_in[25]?`ALU_MULHSU:`ALU_SLT; `FUNCT3_SLTU_MULHU: alu_op_out <= inst_in[25]?`ALU_MULHU:`ALU_SLTU; `FUNCT3_REMU: alu_op_out <= `ALU_REMU; default:begin alu_op_out <= `ALU_NOP; trapped <= 1; trap_value <= 0; trap_cause <= 2; end endcase end
|
I-Type 立即数类型
I-Type 的 OPCODE 均为0010011
。对于ADDI/ANDI/ORI/XORI
四个指令,其立即数为12位,而对于SLLI/SRLI/SRAI
三个移位指令,其立即数为五位,高七位的值是固定的。
因此,对于ADDI/ANDI/ORI/XORI
这四个指令,先判断OPCODE
,再判断funct3
;对于移位指令,则还需判断立即数高位imm[10]
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| `OPCODE_ITYPE: begin branch_cancel <= 0; jalr_cancel <= 0; rd_ptr_out <= rd; imm_val_out <= {4'b0, inst_in[31:20]}; using_imm_out <= 'b1; case (funct3) `FUNCT3_ADD_SUB_MUL: alu_op_out <= `ALU_ADD; `FUNCT3_OR_REM: alu_op_out <= `ALU_OR; `FUNCT3_XOR_DIV: alu_op_out <= `ALU_XOR; `FUNCT3_SLL_MULH: alu_op_out <= `ALU_SLL; `FUNCT3_SRL_SRA_DIVU: alu_op_out <= inst_in[30] ? `ALU_SRA : `ALU_SRL; `FUNCT3_SLT_MULHSU: alu_op_out <= `ALU_SLT; `FUNCT3_SLTU_MULHU: alu_op_out <= `ALU_SLTU; default:begin alu_op_out <= `ALU_NOP; trapped <= 1; trap_value <= 0; trap_cause <= 2; end endcase end
|
S-Type 存储类型
加载和存储指令在寄存器和内存之间传输一个值。加载指令的指令格式与 I-Type 一致,OPCODE
为0000011
,存储指令则是 S-Type ,OPCODE
为0100011
。
其中,为了保持funct3
与rs1
的位置不变,存储指令的12位立即数被拆分为两段。
这里其实写的不规范,应当合并起来。不过为了方便看,暂且将 S-Type 分为 Store 和 Load,毕竟两个的OPCODE
不一致。
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
| `OPCODE_LTYPE: begin branch_cancel <= 0; jalr_cancel <= 0; rd_ptr_out <= rd; imm_val_out <= {20'b0,inst_in[31:25],inst_in[11:7]}; using_imm_out <= 'b1; case (funct3) `FUNCT3_LB: alu_op_out <= `MEM_LB; `FUNCT3_LBU: alu_op_out <= `MEM_LBU; `FUNCT3_LH: alu_op_out <= `MEM_LH; `FUNCT3_LHU: alu_op_out <= `MEM_LHU; `FUNCT3_LW: alu_op_out <= `MEM_LW; default:begin alu_op_out <= `ALU_NOP; trapped <= 1; trap_value <= 0; trap_cause <= 2; end endcase end
`OPCODE_STYPE: begin case (funct3) `FUNCT3_SB: alu_op_out <= `MEM_SB; `FUNCT3_SH: alu_op_out <= `MEM_SH; `FUNCT3_SW: alu_op_out <= `MEM_SW; default:begin alu_op_out <= `ALU_NOP; trapped <= 1; trap_value <= 0; trap_cause <= 2; end endcase end
|
其中LW
就是 LoadWord ,LH
就是 LoadHalfWord ,LB
就是 LoadByte 。
B-Type 分支类型
所有的条件分支指令使用 B-Type 指令格式,12 位 B 立即数以 2 字节的倍数编码符号偏移量。偏移量是符号扩展的,加到分支指令的地址上以给出目标地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| `OPCODE_BTYPE: begin if(jalr_cancel)jalr_cancel <= 0; rd_ptr_out <= rd; imm_val_out <= {{21{inst_in[31]}}, inst_in[7], inst_in[30:25], inst_in[11:8], 1'b0}; using_imm_out <= 'b1; alu_op_out <= `ALU_NOP; case (funct3) `FUNCT3_BEQ: branch_cancel <= rs1_val == rs2_val ; `FUNCT3_BNE: branch_cancel <= rs1_val != rs2_val ; `FUNCT3_BLT: branch_cancel <= $signed(rs1_val) < $signed(rs2_val) ; `FUNCT3_BGE: branch_cancel <= $signed(rs1_val) >= $signed(rs2_val) ; `FUNCT3_BLTU: branch_cancel <= rs1_val < rs2_val ; `FUNCT3_BGEU: branch_cancel <= rs1_val >= rs2_val ; default:begin alu_op_out <= `ALU_NOP; trapped <= 1; trap_value <= 0; trap_cause <= 2; end endcase end
|
U-Type 上部立即数类型
上部立即数类型的指令将高位立即数加载到寄存器中,或基于高位立即数进行地址计算。
一共有两条指令属于 U-Type 。LUI
的OPCODE
为0110111
,而AUIPC
的OPCODE
为0010111
。
LUI
把 32 位的上部立即数放进目标寄存器rd
中,并把最低的 12 位填充为零。该 32 位结果被符号扩展到 64 位。
AUIPC
从上部立即数形成 32 位偏移量,并把最低的 12 位填充为零,把结果符号扩展到 64 位,把它加到AUIPC
指令的地址,然后把结果放进寄存器rd
中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| `OPCODE_UTYPE: begin branch_cancel <= 0; jalr_cancel <= 0; rd_ptr_out <= rd; imm_val_out <= {{12{inst_in[31]}},inst_in[31:12]}; using_imm_out <= 'b1; alu_op_out <= `ALU_ADD; ready_go_q <= 1'b1; rd_valid <= 1'b1; csr_valid <= 1'b0;
mem_en <= 0; id_is_load <= 1'b0; end
|
J-Type 跳转类型
J-Type 指令是无条件跳转指令。J 立即数以 2 字节的倍数编码一个有符号的偏移量。偏移量是符号扩展的,加到当前跳转指令的地址上以形成跳转目标地址。
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
| `OPCODE_JTYPE: begin branch_cancel <= 0; jalr_cancel <= 0; rd_ptr_out <= rd; imm_val_out <= pc_in; using_imm_out <= 'b1; alu_op_out <= `ALU_RIGHT; ready_go_q <= 1'b1; rd_valid <= 1'b0; csr_valid <= 1'b0;
mem_en <= 0; id_is_load <= 1'b0; end `OPCODE_JALR: begin branch_cancel <= 0; rd_ptr_out <= (rd == 0) ? 1 : rd; jalr_addr <= rs1_val + {{20{inst_in[31]}},inst_in[31:20]}; imm_val_out <= pc_in; using_imm_out <= 'b1; alu_op_out <= `ALU_RIGHT; jalr_cancel <= 1'b1; ready_go_q <= 1'b1; rd_valid <= 1'b1; csr_valid <= 1'b0;
mem_en <= 0; id_is_load <= 1'b0; end
|
这里之所以要分开是因为JAL
和JALR
的指令格式与OPCODE
都不同。