RISC-V特权指令集架构概述
特权等级
目前,RISC-V手册中共定义了3个特权等级:U(00)、S(01)和M(11)。
处理器标识寄存器
点击展开
misa
MXL | 0 | Extensions |
---|---|---|
31:30(63:62) | 29:26(61:26) | 25:0 |
功能:说明当前硬件线程支持的ISA扩展。MXL
(Machine XLEN)说明基础整数指令集的位宽,编码如下:01
-32 bits,10
-64 bits,11
-128 bits。当通过写MXL
字段导致MXLEN改变时,MXL
会移动到新宽度下该寄存器的最高两位。Extensions
字段分别表示处理器是否支持A-Z共26个ISA特性。
mvendorid
Bank | Offset |
---|---|
31:7 | 6:0 |
功能:用于标识处理器供应商的JEDEC ID。可以使用全0表示非商用实现。
marchid
Architecture ID |
---|
31(63):0 |
功能:和mvendorid
一起,唯一的确定硬件线程的微架构。开源项目的marchid
由RISC-V International分配,其最高位为0;商用项目由供应商分配,其最高位不为0。
mimpid
Implementation |
---|
31(63):0 |
功能:处理器实现版本的编码。
mhartid
Hard ID |
---|
31(63):0 |
功能:标识正在运行代码的硬件线程的ID,必须存在一个ID为0的hart。
xstatus寄存器
xstatus寄存器是RISC-V处理器中跟踪和控制一个硬件线程执行状态的寄存器。我认为是RISC-V特权架构中最关键的一个寄存器,理解xstatus寄存器中各个字段的意义,有助于理解RISC-V特权架构。sstatus和mstatus寄存器是同一个寄存器在不同特权等级下的不同视图,其中,sstatus寄存器的视图是mstatus寄存器的子集,这里将两个寄存器放在一起有助于对比,RV32中xstatus寄存器字段格式(上为mstatus,下为sstatus):
SD | WPRI | TSR | TW | TVM | MXR | SUM | MPRV | XS | FS | MPP | VS | SPP | MPIE | UBE | SPIE | WPRI | MIE | WPRI | SIE | WPRI |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
31 | 30:23 | 22 | 21 | 20 | 19 | 18 | 17 | 16:15 | 14:13 | 12:11 | 10:9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
SD | WPRI | TSR | TW | TVM | MXR | SUM | MPRV | XS | FS | MPP | VS | SPP | MPIE | UBE | SPIE | WPRI | MIE | WPRI | SIE | WPRI |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
31 | 30:23 | - | - | - | 19 | 18 | - | 16:15 | 14:13 | - | 10:9 | 8 | - | 6 | 5 | 4 | - | 2 | 1 | 0 |
RV64中xstatus寄存器字段格式:
SD | WPRI | MBE | SBE | SXL | UXL | WPRI | Same as RV32 |
---|---|---|---|---|---|---|---|
63 | 62:38 | 37 | 36 | 35:34 | 33:32 | 31:23 | 22:0 |
SD | WPRI | MBE | SBE | SXL | UXL | WPRI | Same as RV32 |
---|---|---|---|---|---|---|---|
63 | 62:38 | - | - | - | 33:32 | 31:23 | 22:0 |
仅存在于RV32中的mstatush寄存器字段格式(没有sstatush):
WPRI | MBE | SBE | WPRI |
---|---|---|---|
31:6 | 5 | 4 | 3:0 |
其中,SIE
/MIE
/SPIE
/MPIE
/SPP
/MPP
字段是与异常/中断处理相关的状态,各个字段的意义见异常/中断CSR中的xstatus;
UBE
/SBE
/MBE
字段分别用于说明处理器在U/S/M模式下以大端模式(xBE
=1)还是小端模式(xBE
=0)访问内存。注意,xBE
对于取指令不生效,RISC-V处理器始终按小端模式取指令。对于S特权级的内存管理的隐式内存访问(比如访问页表),都将使用SBE
位决定访问模式。
MPRV
(Modify PRiVilege),仅在支持U模式的处理器中实现。用于说明Load和Store指令执行在什么特权级。当MPRV
=0时,使用当前正在运行的特权级的转换和保护机制来执行LS指令;当MPRV
=1时,以MPP
中的特权级的地址转换和保护机制,以及大小端模式来执行LS指令。SUM
(permit Supervisor User Memory access),用于说明S模式下是否可访问U模式的内存页(U=1,见虚拟内存)。当SUM=0时,S模式访问U模式内存页会产生错误。MXR
(Make eXecutable Readable),仅在支持S模式的处理器中实现。用于改变Load指令访问的虚拟地址权限。当MXR
=0时,Load指令只能访问R=1(见虚拟内存)的内存页;但是当设置MXR
=1时,Load指令可以访问R=1或者X=1的内存页。
TVM
(Trap Virtual Memory),仅在支持S模式的处理器中实现。用于拦截S模式下对虚拟内存进行管理的操作。当TVM
=1时对satp
寄存器的操作以及SFENCE.VMA和SINVAL.VMA指令将产生非法指令异常;当TVM
=0时,这些指令的操作可以被执行。TW
(Timeout Wait),仅在支持S或者U模式的处理器中实现。用于拦截WFI指令。当TW
=0时,WFI指令可以在较低的特权级执行;当TW
=1时,如果WFI指令在低特权级执行,但是没有在规定的时间内完成,将会产生非法指令异常(一般将规定时间设置为0,这样WFI在低特权级执行总是会产生非法指令异常)。TSR
(Trap SRET),仅在支持S模式的处理器中实现。用于拦截SRET指令。当TSR
=1时,在S模式下执行SRET指令会产生非法指令异常;当TSR
=0时,SRET指令正常执行。
FS
/VS
/XS
用于跟踪浮点扩展单元/向量扩展单元/其它扩展单元的状态,SD
位则用来总结这三个字段,以便于快速判断是否有脏数据需要保存。其中,被跟踪的浮点单元主要包括f0
-f31
,fcsr
,frm
,fflags
;向量单元主要包括v0
-v31
,vcsr
,vxrm
,vstart
,vl
,vtype
,vlenb
。这些状态位用于在进行上下文切换时判断是否需要对相应的寄存器进行保存和恢复,以降低处理器上下文切换的开销。两bit位的编码如下:00
=off, 01
=initial, 10
=clean, 11
=dirty。当对应扩展的字段为off时,所有修改相应状态的指令都会产生非法指令异常;当字段为initial时,对应的状态都为初始化的值;当字段为Clean,所有状态可能与初始化值不同,当与上次保存的值相同;当字段为Dirty,存在状态已经被修改。
RV64 mstatus中的SXL
和UXL
分别用于控制S模式和U模式下寄存器的位宽,编码见misa;
异常/中断处理架构
异常/中断CSR
xstatus
- | MPP | - | SPP | MPIE | - | SPIE | - | MIE | - | SIE | - |
---|---|---|---|---|---|---|---|---|---|---|---|
31(63):13 | 12:11 | 10:9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
功能:上表是RV32和RV64中的xstatus寄存器,隐藏了其中与中断无关的字段。MIE
和SIE
分别是M模式和S模式下的全局中断使能位。为了支持中断嵌套,MPIE
和SPIE
分别用于保存进入中断之前的MIE
和SPIE
的值。MPP
和SPP
用于保存中断/异常进入M模式和S模式之前的模式,由于中断是从低特权级别进入高特权级别,因此SPP
只包含一位(可能由U模式和S模式通过中断/异常进入S模式),而MPP
包含两位(可能由U、S、M模式进入M模式)。
xtvec
BASE[:2] | MODE |
---|---|
31(63):2 | 1:0 |
功能:保存中断向量的基址和进入中断处理程序的方式。当MODE=00
时,BASE是所有进入M模式中断/异常处理程序的入口;当MODE=01
时,BASE是所有进入M模式异常处理的入口,BASE+4*cause是对应的需要进入M模式的中断处理程序的入口。MODE>1时暂时保留。
xedeleg/xideleg
xedeleg/xideleg |
---|
31(63):0 |
功能:当x=m时,用于说明哪些中断(xideleg)和异常(xedeleg)不用在M模式下进行处理。如果系统支持S模式,则这两个寄存器必须实现,否则不实现。根据xcause
中的异常号,为每个中断和异常分配1位,来说明在S模式下进行相应的中断处理。需要注意的是,即使设置了相应中断/异常的委托位,如果中断/异常是在M模式下产生的则依然在M模式进行中断处理。
xip/xie
xip/xie |
---|
31(63):0 |
功能:表中每一位分别表示每个中断的使能(xie)和挂起(xip)位,每一位代表的中断与xcause
中的为每个中断分配的异常码一致。
以mip
/mie
寄存器的低16位为例,如下:
0 | MEIP(E) | 0 | SEIP(E) | 0 | MTIP(E) | 0 | STIP(E) | 0 | MSIP(E) | 0 | SSIP(E) | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
15:12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
MEIP(E)
用于M特权等级的外部中断(external interrupt)的使能和挂起;MTIP(E)
用于M特权等级的计时器中断(timer interrupts)的使能和挂起;MSIP(E)
用于M特权等级的软件中断(software interrupts)的使能和挂起。SEIP(E)
/STIP(E)
/SSIP(E)
分别对应S特权等级的相关中断使能和挂起。
xscratch
xscratch |
---|
31(63):0 |
功能:保存x模式硬件线程的上下文地址空间的指针(暂时理解为栈顶指针),当进入x模式时(通过中断处理程序进入),将会和用户寄存器进行交换,恢复x模式的上下文.
xepc
xepc |
---|
31(63):0 |
功能:保存发生中断/异常的指令(虚拟)地址,将再从中断处理程序返回时使用。
xcause
Interrupt | Exception code |
---|---|
31(63) | 30(62):0 |
功能:保存中断/异常产生的原因,Interrupt
位用于说明是中断还是异常。在M模式下支持的中断/异常代码如下表。
RISC-V M模式下中断/异常代码对照表(点击展开)
中断(Interrupt 位) |
异常代码(Exception code) | 说明 | 优先级(越小越高) |
---|---|---|---|
1 | 0 | 保留 | - |
1 | 1 | Supervisor模式软件中断 | - |
1 | 2 | 保留 | - |
1 | 3 | Machie模式软件中断 | - |
1 | 4 | 保留 | - |
1 | 5 | Supervisor模式Timer中断 | - |
1 | 6 | 保留 | - |
1 | 7 | Machine模式Timer中断 | - |
1 | 8 | 保留 | - |
1 | 9 | Supervisor模式外部中断 | - |
1 | 10 | 保留 | - |
1 | 11 | Machine模式外部中断 | - |
1 | 12-15 | 保留 | - |
1 | >16 | 可自定义 | - |
0 | 0 | 指令地址未对齐 | 3 |
0 | 1 | 指令访问错误 | 1/2 |
0 | 2 | 非法指令 | 3 |
0 | 3 | 断点 | 0/3 |
0 | 4 | Load地址未对齐 | 4/7 |
0 | 5 | Load访问错误 | 5/6 |
0 | 6 | Store/AMO地址未对齐 | 4/7 |
0 | 7 | Store/AMO访问错误 | 5/6 |
0 | 8 | User模式ecall | 3 |
0 | 9 | Supervisor模式ecall | 3 |
0 | 10 | 保留 | - |
0 | 11 | Machine模式ecall | 3 |
0 | 12 | 取指令页错误 | 1 |
0 | 13 | Load页错误 | 5 |
0 | 14 | 保留 | - |
0 | 15 | Store/AMO页错误 | 5 |
0 | 16-23 | 保留 | - |
0 | 24-31 | 可自定义 | - |
0 | 32-47 | 保留 | - |
0 | 48-63 | 可自定义 | - |
0 | >64 | 标准保留 | - |
xtval
xtval |
---|
31(63):0 |
功能:保存一些特定的异常信息以辅助软件进行中断处理。具体如下:当遇到硬件断点或者取指/LS指令出现地址未对齐/访问错误/缺页异常时,xtval将保存发生错误的虚拟地址;当出现非法指令异常时可以用来保存产生异常的指令数据。
虚拟内存支持
RISC-V虚拟内存
satp(0x180)
MODE | ASID | PPN |
---|---|---|
31(32)/63:60(64) | 30:22(32)/59:44(64) | 21:0(32)/43:0(64) |
PPN字段保存根页表的物理页号,ASID保存地址空间的标识符,MODE选择地址转换的模式。每个硬件线程的ASID地址空间都是独立的,这意味着不同的hart可能使用相同的ASID表示不同的地址空间。对于32位处理器,MODE
可以为0(Bare
,不支持虚实地址转换)或1(Sv32
,支持手册中Sv32虚拟内存系统);对于64位处理器,MODE
目前定义了0000
、1000
、1001
和1010
,分别表示Bare
、Sv39
、Sv48
以及Sv57
。
对于32位处理器,RISC-V手册中只定义了RV32虚拟内存。以RV32为例,除去12位的页内偏移,虚拟页号还有20位地址空间,因此其包含1M(2^20)个虚拟页。但是satp中的物理页号使用22位表示,手册中提到,所有的20位虚拟页号都会转换为22位物理页号,因此其支持的物理地址空间实际是34位。
SV32系统使用两级页表进行地址转换。每个页表项占4 Bytes空间,因此每个内存页(4 KiB)可以保存1K个页表项(PTE),20位虚拟页号被分为两个10位的VPN[1]和VPN[0],分别用于第一级和第二级地址转换。SV32系统中页表项的字段组成见下表。
PPN[1] | PPN[0] | RSW | D | A | G | U | X | W | R | V |
---|---|---|---|---|---|---|---|---|---|---|
31:20 | 19:10 | 9:8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字段说明(点击展开)
字段 | 取值 | 功能说明 |
---|---|---|
V | 0/1 | 是否有效 |
XWR | 000 | 下一级页表的指针 |
XWR | 001 | 只读页面 |
XWR | 010 | 保留 |
XWR | 011 | 读写页 |
XWR | 100 | 只执行页 |
XWR | 101 | 读-执行页 |
XWR | 110 | 保留 |
XWR | 111 | 读-写-执行页 |
U | 0/1 | U模式是否可访问 |
G | 0/1 | 是否为全局映射(所有程序都可使用) |
A | 0/1 | 页面是否被访问过(读/写/执行) |
D | 0/1 | 页面是否被修改过 |
由X/W/R字段指定页面的权限后,如果从没有执行权限的页面取指令,会产生fetch page-fault异常;如果load或者LR指令访问没有读权限的页面,会产生load page-fault异常;如果store/SC/AMO指令访问没有写权限的页面,会产生store page-fault异常。注意,X/W/R字段中,只要W有效则R一定有效,因此X或者R有效,则表明该页表项为叶子页表项。
RSW
为supervisor软件保留,不需要实现。
地址转换流程,使用satp.PPN和VPN[1]拼接寻址,进行第一次地址转换,得到叶子页表项的物理页基地址(PTE.PPN)。再使用PTE.PNN和VPN[0]进行第二次转换,得到虚拟页号对应的物理页号。手册中提到,在进行第一次转换时,如果得到的PTE.PPN[0] != 0,会产生misaligned superpage异常。
RISC-V虚拟内存异常/中断机制
- 触发page-fault异常时,mtval寄存器将保存发生异常的虚拟地址。
虚拟内存相关处理机制
- SFENCE.VMA指令会无效掉TLB中的内容;
mstatus.TVM
位会拦截S特权级下的虚拟内存管理操作,如修改satp
寄存器以及执行SFENCE.VMA和SINVAL.VMA指令。
物理地址保护
主要CSR
RISC-V特权手册规定最多可以支持64个PMP项,具体实现中可以为支持0/16/64个PMP项。PMP寄存器只能在M模式下访问。对于RV32,使用16个pmpcfg
(pmpcfg0
-pmpcfg15
)保存64个PMP配置;对于RV64,则使用8个pmpcfg
(pmpcfg0
,pmpcfg2
,…,pmpcfg14
)保存64个PMP配置。
RV32 pmpcfgi
寄存器(i=0,1,…,15):
pmp(i*4+3)cfg | pmp(i*4+2)cfg | pmp(i*4+1)cfg | pmp(i*4)cfg |
---|---|---|---|
31:24 | 23:16 | 15:8 | 7:0 |
RV64 pmpcfgi
寄存器(i=0,2,…,14):
pmp(i*4+7)cfg | pmp(i*4+6)cfg | pmp(i*4+5)cfg | pmp(i*4+4)cfg | pmp(i*4+3)cfg | pmp(i*4+2)cfg | pmp(i*4+1)cfg | pmp(i*4)cfg |
---|---|---|---|---|---|---|---|
63:56 | 55:48 | 47:40 | 39:32 | 31:24 | 23:16 | 15:8 | 7:0 |
与64个配置寄存器相对应,PMP机制包含64个地址寄存器。对于RV32,地址寄存器保存物理地址的高32位;对于RV64,地址寄存器保存物理地址的高54位。
RV32 pmpaddri
寄存器(i=0,1,…,63):
address[33:2] |
---|
31:0 |
RV64 pmpaddri
寄存器(i=0,1,…,63):
0 | address[55:2] |
---|---|
63:54 | 53:0 |
由pmpcfgi
寄存器的格式可以看出,每个pmpxcfg占8位,下面是8-bit pmpxcfg的格式(x=0,1,…,63):
L | 0 | A | X | W | R |
---|---|---|---|---|---|
7 | 6:5 | 4:3 | 2 | 1 | 0 |
X/W/R
分别表示对应地址空间允许执行/写/读,这与页表项中的字段类似。A
字段用于说明对应PMP项所匹配的地址空间,当A
=00
时为OFF,即该PMP项无效,不对任何地址空间进行保护;当A
=01
时为TOR(Top of range),即该PMP项对应的pmpaddr
寄存器为其保护的地址空间的上限。假设第i个pmpconf
的A
字段被设置为TOR,则所有位于pmpaddr(i-1)
到pmpaddri
之间的地址,由第i个PMP项控制,如果i=0,则小于pmpaddri
的地址由其控制;如果pmpaddr(i-1)
>pmpaddri
,则该PMP项不对任何空间进行保护。当A
=10
和11
时,分别对应NA4(naturally aligned four-byte regions)和NAPOT(naturally aligned power-of-2 regions),其控制的地址空间大小与其对应的pmpaddr
编码相关。具体如下表所示:
pmpaddr | pmpcfg.A | address region |
---|---|---|
xxxx…xxxx | NA4 | 4 bytes |
xxxx…xxx0 | NAPOT | 8 bytes |
xxxx…xx01 | NAPOT | 16 bytes |
xxxx…x011 | NAPOT | 32 bytes |
… | … | … |
xx01…1111 | NAPOT | 2^xlen bytes |
x011…1111 | NAPOT | 2^(xlen+1) bytes |
0111…1111 | NAPOT | 2^(xlen+2) bytes |
1111…1111 | NAPOT | 2^(xlen+3) bytes |
L
字段用于说明对应的PMP项被锁定,对该PMP项的配置寄存器和地址寄存器的写操作将被忽略。如果第i项被锁定,而其A
字段被配置为TOR,那么对i-1项的地址寄存器的写操作也将被忽略。除此之外,L
字段还用来说明R/W/X
字段的权限限制是否对M模式生效,如果L
=1,R/W/X
的权限限制作用于所有特权等级;如果L
=0,则其只作用于S和U模式,而M模式不受此限制。
机制说明
当处理器从一个没有执行(X)权限的PMP区域取指令时,会产生指令访问错误异常(即xcause中编码为0x1的异常);当使用Load/LR指令访问一个没有读权限的PMP区域时,会产生load访问错误异常(即xcause中编码为0x5的异常);当使用Store/SC/AMO指令访问一个没有写权限的PMP区域时,会产生store访问错误异常(即xcause中编码为0x7的异常)。
如果一个实现了至少一个PMP项,而访存地址没有匹配的PMP项时,S或者U模式下的访问将会失败,M模式的则会成功。对于一个访存地址,其匹配PMP项时将按照PMP寄存器的编号从小到大的顺序匹配。如果一个访问的数据大小超过了PMP项的地址空间范围(例如:访问0x8~0xf,而某一PMP项的地址空间为0x8~0xb),那么将匹配失败。
一条访存指令可能会产生多次访存操作,在多次访存操作中只要出现一次访存错误就会产生访存错误异常
指令介绍
Supervisor Instruction
SFENCE.VMA
SFENCE.VMA指令用于同步内存中用于内存管理的数据结构,也用于无效TLB中当前硬件线程的页表项。实际上,执行SFENCE.VMA指令将刷新与地址翻译有关的所有硬件缓存。指令中的rs1和rs2则用于指定同步的范围,使用rs1指定虚拟地址(vaddr),使用rs2指令地址空间(asid)。如果rs1=rs2=R0,那么SFENCE.VMA指令作用范围为全局所有地址空间;如果rs1=R0,rs2!=R0,SFENCE.VMA指令作用于rs2中定义的地址空间,全局映射不受影响;如果rs1!=R0,rs2=R0,SFENCE.VMA指令作用于所有地址空间的rs1中指定的虚拟地址叶页表项;如果rs1!=R0,rs2!=R0,仅作用于rs2中定义的地址空间中的rs1指定的虚拟地址,全局映射不受影响。如果rs1中的虚拟地址无效,则SFENCE.VMA指令没有任何影响,也不会产生异常。rs2中除ASID位(从0位开始)外,其余清0,硬件不关心。在简单实现中,SFENCE.VMA可以忽略rs1和rs2中的内容,总是全局同步。如果satp.MODE
始终为0(即始终为Bare
,不支持虚实地址转换),那么执行SFENCE.VMA指令将会产生非法指令异常。根据RISC-V手册,以下情况将执行SFENCE.VMA指令:
- 软件循环利用了ASID,需要对相应的ASID地址空间进行同步;更进一步,只要
satp
中的ASID发生变化,就需要执行SFENCE.VMA指令; - 如果硬件不支持ASID,或者软件只使用ASID=0,那么只要对
satp
进行写操作,就需要执行SFENCE.VMA。为了避免刷新全局映射,可以设置rs2!=R0,但是rs2=0; - 如果软件修改非叶节点PTE,那么需要执行rs1=R0的SFENCE.VMA。如果
G
位发生变化,则rs2=R0,否则rs2可以等于对应的ASID; - 如果软件修改了叶节点PTE,那么需要执行rs1等于对应虚拟地址的SFENCE.VMA。同样地,如果
G
位发生变化,则rs2=R0,否则rs2可以等于ASID。 - 如果一个叶页表项的权限或者有效位发生了变化,可以不立即执行SFENCE.VMA指令,等待访问对应页面发生异常时,再执行SFENCE.VMA指令。
其它FENCE指令: FENCE.I指令用于同步指令和数据流. 同一个hart中, FENCE.I保证在它之前的Strore操作对于在它之后的取指操作可见(但无法保证这些Store操作对其它不同的hart的取指操作可见, 要保证对多有hart可见, 需要执行Store的hart执行FENCE, 其余hart执行FENCE.I).
Debug模式
当一个hart因为外部debug信号挂起时进入Debug模式
dcsr(0x7b0)
debugver | 0 | ebreakvs | ebreakvu | ebreakm | 0 | ebreaks | ebreaku | stepie | stopcount | stoptime | cause | v | mprven | nmip | step | prv |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
31:28 | 27:18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8:6 | 5 | 4 | 3 | 2 | 1:0 |
debugver
字段用于说明是否支持debug,0000
表示不支持;0100
表示支持RISC-V标准的debug;1111
表示支持非RISC-V标准的debug
stopcount
字段用于说明进入debug模式后instret
寄存器(记录提交的指令数量)是否继续计数。0
: 正常计数;1
: 对于单hart核,进入debug模式后将停止计数
stoptime
字段用于说明进入debug模式后time
寄存器是否继续计数。0
: 正常计数;1
: 停止计数,当所有hart都进入debug模式并且该位都为1时,mtime
也可以停止计数。
nmip
字段用于说明这个hart存在一个不可屏蔽终端被挂起
prv
字段用于说明进入debug模式之前的特权等级