本期内容如下:
RISC-V特权级别 RISC-V软件栈和特权等级 ISA如何访问CSR 机器模式CSR寄存器详解一、RISC-V的特权等级
RISC-V的特权等级设计共4级。RISC-V的4个特权等级及编码如下图,每1个确定的时间,处理器一定处于这4个特权等级中的一个。 值得一提的是,RISC-V定义Level 2实际上是Hypervisor模式,目前并没写在RISC-V的稳定版手册中。
... 图1 RISC-V特权等级User级中运行用户程序;Supervisor级中运行操作系统内核(和设备驱动);Hypervisor执行虚拟机;Machine级中运行BootLoader和其它固件。 正常情况下,处理器一直在某个特权等级下运行,除非进入trap(诸如软硬件中断、异常等)才有可能发生特权等级的转换。
RISC-V没有要求所有的处理器均实现这4种特权等级,其中Machine模式是必须实现的,而其余3种模式则可根据处理器对于自身功能定位和成本等考量选择实现。 一般来讲微型嵌入式的处理器并没有运行Linux的需求,为了降低功耗、减少面积成本,只会实现Machine模式,或者Machine/User模式。
二、RISC-V软件栈与特权级别
... 图2 不同软件栈分层框图特权级为不同的软件栈部件提供保护。
当编写一个基于RISC-V的MCU的裸机程序时,软件栈的结构如图2(左),此时MCU程序只要具备Machine模式就可以了,当然如果处理同时具备User模式,我们同样可以采用左侧的软件栈结构。2.1 术语
应用执行环境(Application Execution Environment, AEE) 应用程序二进制接口(Application Binary Interface,ABI) 管理员二进制接口(Supervisor Binary Interface, SBI) 管理员执行环境(Supervisor Execution Environment, SEE) 虚拟机监视器管理程序(hypervisor) hypervisor二进制接口(Hypervisor Binary interface,HBI) hypervisor执行环境(Hypervisor Execution Environment, HEE)3 ISA如何访问CSR
3.1 存取CSR的指令格式
... 图3 访问CSR汇编指令3.2 存取CSR汇编指令举例
CSRRW(Atomic Read/Write CSR):指令原子性的交换 CSR 和整数寄存器中的值。 CSRRW指令读取在 CSR 中的旧值,将其零扩展到 XLEN 位,然后写入整数寄存器 rd 中。 rs1 寄存器中的值将被写入 CSR 中。csrrw a0,misa,zero //读取misa(machine ISA register)"机器模式指令集架构寄存器"内容到a0参数寄存器中 //a0 为之前提到的rd,zero为之前提到的rs1,misa为csr寄存器名12bit地址偏移0x301
CSRRS(Atomic Read and Set Bit in CSR):rs1 中对应bit为 1 的位,将导致 CSR 中对应位被置为 1。
li a1,0x3 csrrs a0, mie,a1 //读取mie(machine interrupt enable register) 机器模式中断使能寄存器内容到a0参数寄存器中 //设置mie的bit0( USIE),bit1( SSIE) 为1,也就是使能用户模式和supervisor模式下的软中断
li a1,0x3 csrrc a0, mie,a1 //读取mie(machine interrupt enable register) 机器模式中断使能寄存器内容到a0参数寄存器中 //设置mie的bit0( USIE),bit1( SSIE) 为0,也就是禁用用户模式和supervisor模式下的软中断
注意:
对于 CSRRS 指令和 CSRRC 指令,如果 rs1=x0,那么指令将根本不会去写 CSR。
csrrc a0, mie,zero //读取mie数据到a0,由于rs1为zero,硬件层面不会有写wie的动作 csrrs a1, mie,zero //读取mie数据到a1中,由于zero就是risc-v x0的别名,所以这两条指令读到的结果是相同的 //mie内容也不会改变
如果 rs1 寄存器包含的值是 0,而不是 rs1=x0,那么将会把一个不修改的值写回 CSR(这时会有一个写CSR 的操作,但是写入的值就是旧值,因此可能会产生副作用)
li a1,0x0 csrrc a0, mstatus,a1 //mstatus 机器模式状态寄存器(machine status register) //由于a1=0,它和zero不是一个寄存器,这时硬件层面会有写mstatus的动作,虽然0, //意味着csr不会改变任何bit,但是原先mstatus数值依然会在硬件层面进行一次写如操作, //这就可能带来一个问题就是,当正在更新状态数值时,csrrc突然进行了一次原始数据的重 //写操作,从而导致更新状态信息bit被丢失了
CSRRWI
csrrwi zero,mstatus,1 //只写1数值到mstatus中,不读csr数据
CSRRSI
csrrsi zero,mstatus,0x02 //只设置bit2数值为1,不读csr数据
CSRRCI
csrrci zero,mstatus,0x01 //只清空bit0,不读csr数据
四、CSR寄存器详解
有关RISC-V有哪些CSR的寄存器,我在上一讲已经介绍了,在此不做赘述,请需要的同学翻看《我们一起学RISC-V——01-了解处理器和寄存器》。RISC-V规定的标准CSR寄存器并不是所有的处理器都实现了,我们需要知道,不同的处理器CSR具体实现是有差异的,这就需要我们在使用时,根据具体情况编写代码。但是有一些必须实现的CSR我在这里将作为介绍重点,有些不太常用的我们在今后的学习过程中用到时再具体介绍。
下面针对具体寄存器进行介绍,内容可能有些多,还请耐心看下去。
首先了解一下CSR区域规范
以下定义和缩写用于指定CSR字段的的行为。
Reserved Writes Preserve Values, Reads Ignore Values (WPRI)保留写入保留值,读忽略数值一些完整的读/写字段被保留以备将来使用。软件应忽略从这些字段中读取的值,并在将值写入同一寄存器的其他字段时保留这些字段中保存的值。为了向前兼容,不提供这些字段的实现必须将它们硬连接到零。这些字段在寄存器描述中被标记为WPRI。
Write/Read Only Legal Values写入/只读合法值(WLRL)有些读/写CSR字段只为可能位编码的一个子集指定行为,而保留其他位编码。软件不应向此类字段写入除合法值以外的任何内容,并且不应假定读取将返回合法值,除非最后一次写入是合法值,或者在另一次操作(如重置)将寄存器设置为合法值后,寄存器尚未写入。这些字段在寄存器描述中被标记为WLRL。
如果指令试图将不受支持的值写入WLRL字段,则允许实现,但不要求实现引发非法指令异常。当最后一次写入是非法值时,实现可以在读取WLRL字段时返回任意位模式,但返回的值应确定地取决于非法写入的值和写入前字段的值。
Write Any Values, Reads Legal Values 写任何值,读取合法值(WARL)有些读/写CSR字段只为位编码的子集定义,但允许写入任何值,同时保证每次读取时都返回合法值。假设编写CSR没有其他副作用,可以通过尝试写入所需的设置,然后读取以查看是否保留了该值来确定支持值的范围。这些字段在寄存器描述中被标记为WARL。
在将不受支持的值写入WARL字段时,实现不会引发异常。当最后一次写入是非法值时,实现可以在读取WARL字段时返回任何合法值,但返回的合法值应确定地取决于非法写入的值和hart的体系结构状态。
如果更改了CSR的宽度(例如,如第3.1.6.2节所述,通过更改MXLEN或UXLEN),则新宽度CSR的可写字段和位的值将根据之前的宽度CSR确定,就像通过此算法一样:
上一个宽度CSR的值被复制到相同宽度的临时寄存器中。 对于先前宽度CSR的只读位,临时寄存器中相同位置的位被设置为零。 临时寄存器的宽度将更改为新的宽度。如果新的宽度W比先前的宽度窄,则保留临时寄存器的最低有效W位,并丢弃较高的有效位。如果新的宽度比先前的宽度宽,则临时寄存器将零扩展到更宽的宽度。 新宽度CSR的每个可写字段取临时寄存器中相同位置的位的值。更改CSR的宽度不是对CSR的读写操作,因此不会引发任何副作用。
4.1 机器模式下的CSRs
在机器模式下指令可以访问所有的CSR。
4.1.1 misa(Machine ISA Register)机器指令集架构寄存器
misa是一个(WARL)可读写的寄存器,用于上报处理器支持指令集架构。任何处理起在实现是都必须实现可读属性,但是如果处理起不想致辞misa,应当返回一个0值。
“MXL位域"占两个bit,用于标识处理器ISA占用位宽,具体参见图5,当然有些处理器也可能将MXL设置为可写入的,这样处理器便可以支持多种ISA位宽,当处理器复位的时候MXL总是被设定为其所支持的最大位宽对应的编码。
... 图5 MXL位域编码(misa)misa的长度总是和MXLEN相等的,如果读取的misa是非0值,那XML的数值总是指示当前处理器的位宽,如果misa数值被改变,MXL的2bit将会被移动到,新位宽misa存器的最高2bit上。
"扩展位域(Extensions)"占用26bit,每1bit用大写字母A-Z为之命名,0bit对应A,1bit对应B,以此类推25bit对应Z。“I”bit将被设定为基本指令级如RV32I,RV64I和RV128I,“E”bit将用于RV32E,扩展域是支持WARL的,这也就是说有些bit是可以支持写入的,随时可被修改的,
"U"和“S”被用于标示是否支持user模式和supervisor模式。
如果处理器有任何非标设计,“X”都将被设置。
注意:在扩展位域中位之间存在依赖关系,如果x依赖于y,那么禁用y,而使能x,将会导致x,y两个功能均无法正常使用,例如:设定“F”=0,“D”=1,将导致F和D都不能使用。
扩展位域详细定义下图6.
... 图6 misa扩展位域定义,所有预留bit当被读取时应该返回04.1.2 mvendorid(Machine Vendor ID Register)机器厂商ID寄存器
mvendorid是一个32bit的只读寄存器,遵循JEDEC 制造商ID规范,任何处理器都必须实现只读功能,如果处理器不支持该功能,应当返回0.
JEDEC制造商ID通常编码为一个字节连续码0x7f的序列,以不等于0x7f的单字节ID结尾,每个字节的最高有效位是奇偶校验位。
”Bank位域“用于记录吧0x7f的个数,“offset位域”用于记录则为最后一个字节的低7bit(不包含最高校验位)。
例如:
JEDEC 制造商ID "0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x8a",12个0x7f,最后一字节为0x8a,最终标示mvendorid数值为0x60a.
4.1.3 marchid(Machine Architecture ID Register)机器结构体系ID寄存器
... 图8 marchid寄存器结构marchid的位数依赖于MXLEN,可能有32,64,128bit,开源处理器的ID是RISC-V基金会统一分配的最高bit被设置为0,商业处理器ID被每个商业处理器厂商独立分配,但是最高为必须为1,剩余MXLEN-1bit不能等于0.
4.1.4 mimpid(Machine Implementation ID Register)机器实现ID寄存器
mimpid提供了与处理器版本不同的编码,就类似软件的build号,任何处理都是可读的,反应的的是各家处理起本身的差异,而不是与其他家处理器的差异。
4.1.5 mhartid(Hard ID register)硬件ID寄存器
mhartid是一个MXLEN bit的只读寄存器,保存处理器当前正运行时软件的线程ID信息,处理器必须实现mhartid的读取功能,在多核处理器中hardid不一定是连续编号的,但至少有一个hard的hardid是0,同时保证运行环境中hardid的互不相同。
... 图9 mhartid寄存器结构4.1.6 mstatus and mstatush(Machine Status Registers)机器状态寄存器
mstatus是一个MXLEN bit的可读写寄存器,RV64参见图10,RV32参见图11,mstatus用于追踪和控制处理器的当前的操作状态。
... 图11 RV32 mstatus结构mstatush是一个32bit的可读写寄存器只有RV32才有,格式参见图12,不支持的位域将硬件上被设为0. mstatush的30:4bit通常包含和RV64 mstatus [62:36]相同的结构,SD,SXL和UXL是不包含在mstatush中的。
... 图12 mstatush结构由于mstatus的结构比较复杂,我将单独写一篇文章用于介绍mstatus,请关注后续文章。
4.1.7 mtvec(Machine Trap-Vector Base-Address Register )机器陷阱向量基地址寄存器
mtvec是一个可读写的MXLEN位宽的寄存器,低2bit为向量模式位域,剩余高MXLEAN-2高位为基地址位域。
... 图13 mtvec结构说白了就是一个缺陷向量地址的入口地址寄存器,当任何trap发生时,处理器都会跳转到向量表对应的地址偏移上执行对应的程序,也类似于ARM中的异常中断向量表的地址的概念。
任何处理器都必须实现mtvec,mtvec可以是固定的,也可以是可设定的,但是mtvec设定值必须是4字节对齐的,写入mtvec时应当注意mode位域的限定。
... 图14 模式位域编码”模式位域“编码参见图14,当为direct直接模式时,当有任何trap发生时,PC指针将会被设定为base;当被设定为向量模式时,当任何trap发生时,pc将会被设定为base+num*4;num为trap对应的编号,通常就是中断号。
4.1.8 medeleg and mideleg(Machine Trap Delegation Registers)机器陷阱托管寄存器
默认情况下,任何特权级别的所有Trap都是在机器模式下处理的,尽管机器模式处理程序可以使用MRET指令将陷阱重定向回适当的级别。为了提高性能,实现可以在medeleg和mideleg中提供单独的读/写位,以指示某些异常和中断应该由较低的权限级别直接处理。机器异常代理寄存器(medeleg)和机器中断委托寄存器(mideleg)是MXLEN位的读/写寄存器。
medeleg mideleg都是为了提高处理器应对对异常处理性能而设定的,它可以在不需要切换处理器工作模式的情况下,直接在较低特权模式下处理相应的异常。
在具有Supervisor模式的系统中,medeleg和mideleg寄存器必须存在,并且在medeleg或mideleg中设置一个位,将在Supervisor模式或User模式下,发生rap时,相应的trap处理将委托给Supervisor模式trap处理程序。在没有S模式的系统中,medeleg和mideleg寄存器不应存在(除非实现了用户模式中断的N扩展)。
当trap被委托给S模式时,trap的原因被写入scause寄存器;trap发生时的指令虚拟地址被写入sepc寄存器;使用异常特定数据被写入stval寄存器;在陷阱发生时,当前激活的特权模式被写入mstatus的SPP字段;当Trap发生时的SIE字段值被写入mstatus的SPIE字段,mstatus的SIE字段被清除。mcause、mepc和mtval寄存器以及mstatus的MPP和MPIE字段不会被写入。
Trap从不会从特权更大的模式转换到特权更小的模式。例如,如果M-mode将非法指令异常委托给S-mode,并且M-mode软件稍后执行一个非法指令,那么Trap将在M-mode中产生,而不是被委托给S-mode。相比之下,Trap可以水平发生的。使用相同的示例,如果M-mode将非法指令异常委托给S-mode,并且S-mode软件稍后执行非法指令,则在S-mode中捕获Trap。
被委托的中断会导致中断在委托权限级别被屏蔽。例如,如果通过设置mideleg[5]将supervisor timer interrupt (STI)委派给s模式,则在m模式下执行时将不会执行STIs。相比之下,如果mideleg[5]是清除的,STIs可以在任何模式下被采取,不管当前模式将转移控制到m模式。
medeleg每1位被设置后,就意味着在更低特权模式中,该特权模式下可以直接处理trap程序,每1位的位号值就是mcause的返回值,各位定义参见图17.
mideleg每位的对应的中断和mip寄存器对应的中断是相同的。
... 图17 medeleg异常编号4.1.9 mip和mie(Machine Interrupt Registers)机器中断寄存器
mip是一个MXLEN位的可读写寄存器,包含有中断的pending信息,与mip相对应的mie也是MXLEN位,每1位控制着中断的使能和禁止,0-15bit是定义的标准终端号,剩下的16bit可用于处理器厂商的自定义中断.具体情况参见图18。
... 图18 中断号定义 ... 图19 mip、mie结构 ... 图20 mip寄存器15:0结构 ... 图21 mie寄存器15:0结构 MEIP和MEIE是机器特权级别下的外部中断pending和使能位,MEIP为只读的,其值受控于具体平台的中断控制器。 MTIP和MTIE为机器特权级别下的定时器中断pending和使能位,MTIP为只读,其数值受控于机器模式的定时器。 MSIP和MSIE为机器特权级别下的软件中断pending和使能位,MSIP为只读,其数值受控于机器模式的内存映射控制器。如果supervisor模式不支持的话,SEIP和SEIE,STIP和STIE,SSIP和SSIE应当被设置为0。
注意suoervisor中的SEIP,STIP和SSIP都是可读写的,以便接收来自及其模式的中断通知。
4.1.10 mtime and mtimecmp(Machine Timer Registers)机器定时器寄存器
mtime是一个64bit的内存映射的可读写的机器模式下的寄存器,mtime的时钟必须是固定的,同时向上增长型的。具体处理器平台必须提供一个状态机用于确定mtime的时间基准,如果计数器溢出将会产生一个trap。
平台必须提供一个内存映射的定时器比较器,如果mtime数值大于等于mtimecmp时,定时器中断将会产生。
注意:
由于mtime和mtimecmp不是csr,而是内存映射,所以存取它们不能使用csr存取指令,应当采用通用内存操作相关的指令
# New comparand is in a1:a0. li t0, -1 la t1, mtimecmp sw t0, 0(t1) # No smaller than old value. sw a1, 4(t1) # No smaller than new value. sw a0, 0(t1) # New value.
4.1.10硬件性能监测器
机器模式包含有一个硬件性能监测装置。
mcycle 保存硬件运行期间,处理器核心执行的,执行的时钟周期数,64 bit位宽;
minstret记录硬件指令重试次数,64 bit位宽
计数器的数值在处理器reset后是随机的,可以写入任何数值,写入指令立即生效。mcycle适合共享的在相同的core上,mcycle对于其他硬件来说,其数值的改变是可被观察到的,平台应当提供一个状态机用于了解那些外设是可以共享mcycle的。
硬件性能检测器包含29个附加的64bit事件计数器,mhpmcounter3–mhpmcounter31
还有事件选择器mhpmevent3–mhpmevent31,它们位宽都是MXLEN。这些事件计数器的定义都是处理器平台自定义的,
在RV32上,读取mcycle, minstret, and mhpmcountern CSRs返回的只是低32bit数值,要获取高32bit,可以使用mcycleh, minstreth, and mhpmcounternh CSRs。
4.1.11 mcounteren (Machine Counter-Enable Register)机器计数器使能寄存器
mcounteren是一个32bit的,用于控制性能检测器的有效性在低一级特权等级模式下,该寄存器控制对应计数器的存取操作。
IR,TM,CY,HPMn分别对应instret,time,cycle和hpmcountern寄存器,当设置对应bit时,对应计数器可用,否则操作寄存器将会产生指令异常。
... 图25 mcounteren结构4.1.12 mcountinhibit (Machine Counter-Inhibit CSR)机器计数器禁用寄存器
mcountinhibit 是一个32bit的WARL寄存器,该寄存器仅仅影响计数器的增加与否,不影响计数器的存取操作。当对应bit设置为1时,计数器数值不会增加。
... 图26 mcountinhibit结构4.1.13 mscratch(Machine Scratch Register)
mscratch是一个MXLEN位宽可读写寄存器,通常,它用于保存指向机器模式hart-local上下文空间的指针,并在进入m模式trap处理程序时与用户寄存器交换。
... 图27 mscratch结构4.1.14 mepc(Machine Exception Program Counter)机器异常程序计数器
mepc是一个MXLEN可读写寄存器,寄存器格式参见图28,低位mepc[0]总是0,在IALIGN=32的实现上,mepc[1:0]总是为0.
当trap进入m模式时,中断或遇到异常时,指令的虚拟地址被写入mepc。否则,虽然mepc可能被软件显式地写,但它从不由实现来编写。
... 图28 mepc结构4.1.15 mcause(Machine Cause Register )机器原因寄存器
... 图29 mcause结构mcause是一个MXLEN位宽的可读写寄存器,当一个trap发生在机器模式下时,mcause被写入一个数值用以指示trap的类型。否则mcause不会被实现写入,虽然mcause可被软件显示写入。
inerrupt位将被设置,如果trap是由中断引起的, exception位域用于记录最后一次异常编码, ... 图30 mcause trap返回值4.1.16 mtval(Machine Trap Value Register)机器陷阱数值寄存器
... 图31 mtval结构mtval为MXLEN位宽可读写寄存器,对于给寄存器主要用于保存各种陷阱的状态数值。至于是什么数值,不同的trap类型,数值类型也不相同。
至此机器模式下CSR寄存器的介绍就告一段落。