指令
RISC-V 的基础指令分为RISU四种类型。基于对立即数的处理,还有两个指令格式的变体BJ。
 
R-Type 寄存器类型
R-type 的 OPCODE 均为0010011。指令分类,根据funct3先进行一次区分,再根据funct7进行一次判断。
 
比如,对于ADD/SUB/MUL,其funct3均为3'h0。因此,还需要再对funct7进行判断:
| 12
 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]:
| 12
 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不一致。
| 12
 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 字节的倍数编码符号偏移量。偏移量是符号扩展的,加到分支指令的地址上以给出目标地址。
 
| 12
 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中。
| 12
 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 字节的倍数编码一个有符号的偏移量。偏移量是符号扩展的,加到当前跳转指令的地址上以形成跳转目标地址。
| 12
 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: beginbranch_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都不同。