我们一起学RISC-V——02-深入了解机器模式下的CSR

我们一起学RISC-V——02-深入了解机器模式下的CSR
...

本期内容如下:

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(中间)软件栈结构进行开发,RISC-V处理应当至少同时支持Machine,User和Supervisor模式。 当需要用到虚拟化技术的时候,就应当选用图2(右)所示的软件栈结构了。

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)
左边,单个应用程序被编码在ABI上运行。这个ABI包含所支持的用户级ISA和与同AEE交互的ABI调用。ABI对应用程序隐藏了AEE的细节,使得AEE具有更大的灵活性。 中间加了一个传统的操作系统,可支持多个应用程序的多道运行。每个应用程序通过ABI和OS进行通信。RISC-V操作系统通过一个SBI和SEE进行通信。这个SBI包含所支持的用户级和管理员级别的ISA,还包含与SEE交互的SBI调用。 右边配置了一个虚拟机监视器配置。 HAL不是必须的。

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模式下的软中断
CSRRC(Atomic Read and Clear Bit in CSR):rs1 中对应bit为 1 的位,将导致 CSR 中对应位被清0. 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)机器指令集架构寄存器


... 图4 misa结构

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当被读取时应该返回0

4.1.2 mvendorid(Machine Vendor ID Register)机器厂商ID寄存器


... 图7 mvendorid寄存器结构

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寄存器


... 图9 mimpid寄存器结构

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高位为基地址位域。

说白了就是一个缺陷向量地址的入口地址寄存器,当任何trap发生时,处理器都会跳转到向量表对应的地址偏移上执行对应的程序,也类似于ARM中的异常中断向量表的地址的概念。

... 图13 mtvec结构

任何处理器都必须实现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模式。


... 图15 medeleg结构 ... 图16 mideleg结构

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)机器定时器寄存器


... 图22 mtime结构 ... 图23 mtimecmp结构

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。


... 图24 硬件性能监测器高低寄存器,高寄存器仅用于RV32

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寄存器的介绍就告一段落。