《手把手教你设计CPU——RISC-V处理器篇》读书笔记(三)
三、RISC指令解读
Risc-v指令
I型指令 = 带常数的算术指令和加载指令。
R型指令 = 其他算术指令和存储指令
U型指令 特指lui指令 用于制造大常数
SB型指令 分支指令 不能编码奇数地址 若编码奇数地址,则是按半字数寻址。
半字=16bit,字节=8bit,字=32bit
UJ型指令 jal 不能编码奇数地址 输入立即数的最低位默认置0

Addi rd rs1 imm12
寄存器rs1和12位立即数相加。12位立即数进行符号位扩展。结果溢出则舍弃,仅保留低32位结果。

slti rd rs1 imm12
寄存器rs1和12位立即数相比较, rs1小则输出1。
sltiu rd rs1 imm12
无符号数比较。符号位扩展不影响比较结果。

(无符号整数 )是否比1小?=是否是0?
andi rd rs1 imm12
与操作。
ori rd rs1 imm12
或操作。
xori rd rs1 imm12
异或操作。
xori rd rs1 -1 相当于 NOT rd rs1 -1 = 11111..111
补码
冗余的-10000..000 = -2^(n-1)
四位
0000 = 0
0001 = 1
0111 = 7
1000 = -8
1001 = -7
1010 = -6
1111 = -1
补码的特性
加法
7+-8 = 0111 + 1000 = (0+1)111 不溢出 符号位改变,最高数值位不进位,
7+-6 = 0111 + 1010 = (0+1+1)001 =10001 = 1 不溢出 符号位改变,最高数值位也进位,
7 + 1 = 0000 + 0001 =(0+1)000 = 1000 =-8 溢出 符号位改变,最高数值位进位,
m表示符号位是否进位,n表示最高数值位是否进位,m^n=1则发生溢出
加法对有符号数无符号数一致
无符号数减法
正常做法按补码运算
7 - 8 = 7 + (- 8) = 0111 + 1000 = 1111
7 - 6 = 7 + (-6) = 0111 + (-0110) =0111 + (1 ~110 + 0001) =0111 + 1000 + ~0110 + 0001 = 0111 + (1010) = 0001
15 - 6 = 15 + (-6) = 01111 + (-00110) =01111 + (1 ~0110 + 00001) =01111 + 10000 + ~0110 + 00001 = 01111 + 10000 + 01001 + 00001 = 01111 +11010 =
15 + 10 + 10000 = 11001 = 1001
e203的做法
7 - 8 = 7 + (- 8) = 0111 - 1000 = 7 +( (~8) + 1) = 000111 + ~001000 + 1 = 000111 + 110111 + 1 = 000111 + 111000 = 111111 = - 1
7 - 8 = 7 + (- 8) = 0111 + 1000 = 7 + (~8) + 1 = 0111 + ~1000 + 1 = 0111 + 0111 + 1 = 1111 = - 1
7 - 6 = 7 + (-6) = 0111 - (0110) = 7 +( (~6) + 1) =0111 + ~0110 + 0001 = 0111 +1001 + 0001 = 0111 + 1001 = 0001
可以看到,对于减法来说,将原码整个取反可以达到形成补码的效果
15 - 6 = 15 + (-6) = 15 +( (~6) + 1) = 01111 + ~(00110) + 1=01111 + ~00110 + 00001 = 01111 +10000 + 01001 + 00001 = 01111 + 11010 = 1001
有符号数减法 同无符号数
e203方法
7 - (-6) = 7 +( (~-6) + 1) = 00111 + ~(11010) + 1=00111 + 00101 + 00001 = 00111 + 00110 =01101 =13
15- (-6) = 7 +( (~-6) + 1) = 01111 + ~(11010) + 1=01111 + 00101 + 00001 = 01111 + 00110 = 10101 =0101 = 5
可以正常向上溢出
7 - 8 = 7 +( (~8) + 1) = 00111 + ~(01000) + 1 = 00111 + 10111 + 00001
= 00111 + 11000 = 11111 = -1
用于无符号数比较
7 - 8 = 11111 = -1 双符号位1 符号位1 7 小于8
7 - 6 = 0111 - 0110 = 00001 双符号位0 符号位0 7 大于6
减法用于符号数比较
符号位扩展
7 - (-6) = 01101 = 13 双符号位0 符号位1 7 大于-6
7 - (- 1) =7 +(1) = - 1 = 000111 + 000001 = 01000 双符号位0 7 大于-1
7 - (- 7) = - 6 = 0111+ 0111= 01110 双符号位0 7 大于-7
7 - (0) = 7 = 0111+ 0000 = 00110 双符号位0 7 大于0
7 - (6) = - 1 =7 +(- 6) = 0111 + 1010 = 00001 双符号位0 7 大于6
实质:取N+1位的比特用于判断比较结果,是运用了符号位扩展的缘故。
slli rd,rs1,shamt[4:0]
对操作数寄存器rs1中的整数值作逻辑左移,低位补0,移位量最多为2^(5)-1=31
srli rd,rs1,shamt[4:0]
对操作数寄存器rs1中的整数值作逻辑右移,高位补0,移位量最多为2^(5)-1=31
srai rd,rs1,shamt[4:0]
对操作数寄存器rs1中的整数值作算术右移,高位补符号位,移位量最多为2^(5)-1=31
0111 -> 0011 = 7 -> 3 除法得商
Lui rd,imm
Lui指令将20位立即数的值左移12位(低12位补0),成为32位数,写入rd
auipc rd,imm
Lui指令将20位立即数的值左移12位(低12位补0),成为32位数,与pc相加写入rd
Add rd, rs1,rs2
寄存器之间整数值加法
Sub rd, rs1,rs2
整数值减法
slt rd, rs1,rs2
整数有符号值比较 rs1小则输出1
sltu rd, rs1,rs2
整数无符号值比较 rs1小则输出1
and rd, rs1,rs2
整数值与运算
or rd, rs1,rs2
整数值或运算
xor rd, rs1,rs2
整数值异或运算
sll rd, rs1,rs2
整数值逻辑左移,低位补0,左移量是rs2的低5位
srl rd, rs1,rs2
整数值逻辑右移,高位补0,右移量是rs2的低5位
sra rd, rs1,rs2
整数值逻辑左移,高位补符号位,右移量是rs2的低5位
Jal rd, label
Jal 指令使用20位立即数(有符号数)作为偏移量(offset),该偏移量乘以2,然后与指令(jal)的PC相加,得到最终跳转目标地址,可以跳转到前后1MB的地址区间。2^19*2=1024*1024bits。
当前PC+4后写入寄存器rd,这是为了便于回到原来的地址。
Label是汇编器根据label所在的地址计算出相对偏移量赋予指令编码。
Jalr rd, rs1, imm
Jal 指令使用12位立即数(有符号数)作为偏移量(offset),与rs1的值相加得到最终的跳转目标地址。 Jalr将下一条指令(当前PC+4后)的pc写入结果rd,这是为了便于回到原来的地址。

