指令

RISC-V 的基础指令分为RISU四种类型。基于对立即数的处理,还有两个指令格式的变体BJ。

显式立即数的 RISC-V 基础指令格式

R-Type 寄存器类型

R-type 的 OPCODE 均为0010011。指令分类,根据funct3先进行一次区分,再根据funct7进行一次判断。

R-type指令构成

比如,对于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 // R-Type
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的指令格式 移位指令的指令格式,注意高七位的数字。

因此,对于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 // I-Type
branch_cancel <= 0;
jalr_cancel <= 0;
rd_ptr_out <= rd;
imm_val_out <= {4'b0, inst_in[31:20]}; // 拼接为16位的立即数
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 一致,OPCODE0000011,存储指令则是 S-Type ,OPCODE0100011

加载与存储指令格式

其中,为了保持funct3rs1的位置不变,存储指令的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 // LOAD
branch_cancel <= 0;
jalr_cancel <= 0;
rd_ptr_out <= rd;
imm_val_out <= {20'b0,inst_in[31:25],inst_in[11:7]}; // 拼接为32位的立即数
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 // STORE
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 字节的倍数编码符号偏移量。偏移量是符号扩展的,加到分支指令的地址上以给出目标地址。

B-Type指令格式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
`OPCODE_BTYPE: begin // Branch
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 。LUIOPCODE0110111,而AUIPCOPCODE0010111

U-Type指令格式

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 // U-Type
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

这里之所以要分开是因为JALJALR的指令格式与OPCODE都不同。