这里所介绍的指令集架构均属于 RISC,相比 CISC 更简单。但是 ARM 比较特殊,因为它作为一个 RISC 架构竟然有 push/pop 指令……
ARMv7
ARM 指令集从旧到新有 ARMv6、ARMv7、ARMv7s、ARMv8,使用新指令集的芯片也能支持旧的指令集,但是无法发挥出较好的性能。同时要注意,指令集版本不同的静态库是不能一起使用的。
ARM 有两种工作状态,ARM 状态的指令长度是 32 位的,Thumb 状态的指令长度是 16 位的(从 ARMv6-T2 开始引入的 Thumb-2 指令集也支持 32 位的指令,具有 .w 后缀)
使用 Thumb 指令集可以得到较小的二进制文件,但是可以访问的寄存器更少,产生的指令数量更多,性能会有损失。
寄存器
Register...
r0函数返回值或函数参数
r1~r3函数参数,更多参数使用栈传递
r4~r10临时变量
r11函数栈帧FP(类似于EBP寄存器)
r12内部过程调用寄存器IP
r13栈指针SP
r14链接寄存器LR,用于存储将要跳转到的地址
r15程序计数器PC
Cortex-M 含有两个栈指针寄存器(SP),分别为 PSP(Process SP)和 MSP(Main SP)。
ARM 指令
指令结构为 MNEMONIC{S}{cond} {Rd}, Operand1, Operand2
如果指令后加了 S,将依据计算结果更新当前程序状态寄存器 CPSR 中相应的标志。
cond 表示执行条件,见下表。
扩展助记符意义(整形)意义(浮点)条件
EQ相等相等Z==1
NE不相等不相等或无序(即至少有一个 NaN)Z==0
CS/HS进位置位/无符号高于等于大于等于或无序C==1
CC/LO进位复位/无符号低于小于C==0
MI减法、负数小于N==1
PL加法、非负数大于等于,或无序N==0
VS溢出置位无序V==1
VC溢出复位不无序V==0
HI无符号高于大于或无序C==1 且 Z==0
LS无符号不高于小于或相等C==0 或 Z==1
GE有符号大于等于大于等于N==V
LT有符号小于小于或无序N!=V
GT有符号大于大于Z==0 且 N==V
LE有符号小于等于小于等于或无序Z==1 或 N!=V
AL(default)无条件无条件
第三个操作数可以是寄存器,也可以是要进行的移位操作。可能的操作有 LSL/LSR/ASR/ROR/RRX,其中 RRX 表示右移一位,最高位由 APSR 寄存器的状态位 C 填充,移出的位给状态位 C。
Mnemonic...
mov复制寄存器。例如 MOV R0,R2,LSL #3 表示把 R2 左移 3 位后传给 R0。
lsl/lsr/asr/ror逻辑左移,逻辑右移,算数右移,循环右移
push把立即数、单个寄存器,多个寄存器压栈。多个寄存器按从右到左压栈
pop出栈到单个寄存器或多个寄存器(从左到右的顺序)
ldr/strldr 从内存中读 32 位数据,str 写 32 位数据到内存。如 ldr r1, [r2, #0x10] 即 r1 = *(r2+0x10);str r1, [r2, #0x01] 即 *(r2+1)=r1。相应地,读写 16 位时 ldrh/strh;8 位时 ldrb/strb。例如 ldrneh 即不相等时读半字。
swp交换值,可用于实现信号量
b/blb 跳转,bl 跳转并把下一条指令地址存储到 lr
bx/blx Rm跳转并切换状态。由于指令是 2 或 4 字节对齐的,地址的最低位一定是 0,故若Rm & 0x01==1则令 CPSR.T=1,把目标地址解释为 Thumb 代码;否则令CPSR.T=0,目标地址解释为 ARM 代码
add/sub加减法,更新标志位
sbc带借位减法,Rd = ~Ra + Rb + CPSR.C
cmp比较两个寄存器或一个立即数(使用减法)并更新标志位
tst/teq测试相等
mul/mla/umullmul 乘法,mla 乘加,即 Ra*Rb+Rc,umull 64 位乘法
svc/swi系统调用
and/orr/eor按位与,按位或,按位异或
几个 SIMD 指令:
Mnemonic...
ldmib/stmib/ldmdb/stmdbb 表示存储器指针在保存/读取第一个值之前更新;i 表示更新方式为增加,d 表示更新方式为减少
ldmfa/stmfa/ldmfd/stmfd存储器指针为 SP 时,就等价于该指令。f 表示堆栈指针指向内含有效数据项的最高地址;a 表示堆栈往高地址增长,d 表示堆栈往低地址增长
ldmea/stmea/ldmed/stmede 表示堆栈指针指向堆栈的第一个空位置 ;a 表示堆栈往高地址增长,d 表示堆栈往低地址增长。
协处理器指令格式:
MRC/MCR {cond} coproc,opcode1,Rd,CRn,CRm{,opcode2}
MCR:从寄存器到协处理器
MRC:从协处理器到寄存器
coproc:协处理器名称,p0~p15
MSR:从寄存器到状态寄存器,如 MSR CPSR,R0
MRS:从状态寄存器到寄存器,如 MRS R2,SPSR
中断
Cortex-A 处理器发生中断时,先跳转进 IRQ 入口,再通过软件判断是由哪个中断源触发,获得相对应的中断服务程序(ISR)的地址。而 Cortex-M 处理器发生中断时,CPU 自动从中断向量表中寻找相应的中断服务程序并跳转。
链接寄存器(LR)的数值会被更新为 EXC_RETURN 值(详细含义可自行搜索)。然后硬件自动将 r0, r1, r2, r3, r12, lr, pc, psr 寄存器依次入栈,同时更新 psp 寄存器。中断处理函数结束时,通过 bx lr 指令触发中断返回,处理器将自动保存的寄存器值从栈上弹出并恢复给相应的寄存器。
svc 指令用于系统调用,如 svc #0x900004 调用 sys_write
另一种方法是把编号传给 r7(如 4 为 sys_write),再使用 swi 0 进行调用。
MIPS32
寄存器
Register NumberName...
0zero它总是返回零,对其写入也没有效果
1at汇编器临时寄存器,用于加载 16 位以上的大常数使用
2-3v0,v1函数返回值,对于 64 位的返回值,两个寄存器都要用到
4-7a0-a3函数调用参数,更多参数使用栈传递
8-15t0-t7无需保存的临时变量
16-23s0-s7需要在当前函数返回之前,恢复其本来值的临时变量
24,25t8-t9无需保存的临时变量
26k0发生中断时,在中断处理过程中用作临时变量
27k1用于中断嵌套的深度计数器。也可作为临时变量
28gp一个全局指针,可以用于访问一些全局变量。没有特定的用法
29sp栈指针
30s8/fp栈帧指针,也可作为需要维护值的临时变量
31ra保存了函数的返回地址,类似于lr
此外还有不可直接操作的 lo、hi 寄存器,在运算指令中会用到。
指令
Mnemonic...
lb/lh/lw从存储器中读取一个 byte/half word/word 的有符号数到寄存器中。lb r1, 0(r2) 即 r1=(signed int)*(char*)(r2+0)
lbu/lhu从存储器中读取一个 byte/half word 的无符号数到寄存器中。lhu r1, 0(r2) 即 r1=(unsigned int)*(short*)(r2+0)
sb/sh/sw把一个 byte/half word/word 的数据从寄存器存储到存储器中。如 sb r1, 0(r2) 即 *(char*)(r2+0)=(char)r1
llo/lhi加载低半字/加载高半字,lhi r1,2 即把 r1 的高半字设为 2
lui把一个 16 位的立即数填入到寄存器的高 16 位,低 16 位补零。i 表示立即数
mflo/mfhi/mtlo/mthi读写 lo、hi 寄存器,mflo r1 即 r1=lo,mthi r1 即 hi=r1
add/addu把两个定点寄存器的内容相加 add r1,r2,r3 即r1=r2+r3;u 为不带符号加
addi/addiu把一个寄存器的内容加上一个立即数 add r1,r2,#3 即 r1=r2+3;u 为不带符号加
sub/subu把两个定点寄存器的内容相减 sub r1,r2,r3 即 r1=r2-r3
div/divu两个定点寄存器的内容相除 div r1,r2 即 lo=r1/r2; hi=r1%r2
mult/multu两个定点寄存器的内容相乘 mult r1,r2 即 hi:lo=r1*r2
and/andi与运算,两个寄存器中的内容相与 and r1,r2,r3 即 r1=r2&r3
or/ori或运算,or r1,r2,r3 即 r1=r2|r3
nor或非运算,nor r1,r2,r3 即 r1=~(r2|r3)
xor/xori异或运算,xor r1,r2,r3 即 r1=r2^r3
beq/beqz/bnez/bne条件转移,eq 相等,z 零,ne 不等。beq r1,r2,label 即 r1 等于 r2 时跳转。bnez r1,label 即 r1 非 0 时跳转
bgtz/blez条件转移,gt 大于,le 小于等于
j/jr/jal/jalrj 相对跳转,jr 使用寄存器绝对跳转,jal 把下一条指令地址给 ra 并相对跳转,jalr 把下一条指令地址给 ra 并使用寄存器绝对跳转
sll/sllv逻辑左移 sll r1,r2,#2,即 r1=r2<<2
sra/srav算数右移 sra r1,r2,#2 即 r1=r2>>2
srl/srlv逻辑右移 srlv r1,r2,r3 即 r1=r2>>>r3
slt/uslt/slti/sltiuSet if Less Than,slt r1,r2,r3 如果 r2 的值小于 r3,那么设置 r1 的值为 1,否则设置 r1 的值为 0
mov/movz/movn复制寄存器 mov r1,r2;movz r1,r2,r3 即 r3 为零则复制 r2 到 r1;movn 即 r3 为负时复制
nop空指令
trap根据地址向量转入管态
eret/rfe从异常中返回到用户态
syscall把系统调用号传给 v0,把参数传给 a0~a3,然后使用 syscall 进入内核态
MIPS 有一个特色:分支延迟槽(branch delay slot)。它的意思是,当遇到分支指令时(即 j/jr/jal/jalr),该分支指令的下一条指令一定会被执行。因此 jal 和 jalr 所设置的 ra 寄存器的值,实际上是分支指令所在地址 +8,而非 +4。这一点在调试的时候需要尤其注意。
此外,在 DIV、DIVU、MULT、MULTU 指令之后,在新的结果写入 HI 或 LO 之前,必须先用 MFHI 、MFLO 将 HI、LO 读出。否则两次连续对 HI、LO 的写入会导致下次读取时产生不可预料的值。
中断
发生中断时,处理器把引发异常的指令地址传给 CR0 中的 EPC 寄存器,把 pc 的值设置为 0x8000 0180,把异常原因传给 CR0 中的 Cause 寄存器。然后在异常处理程序中使用 mfepc 指令(等价于 mfc0 R, EPC)将 EPC 的值赋给 k0;使用 mfco 指令把异常原因读出(等价于 mfc0 R, Cause)。而且在使用这两个指令时,需要多加一个 nop 指令以等待其完成。
对于 MIPS I 和 MIPS II 处理器,从异常返回时,要到 EPC 所指向的地址处继续执行,并使用 rfe 指令切换回用户态(SR 寄存器),汇编指令为:
mfc0 k0, epc
jr k0
rfe
这里 jr 和 rfe 是一起执行的。而在 MIPS III 中只需要一条 eret 指令就可以了。
修改 CR0 使用 mtc0 指令,注意 CR0 只能在内核态下访问。
浮点数
使用 lwc1 把一个 32 位字从存储器加载到 CR1,然后可以进行运算:add.s、add.d、sub.s、sub.d、mul.s、mul.d、div.s、div.d。其中 s 表示单精度,d 表示双精度。最后使用 swc1 把一个 32 位字从 CR1 保存到存储器。
PowerPC 405
寄存器
通用寄存器 r0~r31:
r1 用作栈指针 sp,r3 用作返回值。
函数参数从左往右依次使用 r3,r4,r5,r6…传递,也可以用栈传递。
函数内部临时变量依次使用 r31,r30,r29,r28…,而且函数需要维护 r3~r31 原本的值,在函数返回时恢复到进入时的状态。
定点数异常寄存器(Fixed-Point Exception Register,XER)反映算术操作的溢出 XER[OV]、进位 XER[CA] 和总溢出(Summary Overflow)XER[SO]。
条件寄存器 CR 在一些条件转移指令时会用到。它分为 8 个 4bit 组,依次为 CR0~CR7,其比特位含义如下:
0 1 2 3
| LT | GT | EQ | SO |
指令集
Mnemonic...
addi rD, rA, SIMM先把 SIMM 进行符号扩展到 32 位,如果 rA 为 r0 则直接传给 rD,否则加上 rA 的值传给 rD
addis rD, rA, SIMM把 SIMM 低位补 16 个 0,如果 rA 为 r0 则直接传给 rD,否则加上 rA 的值传给 rD
lis rD, SIMMaddis rD, 0, SIMM 的别名(伪指令)
li rD, SIMMaddi rD, 0, SIMM 的别名
la rD, d(rA)addi rD, rA, d 的别名
mr rA, rSor rA, rS, rS 的别名
nopori,0,0,0 的别名
lwz rD d(rR)rD=*(rR+(singned int)d)
stw rD d(rR)*(rR+(signed int)d)=rD
mtlr/mtctr把寄存器的值传给 LR 寄存器或 CTR 寄存器,实际上是 mfspr 指令的别名
b/ba/bctr/blr/bl/bctrl/blrl无条件跳转,a 表示无条件跳转,ctr 表示跳转到 CTR 寄存器指定的位置,lr 表示跳转到 LR 寄存器指定的位置;再加 l 表示并把下一条指令的地址 赋给 lr
blt/ble/beq/bge/bgt/bne条件跳转。这些指令助记符后同样可以加 a、lr、ctr 并再加 l。如 beqctrl 表示相等则跳转到 ctr 处并更新 lr
cmpw/cmpwi/cmplw/cmplwi比较字 w 或逻辑字 lw,并更新特定的 CR 域(默认 CR0)
sraw/srawi算数右移
srw/srwi/slw/slwi逻辑右移、逻辑左移
注意上表的 SIMM 是带符号地从 16 位扩展到 32 位,例如 00AE0000+(signed short)AAD0 得到 00ADAAD0(舍去进位的 1)。
64 位整数的加减法对应如下指令:
addc r8,r6,r4 # 低位字相加,记录进位 XER[CA]
adde r7,r5,r3 # 高位字相加,使用前面的进位
---
subfc r8,r6,r4 # 减去低位字,记录借位 XER[CA]
subfe r7,r5,r3 # 减去高位字,使用前面的借位
最终效果就是 (r7:r8)=(r3:r4)±(r5:r6)。
CR0~CR7 都可用于反映指令的结果。定点数运算指令如 addic.、andi.、andis. 后面的小数点 . 表示它会更新 CR0 反映他们的结果,如果没有这个点,则只会更新 XER。更新方式为,把结果与 0 进行比较,更新 CR0 的高 3bit,然后把 XER 的 SO 位复制到 CR0 的 SO 位。此外 CR1 还可用作浮点操作的结果(PPC 405 不支持)。
函数调用约定
…调用方传参,参数放到 r3
bl func跳转到 func,把下一条指令地址放到lr寄存器
stwu r1, -0xF8(r1)相当于 *(sp-F8h)=sp,然后 sp-=F8h。这个减去的值叫做 back_chain
mflr r0把 lr 寄存器赋给 r0
stw r31, 0xF8-4(r1)把进入时的寄存器值备份到栈上
stw r0, 0xF8+4(r1)把返回地址保存到栈上,方便后续用 bl 调用别的函数
根据以上过程,可以得出此时栈的情况(上面是低地址,下面是高地址)
地址保存的值
sp栈基地址,进入时的 sp,其实就是 sp+F8h
sp+4分析一下可以发现,如果该函数再调用别的函数,那么LR 存到这里
sp+8局部变量。有时也作为栈传递的参数
……
sp+F8h-8备份进入时的 r30
sp+F8h-4备份进入时的 r31
sp+F8h分析一下可以发现,应当是调用者的栈基地址,即sp+F8h+??
sp+F8h+4返回地址*
函数返回过程如下:
lwz r0, F8h+4(r1)把栈上的返回地址读到 r0
mtlr r0把 r0 赋给 lr
lwz r31, F8h-4(r1)把进入时备份的值恢复给 r31
……
addi r1, r1, F8h恢复堆栈指针
blrjmp lr
Snippet
下面给出一些汇编指令的化简结果,具体细节可以参考指令手册。
汇编指令对应的 C 代码
subfic rA, rB, 0
adde rC, rA, rBrC = (rB ? 0 : 1)
addic rA, rB, -1
subfe rC, rA, rBrC = (rB ? 1 : 0)
addic rA, rB, -1
subfe rC, rA, rArC = (rB ? 0 : -1)
extsb rA, rA
srawi rB, rA, 0x1F
xor rC, rB, rA
subf rC, rC, rB
srawi rC, rC, 0x1FrC = (rA ? -1 : 0)
extsb rA, rA
xor rC, rA, rS
srawi rB, rC, 0x1F
xor rC, rB, rC
subf rC, rC, rB
srawi rC, rC, 0x1FrC = (rA == rS) ? 0 : -1
可以编写一个等价的程序测试一些不同的输入值来验证。
RISC-V
寄存器
32 个定点数寄存器如下:
Register NumberName...
0zero它总是返回零,对其写入也没有效果
1ra返回地址
2sp栈指针
3gp一个全局指针,可以用于访问一些全局变量。没有特定的用法
4tp线程指针
5t0无需保存的临时变量,也作替代 LR
6-7t1-t2无需保存的临时变量
8s0/fp栈帧指针,也可作为需要在当前函数返回之前,恢复其本来值的临时变量
9s1需要在当前函数返回之前,恢复其本来值的临时变量
10-11a0-a1函数参数,或作为返回值
12-17a2-a7函数参数
18-27s2-s11需要在当前函数返回之前,恢复其本来值的临时变量
28-31t3-t6无需保存的临时变量
32 个浮点数寄存器如下:
RegisterName...
0-7ft0-7临时变量
8-9fs0-1需要维护的变量
10-11fa0-1参数/返回值
12-17fa2-7参数
18-27fs2-11需要维护的变量
28-31ft8-11临时变量
指令集
RISC-V 的指令集使用模块化的方式进行组织,基本指令集包括 RV32I、RV32E(Embedded,RV32I 的子集,仅支持 16 个通用整数寄存器)、RV64I、RV128I;必须实现基本指令集其中一种。
扩展指令集包括:M(整数乘除法)、A(存储器原子操作指令和 Load-Reserved/Store-Conditional 指令)、F(单精度浮点指令)、D(双精度浮点指令)、G(通用,即 IMAFD 的组合)、C(压缩指令,指令长为 16 位)等等。
特权级有 4 种,0 为用户应用程序,缩写为 U;1 为管理员,缩写为 S,2 为 Hypervisor,缩写为 H;3 为机器,缩写为 M。一般情况下只需要使用 M 和 U。
RISC-V 架构的一个特殊之处是对任何的运算指令错误(包括整数与浮点指令)均不产生异常,而是产生某个特殊的默认值,同时,设置某些状态寄存器的状态位。RISC-V 架构推荐软件通过其他方法来找到这些错误。
基础指令说明
addi rd, rs1, imm[11:0]
slti rd, rs1, imm[11:0]Set Less Than Immediate 如果 rs1 的值小于立即数的值,则 rd 为 1,否则为 0
sltiu rd, rs1, imm[11:0]
andi rd, rs1, imm[11:0]
ori rd, rs1, imm[11:0]
xori, rs1, imm[11:0]
slli, rs1, imm[11:0]Shift Left Logic Immediate
srli, rs1, imm[11:0]
srai, rs1, imm[11:0]
lui rd, immLoad Upper Immediate 将 20 位立即数的值左移 12 位,低位补 0 并写入 rd
auipc rd, immAdd Upper Immediate to PC 将 20 位立即数的值左移 12 位,低位补 0 和 PC 相加后写入 rd
add rd, rs1, rs2若结果发生溢出,将溢出位舍弃(无进位、借位标志)
sub rd, rs1, rs2若结果发生溢出,将溢出位舍弃(无进位、借位标志)
slt rd, rs1, rs2
sltu rd, rs1, rs2
and rd, rs1, rs2
or rd, rs1, rs2
xor rd, rs1, rs2
sll rd, rs1, rs2
srl rd, rs1, rs2
sra rd, rs1, rs2
jal rd, labelJump And Link 将 20 位立即数视为有符号数并乘以 2 和 PC 相加,作为跳转地址。并将当前 PC+4 的值写入 rd
jalr rd, rs1, immJump And Link Register 将 20 位立即数视为有符号数并乘以 2 和 rs1 相加,作为跳转地址。并将当前 PC+4 的值写入 rd
beq rs1, rs2, label若 rs1 和 rs2 相等,将 12 位立即数视为有符号数并乘以 2 和 PC 相加,作为跳转地址。
bne rs1, rs2, label
blt rs1, rs2, label
bltu rs1, rs2, label
bge rs1, rs2, label
bgeu rs1, rs2, label
lw/lh/lb rd, offset[11:0](rs1)从存储器中读出一个 16/8 位的有符号数写入 rd
lhu/lbu rd, offset[11:0](rs1)从存储器中读出一个 16/8 位的无符号数写入 rd
sw/sh/sb rs2, offset[11:0](rs1)将 rs2 的低 32/16/8 位写回存储器
csrrw rd, csr, rs1将 CSR 寄存器的值写入 rd,将 rs1 的值写入 CSR 寄存器
csrrs rd, csr, rs1以 rs1 的各比特位为参考,将 CSR 寄存器的对应比特位置 1
csrrc rd, csr, rs1以 rs1 的各比特位为参考,将 CSR 寄存器的对应比特位清 0
csrrwi rd, csr, imm[4:0]将 CSR 寄存器的值写入 rd,将 5 位立即数高位补 0 后写入 CSR 寄存器
csrrsi rd, csr, imm[4:0]将 5 位立即数高位补 0 后的各比特位为参考,将 CSR 寄存器的对应比特位置 1
csrrci rd, csr, imm[4:0]将 5 位立即数高位补 0 后的各比特位为参考,将 CSR 寄存器的对应比特位清 0
ecall产生环境调用异常(Environment Exception),mepc 寄存器的值更新为该指令的地址
ebreak产生断点异常(Breakpoint Exception),mepc 寄存器的值更新为该指令的地址
mret异常返回
wfi处理器进入休眠状态,直到中断发生。如果中断被全局关闭(通过 mstatus 寄存器的 MIE 域),则不进入中断异常服务程序,而是继续向下执行
mul rd, rs1, rs2将两个 32 位数相乘(得到一个 64 位数),将结果的低 32 位写入 rd。由于两个 32 位操作数不论是否有符号,相乘所得的低 32 位结果一定是相同的,因此该指令不区分符号。
mulh rd, rs1, rs2将两个 32 位有符号数相乘(得到一个 64 位数),将结果的高 32 位写入 rd
mulhu rd, rs1, rs2将两个 32 位无符号数相乘(得到一个 64 位数),将结果的高 32 位写入 rd
mulhsu rd, rs1, rs2将 32 位有符号数 rs1 和无符号数 rs2 相乘(得到一个 64 位数),将结果的高 32 位写入 rd
div rd, rs1, rs2有符号数除法。注意:除以零并不会产生异常,而是得到 -1
divu rd, rs1, rs2无符号数除法。注意:除以零并不会产生异常,而是得到 2^32-1
rem rd, rs1, rs2将 rs1 和 rs2 的值当作有符号数相除,将余数写入 rd。注意:除以零并不会产生异常,而是得到 rs1
remu rd, rs1, rs2将 rs1 和 rs2 的值当作无符号数相除,将余数写入 rd。注意:除以零并不会产生异常,而是得到 rs1
注:屏障指令、原子指令、浮点指令从略
一部分 32 位指令编码空间试探性地(tentatively)分配给了超过 32 位长的指令,如下所示:
RISC-V 指令长度编码方案。目前只有 16 位和 32 位编码方案已冻结。
伪指令基础指令含义
la rd, symbol (non-PIC)auipc rd, delta[31 : 12] + delta[11]
addi rd, rd, delta[11:0]Load absolute address, where delta = symbol − pc
la rd, symbol (PIC)auipc rd, delta[31 : 12] + delta[11]
l{w|d} rd, rd, delta[11:0]Load absolute address, where delta = GOT[symbol] − pc
lla rd, symbolauipc rd, delta[31 : 12] + delta[11]
addi rd, rd, delta[11:0]Load local address, where delta = symbol − pc
l{b|h|w|d} rd, symbolauipc rd, delta[31 : 12] + delta[11]
l{b|h|w|d} rd, delta[11:0](rd)Load global
s{b|h|w|d} rd, symbol, rtauipc rt, delta[31 : 12] + delta[11]
s{b|h|w|d} rd, delta[11:0](rt)Store global
fl{w|d} rd, symbol, rtauipc rt, delta[31 : 12] + delta[11]
fl{w|d} rd, delta[11:0](rt)Floating-point load global
fs{w|d} rd, symbol, rtauipc rt, delta[31 : 12] + delta[11]
fs{w|d} rd, delta[11:0](rt)Floating-point store global
基础指令使用和 pc 相关的取址,因此链接器把符号与 pc 相减得到偏移量。链接器把偏移量的第 11 比特加到高 20 比特上,抵消低 12 比特的符号扩展。
nopaddi x0, x0, 0No operation
li rd, immediateMyriad sequencesLoad immediate
mv rd, rsaddi rd, rs, 0Copy register
not rd, rsxori rd, rs, -1One’s complement
neg rd, rssub rd, x0, rsTwo’s complement
negw rd, rssubw rd, x0, rsTwo’s complement word
sext.w rd, rsaddiw rd, rs, 0Sign extend word
seqz rd, rssltiu rd, rs, 1Set if = zero
snez rd, rssltu rd, x0, rsSet if ̸= zero
sltz rd, rsslt rd, rs, x0Set if < zero
sgtz rd, rsslt rd, x0, rsSet if > zero
fmv.s rd, rsfsgnj.s rd, rs, rsCopy single-precision register
fabs.s rd, rsfsgnjx.s rd, rs, rsSingle-precision absolute value
fneg.s rd, rsfsgnjn.s rd, rs, rsSingle-precision negate
fmv.d rd, rsfsgnj.d rd, rs, rsCopy double-precision register
fabs.d rd, rsfsgnjx.d rd, rs, rsDouble-precision absolute value
fneg.d rd, rsfsgnjn.d rd, rs, rsDouble-precision negate
beqz rs, offsetbeq rs, x0, offsetBranch if = zero
bnez rs, offsetbne rs, x0, offsetBranch if ̸= zero
blez rs, offsetbge x0, rs, offsetBranch if ≤ zero
bgez rs, offsetbge rs, x0, offsetBranch if ≥ zero
bltz rs, offsetblt rs, x0, offsetBranch if < zero
bgtz rs, offsetblt x0, rs, offsetBranch if > zero
bgt rs, rt, offsetblt rt, rs, offsetBranch if >
ble rs, rt, offsetbge rt, rs, offsetBranch if ≤
bgtu rs, rt, offsetbltu rt, rs, offsetBranch if >, unsigned
bleu rs, rt, offsetbgeu rt, rs, offsetBranch if ≤, unsigned
控制和状态寄存器 CSR 需要通过 CSRRW、CSRRS、CSRRC、CSRRWI、CSRRSI 以及 CSRRCI 指令访问。
伪指令基础指令含义
j offsetjal x0, offsetJump
jal offsetjal x1, offsetJump and link
jr rsjalr x0, 0(rs)Jump register
jalr rsjalr x1, 0(rs)Jump and link register
retjalr x0, 0(x1)Return from subroutine
call offsetauipc x1, offset[31 : 12] + offset[11] jalr x1, offset[11:0](x1)Call far-away subroutine
tail offsetauipc x6, offset[31 : 12] + offset[11] jalr x0, offset[11:0](x6)Tail call far-away subroutine
fencefence iorw, iorwFence on all memory and I/O
rdinstret[h] rdcsrrs rd, instret[h], x0Read instructions-retired counter
rdcycle[h] rdcsrrs rd, cycle[h], x0Read cycle counter
rdtime[h] rdcsrrs rd, time[h], x0Read real-time clock
csrr rd, csrcsrrs rd, csr, x0Read CSR
csrw csr, rscsrrw x0, csr, rsWrite CSR
csrs csr, rscsrrs x0, csr, rsSet bits in CSR
csrc csr, rscsrrc x0, csr, rsClear bits in CSR
csrwi csr, immcsrrwi x0, csr, immWrite CSR, immediate
csrsi csr, immcsrrsi x0, csr, immSet bits in CSR, immediate
csrci csr, immcsrrci x0, csr, immClear bits in CSR, immediate
frcsr rdcsrrs rd, fcsr, x0Read FP control/status register
fscsr rd, rscsrrw rd, fcsr, rsSwap FP control/status register
fscsr rscsrrw x0, fcsr, rsWrite FP control/status register
frrm rdcsrrs rd, frm, x0Read FP rounding mode
fsrm rd, rscsrrw rd, frm, rsSwap FP rounding mode
fsrm rscsrrw x0, frm, rsWrite FP rounding mode
frflags rdcsrrs rd, fflags, x0Read FP exception flags
fsflags rd, rscsrrw rd, fflags, rsSwap FP exception flags
fsflags rscsrrw x0, fflags, rsWrite FP exception flags
异常
同步异常指试图执行指令而造成的异常,例如非法指令异常。
异步异常指不能精确定位到某条指令的异常,例如外部中断是一种精确异步异常,而访存出错是一种非精确异步异常。
当发生异常时,处理器跳转到由 mtvec 寄存器指定的地址处执行。此外处理器还会更新如下寄存器:
mcause:保存了产生异常的原因,最高位为 1 表示中断,否则为异常(调试断点是一种异常)。
mtval(或 mbadaddr):如果是访存异常,存储了产生异常的存储器地址;如果是指令异常,存储了产生异常的指令编码。
mstatus:设置 MIE 域、MPIE 域和 MPP 域。相当于关闭全局中断。
mepc:存储了异常返回地址。对于中断,mepc 为下一条指令地址;对于异常,mepc 为当前指令地址。
mtvec 的低 2 位为 MODE 域,其余为 BASE 域。若为 0 则所有异常均跳转到 BASE 地址;若为 1 则跳转到 BASE+4*CAUSE 地址,CAUSE 表示异常编号。
退出异常使用 mret 指令,从 mepc 寄存器指定处继续执行并更新 mstatus 寄存器,相当于重新打开全局中断。由于 RISC-V 架构规定的进入异常和退出异常机制中没有硬件自动保存和恢复上下文的操作,因此需要通过软件来保存和恢复上下文。RISC-V 架构默认不支持中断嵌套,开发者可以在中断服务程序中通过修改 mstatus 的 MIE 域重新使能全局中断,同时维护 mepc 寄存器及上下文的恢复与保存。
RISC-V 的中断分为四种,由 mie 寄存器负责使能,由 mip 寄存器反映中断等待(pending)状态 :
外部中断由 PLIC(Platform Level Interrupt Controller,平台级中断控制器)进行仲裁和派发。包括优先级和使能控制等。
计时器中断由 CLINT(Core Local Interrupt Controller,核局部中断控制器)管理,主要是设置 mtime 和 mtimecmp 寄存器。
软件中断由 msip 寄存器触发,对其写 1 或 0 即可触发或清除软件中断。
调试中断。
外部中断的优先级高于软件中断,软件中断的优先级高于计时器中断。
引用