分支跳转指令在距离当前指令-2^10~+2^10个字的地址寻址,意味着寻址地址0是-2^12~+2^12个字节。由于RISCV不支持以字节为单位寻址。因此PC增减的最少单位是2
,等于两个字节,因此可以省略分支地址的最末位。12bit有符号数可以表示-2^11~+(2^11-1),若考虑忽略分支地址的最末位,12bit有符号数可以表示-2^12~+(2^12-1)当中的所有偶数,也即按-2^10~+2^10个字的范围进行地址寻址。
这种寻址方式,与可容纳立即数的长度L有关,寻址范围是-2^(L-2)~+2^(L-2)
运用lui指令进行长距离跳转时,需要先把地址的12~31位写入临时寄存器。然后用jalr指令将地址的低12位加到临时寄存器,再按该寄存器的值进行跳转。需要注意的是,此时寻址模式是按照jalr的规则,寻址地址的末位有效,此时跳转指令可以跳转到任意地址(有错位风险)。
寻址模式总结

立即数寻址:jal(特别) addi 操作数本身是指令的一部分
寄存器寻址:特点的是功能指示在前,如add sub 操作数在寄存器中
基址寻址:jalr 可以按字节寻址 操作数在内存(指令内存和寄存器)中
PC相对寻址: beq bne 最少可以按半字寻址 操作数在PC和指令中
Beq rs1,rs2,label
有条件跳转指令,使用12位立即数作为偏移量,乘以2,然后与PC相加得到最终的跳转地址。当仅rs1=rs2,跳转。
Bne rs1,rs2,label
有条件跳转指令,使用12位立即数作为偏移量,乘以2,然后与PC相加得到最终的跳转地址。当仅rs1~=rs2,跳转。
Blt rs1,rs2,label
有条件跳转指令,使用12位立即数作为偏移量,乘以2,然后与PC相加得到最终的跳转地址。当仅有符号数rs1 < rs2,跳转。
Bltu rs1,rs2,label
有条件跳转指令,使用12位立即数作为偏移量,乘以2,然后与PC相加得到最终的跳转地址。当仅无符号数rs1 < rs2,跳转。
Bge rs1,rs2,label
有条件跳转指令,使用12位立即数作为偏移量,乘以2,然后与PC相加得到最终的跳转地址。当仅有符号数rs1 > rs2,跳转。
Bgeu rs1,rs2,label
有条件跳转指令,使用12位立即数作为偏移量,乘以2,然后与PC相加得到最终的跳转地址。当仅无符号数rs1 > rs2,跳转。
LW rd, offset[11:0](rs1)
从rs1读取值加12位立即数,作为地址,读32位数据,写到rd中
Lh rd, offset[11:0] (rs1)
从rs1读取值加12位立即数,作为地址,读16位数据,符号位扩展后写到rd中
Lhu rd, offset[11:0] (rs1)
从rs1读取值加12位立即数,作为地址,读16位数据,高位补零扩展后写到rd中
Lb rd, offset[11:0] (rs1)
从rs1读取值加12位立即数,作为地址,读8位数据,符号位扩展后写到rd中
Lbu rd, offset[11:0] (rs1)
从rs1读取值加12位立即数,作为地址,读8位数据,高位补零扩展后写到rd中
sw rs2, offset[11:0] (rs1)
从rs1读取值加12位立即数,作为地址,将rs2的值写入地址
sh rd, offset[11:0]
从rs1读取值加12位立即数,作为地址,将rs2低16位的值写入地址
sb rd, offset[11:0]
从rs1读取值加12位立即数,作为地址,将rs2低8位的值写入地址
RV支持地址非对齐的存储器操作,e203使用软件异常服务程序来支持。
对于地址对齐的存储器读写,RV规定其读写操作必须具备原子性。
Csrrw rd,csr,rs1
将csr索引的csr寄存器读出,写回结果寄存器rd中。将rs1的值写入csr索引的csr寄存器。一读一写。
Csrrs rd,csr,rs1
将csr索引的csr寄存器读出,写回结果寄存器rd中。
以rs1的值逐位参考,如果rs1中的值某个比特位是1,将csr索引的csr寄存器对应比特位置为1,其余位不受影响。
一读一写。
Csrrc rd,csr,rs1
将csr索引的csr寄存器读出,写回结果寄存器rd中。
以rs1的值逐位参考,如果rs1中的值某个比特位是1,将csr索引的csr寄存器对应比特位置为0,其余位不受影响。
一读一写。
Csrrwi rd,csr,imm[4:0]
将csr索引的csr寄存器读出,写回结果寄存器rd中。
以五位立即数的值(高位补0扩展)参考,写入由csr索引的csr寄存器。
一读一写。
Csrrsi rd,csr, imm[4:0]
将csr索引的csr寄存器读出,写回结果寄存器rd中。
以五位立即数的值(高位补0扩展)逐位参考,如果某个比特位是1,将csr索引的csr寄存器对应比特位置为1,其余位不受影响。
一读一写。
Csrrci rd,csr, imm[4:0]
将csr索引的csr寄存器读出,写回结果寄存器rd中。
以五位立即数的值(高位补0扩展)逐位参考,如果某个比特位是1,将csr索引的csr寄存器对应比特位置为0,其余位不受影响。
一读一写。
Rs rd imm 可以为0
Fence

