PicoRV32 中实现压缩指令集选项 COMPRESSED_ISA,当设置COMPRESSED_ISA=1开启支持16位指令集。压缩指令有很多优点,当我们在FPGA中实现PicoRV32的时候,使用RISCV的C扩展能有效的增大代码密度,原本32位1条指令变为16位一条指令,用更少的空间存储更多的指令。
RISCV C使用一种简单的压缩方案,以便在下列情形时,提供更短的 16 位版本的 32 位 RISC-V
指令:
立即数或者地址偏移量较小时
其中一个寄存器是零寄存器(x0)、ABI 链接寄存器(x1)或者 ABI 栈寄存器(x2)
目标寄存器和第一个源寄存器相同
最常见情况下使用了 8 个寄存器
C 扩展与其它所有标准扩展兼容。C 扩展允许 16 位指令可以自由地和 32 位指令混合执行,并运行 32 位指令可以在任何 16 位边界开始。
(注:一般情况下, 32 位指令必须对齐到 32 位存储器地址边界上,否则会导致非对齐存储器访问异常。 )
1.压缩RVC 16指令格式
说明 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
CR | funct4 | RD/RS1 | RS2 | OP | ||||||||||||
CI | funct3 | IMM | RD/RS1 | IMM | ||||||||||||
CSS | IMM | RS2 | ||||||||||||||
CIW | IMM | RD' | ||||||||||||||
CL | IMM | RS' | IMM | |||||||||||||
CS | ||||||||||||||||
CB | offset | offset | ||||||||||||||
CJ | jump target | |||||||||||||||
寄存器编号
000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 |
x8 | x9 | x10 | x11 | x12 | x13 | x14 | x15 |
2.Load和store指令
基于栈指针 ,栈指针为x2
指令使用 CI 格式
C.LWSP 指令将一个 32 位数值从存储器读入寄存器 rd 中。其有效地址的计算是通过将
零 扩展的偏移量*4,然后加上栈指针 x2 形成的。它被扩展为 lw rd, offset[7:2](x2)指令。
指令使用 CSS 格式
C.SWSP 指令将寄存器 rs2 中的 32 位值保存到存储器中。其有效地址的计算是通过将 零
扩展的偏移量*4,然后加上栈指针 x2 形成的。它被扩展为 sw rs2, offset[7:2](x2)指令。
基于寄存器
指令使用 CL 格式。
C.LW 指令将一个 32 位数值从存储器读入寄存器 rd’中。其有效地址的计算是通过将 零
扩展的偏移量*4,然后加上寄存器 rs1’中的基址形成的。它被扩展为 lw rd’, offset[6:2](rs1’)
指令。
指令使用 CS 格式。
C.SW 指令将寄存器 rs2’中的 32 位值保存到存储器中。其有效地址的计算是通过将 零 扩
展的偏移量*4,然后加上寄存器 rs1’中的基址形成的。它被扩展为 sw rs2’, offset[6:2](rs1’)
指令。
3.控制转移指令
RVC 提供了无条件跳转指令和条件分支指令。如同基本 RVI 指令一样,所有 RVC 控制转
移指令的偏移量都是 2 字节的倍数。
这些指令使用 CJ 格式。
C.J 指令执行一个无条件控制转移。偏移量被符号扩展后,与 pc 相加形成跳转目标地址。
C.J 指令因此可以在±2KB 范围内进行跳转。C.J 指令被扩展为 jal x0, offset[11:1]。
C.JAL 指令是一条 RV32C 仅有指令,它执行与 C.J 指令相同的操作,但是它还将在跳转
指令后的指令地址(pc+2)写入到链接寄存器 x1 中。 C.JAL 指令被扩展为 jal x1, offset[11:1]。
这些指令使用 CR 格式。
C.JR(jump register)指令执行一个无条件控制转移到寄存器 rs1 的地址。C.JR 指令被扩
展为 jalr x0, rs1, 0。
C.JALR(jump and link register)指令执行与 C.JR 指令相同的操作,但是它还将在跳转指
令后的指令地址(pc+2)写入到链接寄存器 x1 中。C.JALR 指令被扩展为 jalr x1, rs1, 0。
注意:严格来说, C.JALR 指令并没有被精确地扩展为基本 RVI 指令,因为那个被
加到 pc 上以形成链接地址的值是 2 ,而不是基本 ISA 中的 4 ,但是同时支持偏
移量 2 个字节和 4 个字节,只对微体系结构产生微小的影响。
这些指令使用 CB 格式。
C.BEQZ 指令执行条件控制转移。偏移量被符号扩展后,与 pc 相加形成跳转目标地址。
C.BEQZ 指令因此可以在±256B 范围内进行跳转。如果寄存器 rs1’的值是 0,则 C.BEQZ 指令产生控制转移(take the branch)。这条指令被扩展为 beq rs1’, x0, offset[8:1]。
C.BNEZ 指令定义相似,只是当寄存器 rs1’的值是非 0 值,则指令产生控制转移(take the branch)
。这条指令被扩展为 bne rs1’, x0, offset[8:1]。
4.整数计算指令
整数常数生成指令
两条常数-生成指令都使用 CI 格式,并且可以以任何整数寄存器为目标。
C.LI 指令将符号扩展的 6 位立即数 imm,写入寄存器 rd 中。C.LI 指令仅在 rd≠x0 时才是有效的。C.LI 指令被扩展为 addi rd, x0, imm[5:0]。
C.LUI 指令将非零的 6 位立即数写入到目标寄存器的 17-12 位,并将目标寄存器的低 12位清零,然后将第 17 位符号扩展到整个目标寄存器的高位部分。 C.LUI 寄存器仅在 rd≠{x0, x2}且立即数不等于 0 时才是有效的。C.LUI 指令被扩展为 lui rd, nzimm[17:12]。
整数寄存器,立即数指令
这些整数寄存器-立即数指令都使用 CI 格式,并在认为非 x0 整数寄存器和一个 6 位立即数之间进行操作。立即数不能为 0。
C.ADDI 指令将非零的、符号扩展的 6 位立即数加到寄存器 rd 的值上,将结果写入 rd。
C.ADDI 指令被扩展为 addi rd, rd, nzimm[5:0]。
C.ADDI16SP 指令的操作码与 C.LUI 指令相同,但是使用 x2 作为目标寄存器。 C.ADDI16SP指令将一个非零的、符号扩展的 6 位立即数加到栈指针寄存器(sp=x2)上,此处立即数被放大 16 倍,其范围为(-512,496)。C.ADDI16SP 指令用于在过程的头部和尾部对栈指针进行调整。它被扩展为 addi x2, x2, nzimm[9:4]。
C.ADDI4SPN 指令是一条 CIW 格式的、RV32C/RV64C 仅有的指令,它将一个零扩展的、非零立即数,乘以 4,然后加到栈指针 x2 上,并将结果写入 rd’。这条指令用于产生指向分配在栈中的变量的指针,它被扩展为 addi rd’, x2, zimm[9:2]。
C.SLLI 指令是一条 CI 格式的指令,它对寄存器 rd 中的数值进行逻辑左移操作,并将结果写入 rd。移位次数被编码到 shamt 字段,此处对 RV32C,shamt[5]必须为 0。对于RV32C/RV64C,移位次数必须为非零值。对于 RV128C,一个 shamt 为 0,编码为移位 64 次。C.SLLI 指令被扩展为 slli rd, rd, shamt[5:0],除了对于 RV128C 且 shamt=0,则被扩展为 slli rd, rd,64。
C.SRLI 指令是一条 CB 格式的指令,它对寄存器 rd’中的数值进行逻辑右移操作,并将结果写入 rd’。移位次数被编码到 shamt 字段,此处对 RV32C,shamt[5]必须为 0。对于RV32C/RV64C,移位次数必须为非零值。对于 RV128C,一个 shamt 为 0,编码为移位 64 次。而且对于 RV128C,移位次数是符号扩展的,因此合法的移位次数是 1-31、 64、 96-127。
C.SRLI指令被扩展为 srli rd’, rd’, shamt[5:0],除了对于 RV128C 且 shamt=0,则被扩展为 srli rd’, rd’, 64。
C.SRAI 指令与 C.SRLI 指令相似,不过它执行一个算术右移操作。 C.SRAI 指令被扩展为 srai rd’, rd’, shamt[5:0]。
C.ANDI 指令是一条 CB 格式的指令,它在寄存器 rd’的值和一个符号扩展的 6 位立即数之间进行按位 AND 运算,并将结果写入到 rd’中。 C.ANDI 指令被扩展为 andi rd’, rd’, imm[5:0]。
5.整数寄存器-寄存器指令
这些指令使用 CB 格式。
C.MV 指令将寄存器 rs2 的值复制到寄存器 rd 中。C.MV 指令被扩展为 add rd, x0, rs2
C.ADD 指令将寄存器 rd 的值与寄存器 rs2 的值相加,并将结果写入到寄存器 rd 中。C.ADD指令被扩展为 add rd, rd, rs2。
这些指令使用 CS 格式。
C.AND 指令在寄存器 rd’和 rs2’之间执行按位 AND 操作,并将结果写入寄存器 rd’。 C.AND指令被扩展为 and rd’, rd’ rs2’。
C.OR 指令在寄存器 rd’和 rs2’之间执行按位 OR 操作,并将结果写入寄存器 rd’。C.OR 指令被扩展为 or rd’, rd’ rs2’。
C.XOR 指令在寄存器 rd’和 rs2’之间执行按位 XOR 操作,并将结果写入寄存器 rd’。C.XOR指令被扩展为 xor rd’, rd’ rs2’。
C.SUB 指令将寄存器 rd’的值减去 rs2’的值,并将结果写入寄存器 rd’。C.SUB 指令被扩展为 sub rd’, rd’ rs2’。
6.非法指令
一条所有位都是 0 的 16 位指令,被永久的保留为一条非法指令。
7.NOP指令
C.NOP 指令是一条 CI 格式指令,它不改变任何用户可见状态,除了推进 pc 之外。 C.NOP指令被编码为 c.addi x0, 0 并且被扩展为 addi x0, x0, 0。
8.断点指令
调试器可以使用 C.EBREAK 指令,它将被扩展为 ebreak 指令,并导致控制被转移回到调试环境。C.EBREAK 指令的操作码与 C.ADD 指令的操作码相同,但是其 rd 和 rs2 都是 0,因此也可以使用 CR 格式