Fence io iorw = fence之前的设备读写必须在fence之后的设备读写存储器读写完成.

E203的一切fence指令,都实现为 fence iorw, iorw
Fence.i

Ecall
产生环境调用异常,mepc寄存器将会被更新为ecall指令本身的PC值。
Ebreak
产生断点异常,mepc寄存器将会被更新为Ebreak指令本身的PC值。
mret
退出异常,mepc寄存器将会被更新为ecall指令本身的PC值。
执行mret时,跳转到mepc寄存器指定的PC地址。
同时,mie被更新为mpie的值(回到进入异常以前的值,可以响应中断),mpie的值更新为1。
wfi
等待中断,可以实现为nop。收到中断以后进入中断异常服务。如果中断被关闭,则继续执行之前停止的指令流
RV32M
MUL rd, rs1,rs2
Rs1与rs2相乘(无论有没有符号),低32位写入寄存器rd中。
MULh rd, rs1,rs2
Rs1与rs2相乘(有符号),高32位写入寄存器rd中。
MULhu rd, rs1,rs2
Rs1与rs2相乘(无符号),高32位写入寄存器rd中。
MULhsu rd, rs1,rs2
Rs1与rs2相乘(前有符号,后无符号),高32位写入寄存器rd中。

Div rd,rs1,rs2
Divu rd,rs1,rs2
rem rd,rs1,rs2
remu rd,rs1,rs2
div指令将操作数寄存器rs1和rs2中的32位整数相除,值都被当作有符号数,将除法所得的商写回寄存器rd中。
divu指令将操作数寄存器rs1和rs2中的32位整数相除,值都被当作无符号数,将除法所得的商写回寄存器rd中。
rem指令将操作数寄存器rs1和rs2中的32位整数相除,值都被当作有符号数,将除法所得的余数写回寄存器rd中。
divu指令将操作数寄存器rs1和rs2中的32位整数相除,值都被当作无符号数,将除法所得的余数写回寄存器rd中。

除法除以0会触发异常跳转从而进入异常模式,但是RISC-V架构的除法指令在除以0时并不会进入异常模式。

浮点有关指令略过。
RV32A
amoswap.w rd,rs2,(rs1)
amoadd.w rd,rs2,(rs1)
amoand.w rd,rs2,(rs1)
amoor.w rd,rs2,(rs1)
amoxor.w rd,rs2,(rs1)
amomax.w rd,rs2,(rs1)
amomaxu.w rd,rs2,(rs1)
amomin.w rd,rs2,(rs1)
amominu.w rd,rs2,(rs1)
从以rs1为地址存储器中读出一个数据,存放至rd寄存器中,并将读出的数据与rs2寄存器的值进行计算。将计算后的结果写回存储器,还是原来的rs地址。rd起到temp作用。

Lr.w rd, (rs1)
sc.w rd, rs2,(rs1)
LR从rs1寄存器保存的地址中读出32位数据。执行成功向rd写0,否则写非零。
SC从rs1寄存器保存的地址中写入32位数据,值来自rs2寄存器中的值,执行成功向rd写0,否则写非零。
顺序无缝执行则实现数据的原子交换。
有编码位可以赋予属性。
RV32C
关于压缩指令集RV32C的内容暂不展开