修订历史

版本号 修订日期 修订的章节 修订的内容
1.0 2019/5/12 N/A 初始版本
1.1 2019/7/29 5.3.2.3,6.2.4,7.2,7.4.39,9.2.2,9.2.3,9.4 1. 添加了存储器访问错误中断(5.3.2.3,6.2.4)
2. 修改了mmisc_ctl寄存器(7.2,7.5.5)
3. 添加了DSP相关的寄存器(7.2,7.4.39)
4. 修改了WFE的相关描述(9.2.2,9.2.3,9.4)
1.2 2019/10/8 1.2 删除了N扩展的描述,增加了TEE相关的描述。
1.3 2019/12/17 3.4.2,4.6,6.1.7,6.2.4 1. 添加了mdcause寄存器
2. 删除了bus error转内部中断的描述
3. 添加了MSFTRST寄存器的描述
1.4 2020/1/9 6.1.2,6.1.4 将mstop改名为mtimectl,并添加了自清使能位以及TIMER计数时钟模式位
1.5 2020/1/20 7.5.5 增加了Scratchpad的相关描述
1.6 2020/2/17 1.3, 8.1 6.2.3, 6.2.4, 6.2.5 1. 删除了型号N201的描述
2. 将ECLIC的编程模型中的理论最大中断数目修正为4096
1.7 2020/3/11 6.2.5 修改错误描述,将mth寄存器的地址由0x8改成0xb

1. 内核指令集与CSR介绍

1.1. RISC-V指令集介绍

N级别处理器内核遵循的标准RISC-V指令集文档版本为:“指令集文档版本2.2”(riscv-spec-v2.2.pdf)。用户可以在RISC-V基金会的网站上需注册便可关注并免费下载其完整原文(https://riscv.org/specifications/)。

除了RISC-V “指令集文档版本2.2”英文原文之外,用户还可以参阅中文书籍《手把手教你设计CPU——RISC-V处理器篇》的附录A、附录C\~G部分,其使用通俗易懂的中文对RISC-V指令集标准进行了系统讲解。

1.2. N级别处理器内核支持指令集

RISC-V指令集基于模块化设计,可以根据配置进行灵活组合。Nuclei N级别处理器内核支持的是如下模块化指令集:

  • RV32架构:32位地址空间,通用寄存器宽度32位。

  • I:支持32个通用整数寄存器。

  • M: 支持整数乘法与除法指令

  • C:支持编码长度为16位的压缩指令,提高代码密度。

  • A:支持原子操作指令。

  • F:支持单精度浮点指令。

  • D:支持双精度浮点指令。

  • N:支持用户异常和中断在用户模式下处理。

  • P:支持数字信号处理指令。

按照RISC-V架构命名规则,以上指令子集的组合可表示为RV32IMAC, RV32IMFC, RV32IMAFDC,RV32IMAFDCNP等。RISC-V架构定义IMAFD为通用组合(General Purpose),以字母G表示,因此RV32IMAFDC也可表示为RV32GC。

注意:

  • N级别处理器内核是Nuclei的一个处理器内核(Processor Core)级别,这个级别中包含了N200,N300,N600,N900四个系列,每个系列中具体每一款处理器内核(譬如N203,N205等)支持的指令子集组合可能略有差异(譬如N203可以配置为仅支持RV32IMC),请参见各个系列的简明数据手册了解其详情。

  • N100系列是一个极小面积的处理器内核系列,其进行了大量的架构删减以求达到极小的硬件面积,因此其不遵循本文档中定义的架构细节。有关N100系列支持的指令集和架构详情请参见《Nuclei N100系列指令架构手册》。

另外,本文中所描述的架构并不支持TEE(Turst Execution Environment)安全特性,如需了解TEE相关架构的更多信息,请参见《Nuclei TEE Architecture》文档。

1.3. CSR寄存器

RISC-V的架构中定义了一些控制和状态寄存器(Control and Status Register,CSR),用于配置或记录一些处理器核的运行状态。CSR寄存器是处理器核内部的寄存器,使用其专有的12位地址编码空间。详情请参见第7章。

2.内核特权架构介绍

2.1. 总体介绍

N级别处理器内核遵循的标准RISC-V特权架构文档版本为:“特权架构文档版本1.10”(riscv-privileged-v1.10.pdf)。用户可以在RISC-V基金会的网站上需注册便可关注并免费下载其完整原文(https://riscv.org/specifications/)。

除了RISC-V “特权架构文档版本1.10”英文原文之外,用户还可以参阅中文书籍《手把手教你设计CPU——RISC-V处理器篇》的附录A、附录C\~G部分,其使用通俗易懂的中文对RISC-V特权架构标准进行了系统讲解。

2.2. 特权模式(Privilege Modes)

N级别处理器内核支持两个特权模式(Privilege Modes):

  • 机器模式(Machine Mode)是必须的模式,该Privilege Mode的编码是0x3。

  • 用户模式(User Mode)是可配置的模式,该Privilege Mode的编码是0x0。

    • 如果配置了User Mode,则也支持PMP(Physical Memory Protection)单元对物理地址区间进行隔离和保护。

注意:如果配置了TEE特性,则还支持监督模式(Supervisor Mode),如需了解TEE相关架构的更多信息,请参见《Nuclei TEE Architecture》文档。

2.2.1. 机器模式(Machine Mode)

N级别处理器内核有关Machine Mode的关键要点如下:

  • 处理器内核被复位后,默认处于Machine Mode。

  • 在Machine Mode下,程序能够访问所有的CSR寄存器。

  • 在Machine Mode下,程序能够访问所有的物理地址区域(除了PMP禁止访问的区域之外,请参见第8章了解PMP的详情)。

2.2.2. 用户模式(User Mode)

N级别处理器内核有关User Mode的关键要点如下:

  • 在User Mode下只能够访问User Mode限定的CSR寄存器,请参见第7.3节了解详情。

  • 在User Mode下只能够访问PMP设定权限的物理地址区域,请参见第8章了解PMP的详情。

2.2.3. 机器子模式(Machine Sub-Mode)

N级别处理器内核的Machine Mode可能处于四种不同的状态下,将之称之为机器子模式(Machine Sub-Mode):

  • 正常机器模式(该Machine Sub-Mode的编码是0x0):

    • 处理器内核被复位之后,处于此子模式之下。处理器复位后如果不产生异常、NMI、中断,则一直正常运行于此模式之下。
  • 异常处理模式(该Machine Sub-Mode的编码是0x2):

    • 响应异常后处理器内核处于此状态。

    • 有关异常机制的详情,请参见第3章。

  • NMI处理模式(该Machine Sub-Mode的编码是0x3):

    • 响应NMI后处理器内核处于此状态。

    • 有关NMI机制的详情,请参见第4章。

  • 中断处理模式(该Machine Sub-Mode的编码是0x1):

    • 响应中断后处理器内核处于此状态。

    • 有关中断机制的详情,请参见第5章。

处理器内核当前处于的Machine Sub-Mode反映在CSR寄存器msubm的TYP域中,因此软件可以通过读取此CSR寄存器查看当前处于的Machine Sub-Mode。有关msubm寄存器的详情,请参见第7.5.3节。

注意:在RISC-V架构中,进入异常、NMI或者中断也被统称为Trap。

2.2.4. 模式(Mode)的查看

处理器模式(Mode)查看的关键要点如下:

  • 根据RISC-V的架构定义,处理器当前的Machine Mode或者User Mode并没有反映在任何软件可见的寄存器中(处理器内核会维护一个对软件不可见的硬件寄存器),因此软件程序无法通过读取任何寄存器而查看当前自己所处的Machine Mode或者User Mode。

  • N级别处理器内核的四种机器子模式(Machine Sub-Mode)反映在CSR寄存器msubm的TYP域中,因此软件可以通过读取此CSR寄存器查看当前处于的Machine Sub-Mode。

2.2.5. Machine Mode到User Mode的切换

在Machine Mode下可以直接执行mret指令。从Machine Mode切换到User Mode只能通过执行mret指令发生。由于如第2.2.3节中所述,Machine Mode可能处于四种不同的状态下,分别介绍如下:

  • 如果是在正常机器模式下,执行mret指令的硬件行为与异常处理模式下执行mret指令的行为相同,请参见第3.5节了解其详情。

    • 因此,如果在正常机器模式下,希望从Machine Mode切换到User Mode,那么需要软件先修改mstatus的MPP域的值,然后执行mret指令达到模式切换的效果。典型的程序代码片段如下所示:

    /* Switch Machine sub-mode to User mode */
    li t0, MSTATUS_MPP  // MSTATUS_MPP的值为0x00001800,即对应mstatus的MPP位域,请参
                    // 见第7.4.7节了解mstatus的位域详情。
    csrc mstatus, t0    // 将mstatus寄存器的MPP位域清为0
    la t0, 1f         // 将前面的标签1所在的PC地址赋值给t0          
    csrw mepc, t0    // 将t0的值赋值给CSR寄存器mepc
    mret               // 执行mret指令,则会将模式切换到User Mode,并且从前的标签1处开始执行
                    // 程序(标签1即为mret的下一条指令的位置)
1:                      // 标签1的位置

  • 如果是在异常处理模式下,执行mret指令的硬件行为,请参见第3.5节了解其详情。

    • 通常来说,mret指令用于从异常处理模式下退出至进入异常之前的模式。

    • 如果明确希望从Machine Mode退出至User Mode(或者正常机器模式),那么需要软件先修改mstatus的MPP域的值,然后执行mret指令达到模式切换的效果。

  • 如果是在中断处理模式下,执行mret指令的硬件行为,请参见第5.7节了解其详情。

    • 通常来说,mret指令用于从中断处理模式下退出至进入中断之前的模式。

    • 如果明确希望从Machine Mode退出至User Mode(或者正常机器模式),那么需要软件先修改mstatus的MPP域的值,然后执行mret指令达到模式切换的效果。

  • 如果是在NMI处理模式下,执行mret指令的硬件行为,请参见第4.4节了解其详情。

    • 通常来说,mret指令用于从NMI处理模式下退出至正常机器模式。

    • 如果明确希望从Machine Mode退出至User Mode(或者正常机器模式),那么需要软件先修改mstatus的MPP域的值,然后执行mret指令达到模式切换的效果。

注意:

  • 如果在User Mode下直接执行mret指令会产生非法指令(Illegal Instruction)异常。

  • 在User Mode下处理器只能够访问PMP设定权限的物理地址区域,因此在切换到User Mode之前,需要配置PMP相关寄存器设定User Mode 可以访问的物理地址区域,请参见第8章了解其详情。

2.2.6. User Mode到Machine Mode的切换

N级别处理器内核从User Mode切换到Machine Mode只能通过异常、响应中断或者NMI的方式发生:

  • 响应异常进入异常处理模式。请参见第3.4节了解其详情。

    • 注意:软件可以通过调用ecall指令强行进入ecall异常处理函数。
  • 响应中断进入中断处理模式。请参见第5.6节了解其详情。

  • 响应NMI进入NMI处理模式。请参见第4.3节了解其详情。

2.2.7. 中断、异常、NMI的嵌套

中断和异常能自我发生嵌套,NMI无法自我嵌套:

  • 在NMI处理模式下,如果再次发生NMI,新来的NMI会被屏蔽掉,因此,NMI无法自我嵌套,请参见第4.6节了解详情。

  • 在异常处理模式下,如果再次发生异常,这属于异常嵌套情形,请参见第3.7节了解详情。

  • 在中断处理模式下,如果再次发生中断,这属于中断嵌套情形,请参见第5.11节了解详情。

中断、异常和NMI彼此之间也可能会发生嵌套,存在如下情形:

  • 在中断处理模式下发生了异常,则进入异常处理模式。

  • 在NMI处理模式下发生了异常,则进入异常处理模式。

  • 在中断处理模式下发生了NMI,则进入NMI处理模式。

  • 在异常处理模式下发生了NMI,则进入NMI处理模式。

  • 注意:在NMI和异常模式下默认由于全局中断位被硬件自动关闭,因此不会再响应中断。

为了能够保证异常和NMI彼此之间发生嵌套后还能够恢复到之前的状态(Recoverable),N级别处理器内核实现了一种“两级NMI/异常状态堆栈(Two Levels of NMI/Exception State Save Stacks)”技术,请参见第4.6节了解更多详情。

2.3. 物理存储器保护(PMP)

由于N级别处理器内核是面向微控制器领域的低功耗内核,其不支持虚拟地址管理单元(Memory Management Unit),因此所有的地址访问操作都是使用的物理地址。为了根据不同的存储器物理地址区间和不同的Privilege Mode进行权限隔离和保护,N级别处理器内核支持物理存储保护,即(Physical Memory Protection,PMP)单元。有关PMP单元的详情,请参见第8章。

3. 内核异常机制介绍

3.1. 异常概述

异常(Exception)机制,即处理器核在顺序执行程序指令流的过程中突然遇到了异常的事情而中止执行当前的程序,转而去处理该异常,其要点如下:

  • 处理器遇到的“异常的事情”称为异常(Exception)。异常是由处理器内部事件或程序执行中的事件引起的,譬如本身硬件故障、程序故障,或者执行特殊的系统服务指令而引起的,简而言之是一种内因。

  • 异常发生后,处理器会进入异常服务处理程序。

3.2. 异常屏蔽

RISC-V架构中规定异常是不可以被屏蔽的,也就是说一旦发生了异常,处理器一定会停止当前操作转而进入异常处理模式。

3.3. 异常的优先级

处理器内核可能存在多个异常同时发生的情形,因此异常也有优先级。异常的优先级如表 3‑1中所示,异常编号数字越小的异常优先级越高。

3.4. 进入异常处理模式

进入异常时,N级别处理器内核的硬件行为可以简述如下。注意,下列硬件行为在一个时钟周期内同时完成:

  • 停止执行当前程序流,转而从CSR寄存器mtvec定义的PC地址开始执行。

  • 更新相关CSR寄存器,分别是以下几个寄存器:

    • mcause(Machine Cause Register)

    • mdcause (Machine Detailed Trap Cause Register)

    • mepc(Machine Exception Program Counter)

    • mtval(Machine Trap Value Register )

    • mstatus(Machine Status Register)

  • 更新处理器内核的Privilege Mode以及Machine Sub-Mode。

异常响应总体过程如图 3‑1所示。

图 3‑1 异常响应总体过程

下文将分别予以详述。

3.4.1. 从mtvec定义的PC地址开始执行

N级别处理器内核遇到异常后跳入的PC地址由CSR寄存器mtvec指定。

mtvec寄存器是一个可读可写的CSR寄存器,因此软件可以编程更改其中的值。mtvec寄存器的详细格式如表 7‑3所示。

3.4.2. 更新CSR寄存器mcause和mdcause

N级别处理器内核在进入异常时,CSR寄存器mcause和mdcause被同时(硬件自动)更新,以反映当前的异常种类,软件可以通过读此寄存器查询造成异常的具体原因。

mcause寄存器的详细格式如表 7‑6所示,其中低5位为异常编号域,用于指示各种不同的异常类型,如表 3‑1所示。

表 3‑1 mcause寄存器中的Exception Code以及对应的mdcause

异常编号(Exception Code) 异常和中断类型 同步/异步 描述
0 指令地址非对齐(Instruction address misaligned) 同步 指令PC地址非对齐。 注意:该异常类型在配置了“C”扩展指令子集的处理器中不可能发生。
1 指令访问错误(Instruction access fault) 同步 取指令访存错误。 mdcause提供详细的指令放错误类型:
2 非法指令(Illegal instruction) 同步 非法指令。
3 断点(Breakpoint) 同步 RISC-V架构定义了EBREAK指令,当处理器执行到该指令时,会发生异常进入异常服务程序。该指令往往用于调试器(Debugger)使用,譬如设置断点
4 读存储器地址非对齐(Load address misaligned) 同步 Load指令访存地址非对齐。 注意:N级别处理器内核支持可配置的地址非对齐的数据存储器读写操作,如果没有配置此选项或者未打开此开关,访问地址非对齐时会产生此异常。
5 读存储器访问错误(Load access fault) 非精确异步 Load指令访存错误。 mdcause提供详细的读存储器访问错误类型:
6 写存储器和AMO地址非对齐(Store/AMO address misaligned) 同步 Store或者AMO指令访存地址非对齐。注意:N级别处理器内核支持可配置的地址非对齐的数据存储器读写操作,如果没有配置此选项或者未打开此开关,访问地址非对齐时会产生此异常。AMO指令不支持非对其访问。
7 写存储器和AMO访问错误(Store/AMO access fault) 非精确异步 Store或者AMO指令访存错误。 mdcause提供详细的写存储器访问错误类型:
8 用户模式环境调用(Environment call from U-mode) 同步 User Mode下执行ecall指令。 RISC-V架构定义了ecall指令,当处理器执行到该指令时,会发生异常进入异常服务程序。该指令往往供软件使用,强行进入异常模式。
11 机器模式环境调用(Environment call from M-mode) 同步 Machine Mode下执行ecall指令。 RISC-V架构定义了ecall指令,当处理器执行到该指令时,会发生异常进入异常服务程序。该指令往往供软件使用,强行进入异常模式。
  • 1:PMP检测指令访问出错

  • 2:指令访问返回总线错误

  • 1: PMP检测读操作访问存储器出错

  • 2:读操作访问存储器返回总线错误

  • 3:NICE长指令错误

  • 1: PMP检测写操作访问存储器出错

  • 2: 写操作访问存储器返回总线错误

mdcause寄存器的详细格式如表 7‑14所示,其中低2位为异常子编号域,用于指示详细的异常类型,如表 3‑1所示。

3.4.3. 更新CSR寄存器mepc

N级别处理器内核退出异常时的返回地址由CSR寄存器mepc(Machine Exception Program Counter)保存。在进入异常时,硬件将自动更新mepc寄存器的值,该寄存器将作为退出异常的返回地址,在异常结束之后,能够使用它保存的PC值回到之前被异常停止执行的程序点。

注意:

  • 出现异常时,异常返回地址mepc的值被更新为当前发生异常的指令PC。

  • 虽然mepc寄存器会在异常发生时自动被硬件更新,但是mepc寄存器本身也是一个可读可写的寄存器,因此软件也可以直接写该寄存器以修改其值。

3.4.4. 更新CSR寄存器mtval

N级别处理器内核在进入异常时,硬件将自动更新CSR寄存器mtval(Machine Trap Value Register ),以反映引起当前异常的存储器访问地址或者指令编码:

  • 如果是由存储器访问造成的异常,譬如遭遇硬件断点、取指令、存储器读写造成的异常,则将存储器访问的地址更新到mtval寄存器中。

  • 如果是由非法指令造成的异常,则将该指令的指令编码更新到mtval寄存器中。

3.4.5. 更新CSR寄存器mstatus

mstatus寄存器的详细格式如表 7‑2所示,N级别处理器内核在进入异常时,硬件将自动更新CSR寄存器mstatus(Machine Status Register)的某些域:

  • mstatus.MPIE域的值被更新为异常发生前mstatus.MIE域的值,如图 3‑2所示。mstatus.MPIE域的作用是在异常结束之后,能够使用mstatus.MPIE的值恢复出异常发生之前的mstatus.MIE值。

  • mstatus.MIE域的值则被更新成为0(意味着进入异常服务程序后中断被全局关闭,所有的中断都将被屏蔽不响应)。

  • mstatus.MPP域的值被更新为异常发生前的Privilege Mode,如图 3‑2所示。mstatus.MPP域的作用是在异常结束之后,能够使用mstatus.MPP的值恢复出异常发生之前的Privilege Mode。

3.4.6. 更新Privilege Mode

异常需要在机器模式(Machine Mode)下处理,在进入异常时,处理器内核的Privilege Mode被更新为机器模式。

3.4.7. 更新Machine Sub-Mode

N级别处理器内核的Machine Sub-Mode实时反映在CSR寄存器msubm.TYP域中。在进入异常时,处理器内核的Machine Sub-Mode被更新为异常处理模式,因此:

  • CSR寄存器msubm.PTYP域的值被更新为异常发生前的Machine Sub-Mode(msubm.TYP域的值),如图 3‑2所示。msubm.PTYP域的作用是在异常结束之后,能够使用msubm.PTYP的值恢复出异常发生之前的Machine Sub-Mode值。

  • CSR寄存器msubm.TYP域的值则被更新为“异常处理模式”如图 3‑2所示,以实时反映当前的模式已经是“异常处理模式”。

图 3‑2进入/退出异常时CSR寄存器的变化

3.5. 退出异常处理模式

当程序完成异常处理之后,最终需要从异常服务程序中退出。

由于异常处理处于Machine Mode下,所以退出异常时,软件必须使用mret指令。处理器执行mret指令后的硬件行为如下。注意,下列硬件行为在一个时钟周期内同时完成:

  • 停止执行当前程序流,转而从CSR寄存器mepc定义的PC地址开始执行。

  • 更新CSR寄存器mstatus(Machine Status Register)如图 3‑2所示,并更新处理器内核的Privilege Mode以及Machine Sub-Mode。

退出异常的总体过程如所图 3‑3示。

图 3‑3 退出异常总体过程

下文将分别予以详述。

3.5.1. 从mepc定义的PC地址开始执行

在进入异常时,mepc寄存器被同时更新,以反映当时遇到异常的指令PC值。通过这个机制,意味着mret指令执行后处理器回到了当时遇到异常的指令的PC地址,从而可以继续执行之前被中止的程序流。

注意:退出异常之前可能需要使用软件更新mepc的值。例如,如果异常由ecall或ebreak产生,由于mepc的值被更新为ecall或ebreak指令自己的PC。因此在异常返回时,如果直接使用mepc保存的PC值作为返回地址,则会再次跳回ecall或者ebreak指令,从而造成死循环(执行ecall或者ebreak指令导致重新进入异常)。正确的做法是在异常处理程序中软件改变mepc指向下一条指令,由于现在ecall/ebreak都是4字节指令,因此改写设定mepc=mepc+4即可。

3.5.2. 更新CSR寄存器mstatus

mstatus寄存器的详细格式如表 7‑2所示。在执行mret指令后,硬件将自动更新CSR寄存器mstatus的某些域:

  • mstatus.MIE域的值被恢复为当前mstatus.MPIE的值。

  • 当前mstatus.MPIE域的值则被更新为1。

  • mstatus.MPP域的更新值分为以下两种情形:

    • 配置了用户模式U-mode时,mstatus.MPP被更新为0x0。

    • 没有配置用户模式U-mode时,mstatus.MPP被更新为0x11。

在进入异常时,mstatus.MPIE的值曾经被更新为异常发生前的mstatus.MIE值,如图 3‑2所示。而mret指令执行后,将mstatus.MIE域的值恢复为mstatus.MPIE的值。通过这个机制,则意味着mret指令执行后,处理器的mstatus.MIE值被恢复成异常发生之前的值(假设之前的mstatus.MIE值为1,则意味着中断被重新全局打开)。

3.5.3. 更新Privilege Mode

在进入异常时,mstatus.MPP的值曾经被更新为异常发生前的Privilege Mode,而在执行mret指令后,处理器的Privilege Mode被恢复为mstatus.MPP的值,如图 3‑2所示。通过这个机制,保证了处理器回到了异常发生前的处理器的Privilege Mode。

3.5.4. 更新Machine Sub-Mode

N级别处理器内核的Machine Sub-Mode实时反映在CSR寄存器msubm.TYP域中。在执行mret指令后,硬件将自动恢复处理器的Machine Sub-Mode为msubm.PTYP域的值:

  • 在进入异常时,msubm.PTYP域的值曾经被更新为异常发生前的Machine Sub-Mode值。而使用mret指令退出异常后,硬件将处理器Machine Sub-Mode的值恢复为msubm.PTYP域的值,如图 4‑2所示。通过这个机制,则意味着退出异常后,处理器的Machine Sub-Mode被恢复成异常发生之前的Machine Sub-Mode。

3.6. 异常服务程序

当处理器进入异常后,即开始从mtvec寄存器定义的PC地址执行新的程序,该程序通常为异常服务程序,并且程序还可以通过查询mcause中的异常编号(Exception Code)以及mdcause中的详细异常信息决定进一步跳转到更具体的异常服务程序。譬如当程序查询mcause中的值为0x2,则得知该异常是非法指令错误(Illegal Instruction)引起的,因此可以进一步跳转到非法指令错误异常服务子程序中去。

注意:由于进入异常和退出异常机制中没有硬件自动保存和恢复上下文的操作,因此需要软件明确地使用(汇编语言编写的)指令进行上下文的保存和恢复。请参见《Nuclei_N级别处理器内核SDK使用说明》结合一个完整的异常服务程序代码示例对其进行理解。

3.7. 异常嵌套

N级别处理器内核支持两级NMI/异常状态堆栈(Two Levels of NMI/Exception State Save Stacks),更多细节请参见4.6节。

4. 内核NMI机制介绍

4.1. NMI概述

NMI(Non-Maskable Interrupt)是处理器内核的一根特殊的输入信号,往往用于指示系统层面的紧急错误(譬如外部的硬件故障等)。在遇到NMI之后,处理器内核应该立即中止执行当前的程序,转而去处理该NMI错误。

4.2. NMI屏蔽

N级别处理器内核中NMI是不可以被屏蔽的,也就是说一旦发生了NMI,处理器一定会停止当前操作转而处理NMI。

4.3. 进入NMI处理模式

进入NMI处理模式时,N级别处理器内核的硬件行为可以简述如下。注意,下列硬件行为在一个时钟周期内同时完成:

  • 停止执行当前程序流,转而从CSR寄存器mnvec定义的PC地址开始执行。

  • 更新相关CSR寄存器,分别是以下几个寄存器:

  • mepc(Machine Exception Program Counter )

  • mstatus(Machine Status Register)

  • mcause(Machine Cause Register)

  • 更新处理器内核的Privilege Mode以及Machine Sub-Mode。

NMI响应总体过程如图 4‑1所示。

图 4‑1 NMI响应总体过程

下文将分别予以详述。

4.3.1. 从mnvec定义的PC地址开始执行

N级别处理器内核遇到NMI后跳入的PC地址由CSR寄存器mnvec指定。mnvec寄存器的值有以下两种情况:

  • 当mmisc_ctl[9]=1时,mnvec寄存器的值等于mtvec,即NMI与异常拥有相同的Trap入口地址。

  • 当mmisc_ctl[9]=0时, mnvec寄存器的值等于reset_vector,reset_vector为处理器被reset后的PC的值。

4.3.2. 更新CSR寄存器mepc

N级别处理器内核退出NMI时的返回地址由CSR寄存器mepc(Machine Exception Program Counter)保存。在进入NMI时,硬件将自动更新mepc寄存器的值,该寄存器将作为退出NMI的返回地址,在NMI结束之后,能够使用它保存的PC值回到之前被停止执行的程序点。

注意:

  • 出现NMI时,NMI返回地址mepc被指向下一条尚未执行的指令(因为NMI时的指令已经被正确执行)。那么在退出NMI后,程序便会回到之前的程序点,从下一条指令开始重新执行。

  • 虽然mepc寄存器会在NMI发生时自动被硬件更新,但是mepc寄存器本身也是一个可读可写的寄存器,因此软件也可以直接写该寄存器以修改其值。

4.3.3. 更新CSR寄存器mcause

mcause寄存器的详细格式如表 7‑6所示。N级别处理器内核在进入NMI时,硬件自动保存当前Trap的ID到mcause,以表明Trap的原因。中断、异常以及NMI都有各自特殊的Trap ID。NMI的Trap ID有以下两种值:

  • 当mmisc_ctl[9]=1时,NMI Trap ID为0xfff。

  • 当mmisc_ctl[9]=0时,NMI Trap ID为 0x1。

通过给每个Trap分配特定的Trap ID,可以识别Trap的原因,软件可以根据Trap的原因来设计特定的处理程序处理Trap。

4.3.4. 更新CSR寄存器mstatus

mstatus寄存器的详细格式如表 7‑2所示, N级别处理器内核在进入NMI时,硬件将自动更新CSR寄存器mstatus的某些域:

  • mstatus.MPIE域的值被更新为NMI发生前mstatus.MIE域的值,如图 4‑2所示。mstatus.MPIE域的作用是在NMI结束之后,能够使用mstatus.MPIE的值恢复出NMI发生之前的mstatus.MIE值。

  • mstatus.MIE域的值则被更新成为0(意味着进入NMI服务程序后中断被全局关闭,所有的中断都将被屏蔽不响应)。

  • mstatus.MPP域的值被更新为NMI发生前的Privilege Mode。保存mstatus.MPP域的作用时在NMI结束之后,能够使用mstatus.MPP的值恢复出NMI发生前的Privilege Mode。

4.3.5. 更新Privilege Mode

NMI处理是在机器模式(Machine Mode)下完成的,所以在进入NMI时,处理器内核的特权模式(Privilege Mode)切换成机器模式。

4.3.6. 更新Machine Sub-Mode

N级别处理器内核的Machine Sub-Mode实时反映在CSR寄存器msubm.TYP域中。在进入NMI时,处理器内核的Machine Sub-Mode被更新为NMI处理模式,因此:

  • CSR寄存器msubm.PTYP域的值被更新为NMI发生前的Machine Sub-Mode(msubm.TYP域的值),如图 4‑2所示。msubm.PTYP域的作用是在NMI结束之后,能够使用msubm.PTYP的值恢复出NMI发生之前的Machine Sub-Mode值。

  • CSR寄存器msubm.TYP域的值则被更新为“NMI处理模式”如图 4‑2所示,以实时反映当前的模式已经是“NMI处理模式”。

图 4‑2进入/退出NMI时CSR寄存器的变化

4.4. 退出NMI处理模式

当程序完成NMI处理之后,最终需要从NMI服务程序中退出,并返回主程序。

由于NMI处理处于Machine Mode下,所以在退出NMI时,软件必须使用mret指令。处理器执行mret指令后的硬件行为如下。注意,下列硬件行为在一个时钟周期内同时完成:

  • 停止执行当前程序流,转而从CSR寄存器mepc定义的PC地址开始执行。

  • 更新CSR寄存器mstatus。

  • 更新Privilege Mode以及Machine Sub-Mode。

退出NMI的总体过程如图 4‑3所示。

图 4‑3 退出NMI总体过程

下文将分别予以详述。

4.4.1. 从mepc定义的PC地址开始执行

在进入NMI时,mepc寄存器被同时更新,以反映当时遇到NMI的下一条指令的PC值。通过这个机制,意味着mret指令执行后处理器回到了当时遇到NMI的下一条指令的PC地址,从而可以继续执行之前被中止的程序流。

4.4.2. 更新CSR寄存器mstatus

mstatus寄存器的详细格式如表 7‑2所示,在执行mret指令后,硬件将自动更新CSR寄存器mstatus某些域:

  • mstatus.MIE域的值被恢复为当前mstatus.MPIE的值。

  • mstatus.MPIE域的值则被更新为1。

  • mstatus.MPP域的更新值分为以下两种情形:

    • 配置了用户模式U-mode时,mstatus.MPP被更新为0x0。

    • 没有配置用户模式U-mode时,mstatus.MPP被更新为0x11。

在进入NMI时,mstatus.MPIE的值曾经被更新为NMI发生前的mstatus.MIE值。而mret指令执行后,将mstatus.MIE的值恢复为mstatus.MPIE的值,如图 4‑2所示。通过这个机制,则意味着mret指令执行后,处理器的mstatus.MIE值被恢复成NMI发生之前的值(假设之前的mstatus.MIE值为1,则意味着中断被重新全局打开)。

4.4.3. 更新Privilege Mode

在进入NMI时,mstatus.MPP的值曾经被更新为NMI发生前的Privilege Mode,而在执行mret指令后,处理器的Privilege Mode被恢复为mstatus.MPP的值,如图 4‑2所示。通过这个机制,保证了处理器回到了NMI发生前的处理器的Privilege Mode。

4.4.4. 更新Machine Sub-Mode

N级别处理器内核的Machine Sub-Mode实时反映在CSR寄存器msubm.TYP域中。在执行mret指令后,硬件将自动恢复处理器的Machine Sub-Mode为msubm.PTYP域的值:

  • 在进入NMI时,msubm.PTYP域的值曾经被更新为NMI发生前的Machine Sub-Mode值。而使用mret指令退出NMI后,硬件将处理器Machine Sub-Mode的值恢复为msubm.PTYP域的值,如图 4‑2所示。通过这个机制,则意味着退出NMI后,处理器的Machine Sub-Mode被恢复成NMI发生之前的Machine Sub-Mode。

4.5. NMI服务程序

当处理器进入NMI后,即开始从mnvec寄存器定义的PC地址执行新的程序,该程序通常为NMI服务程序。

注意:由于进入NMI和退出NMI机制中没有硬件自动保存和恢复上下文的操作,因此需要软件明确地使用(汇编语言编写的)指令进行上下文的保存和恢复。请参见《Nuclei_N级别处理器内核SDK使用说明》结合一个完整的NMI服务程序代码示例对其进行理解。

4.6. NMI/异常嵌套

N级别处理器内核自定义了如图 4‑4所示的两级NMI/异常状态堆栈(Two Levels of NMI/Exception State Save Stacks),至多保存三级NMI/异常的处理器状态,可以实现二级可恢复的NMI/异常嵌套。

注意:由于处理器器处于NMI状态时,NMI的响应在硬件上被屏蔽掉了,因此NMI无法实现自我嵌套。N级别处理器内核中的NMI/异常嵌套只支持以下3种嵌套:

  • NMI嵌套异常

  • 异常嵌套异常

  • 异常嵌套NMI

图 4‑4 N级别处理器内核两级NMI/异常状态堆栈机制示意图

4.6.1. 进入NMI/异常嵌套

响应NMI与异常时,N级别处理器内核的硬件行为如图 4‑4所示,可以简述如下。

  • 停止执行当前程序流,转而从新的PC地址开始执行。

    • 如果响应的是异常则从mtvec所存储的PC地址开始执行。

    • 如果响应的是NMI则从mnvec所存储的PC地址开始执行。

  • 更新相关CSR寄存器,分别是以下几个寄存器及其相关的域:

    • mepc: 记录当前NMI/异常发生前的PC,退出NMI/异常时可从mepc恢复NMI/异常发生前的PC。

    • msaveepc1:第一级NMI/异常状态堆栈记录第一级嵌套NMI/异常(被当前NMI/异常嵌套的NMI/异常)发生前的PC,亦即当前NMI/异常发生前的mepc值,退出NMI/异常时可从msaveepc1恢复mepc的值。

    • msaveepc2: 第二级NMI/异常状态堆栈记录第二级嵌套NMI/异常(被第一级嵌套NMI/异常嵌套的NMI/异常)发生前的PC,亦即当前NMI/异常发生前的msaveepc1的值,退出NMI/异常时可从msaveepc2恢复msaveepc1的值。

    • mstatus:

      • MPIE:记录当前NMI/异常发生之前的MIE。

      • MPP: 记录当前NMI/异常发生之前的Privilege Mode。

    • msavestatus:

      • MPIE1:第一级NMI/异常状态堆栈记录第一级嵌套NMI/异常发生前的MIE,亦即当前NMI/异常发生前的MPIE,退出NMI/异常时可从MPIE1恢复MPIE的值。

      • MPIE2:第二级NMI/异常状态堆栈记录第二级嵌套NMI/异常发生前的MIE,亦即当前NMI/异常发生前的MPIE1,退出NMI/异常时可从MPIE2恢复MPIE1的值。

      • MPP1:第一级NMI/异常状态堆栈记录第一级嵌套NMI/异常发生前的Privilege Mode,亦即当前NMI/异常发生前的MPP,退出NMI/异常时可从MPP1恢复MPP的值。

      • MPP2:第二级NMI/异常状态堆栈记录第二级嵌套NMI/异常发生前的Privilege Mode,亦即当前NMI/异常发生前的MPP1,退出NMI/异常时可从MPP2恢复MPP1的值。

    • mcause: 记录发生当前NMI/异常的原因。

    • msavecause1:第一级NMI/异常状态堆栈记录第一级嵌套NMI/异常原因。

    • msavecause2:第二级NMI/异常状态堆栈记录第二级嵌套NMI/异常原因。

    • mdcause: 记录发生当前异常的详细原因。

    • msavedcause1:第一级NMI/异常状态堆栈记录第一级嵌套异常的详细原因。

    • msavedcause2:第二级NMI/异常状态堆栈记录第二级嵌套异常的详细原因。

    • msubm:

      • TYP:记录当前NMI/异常的Trap类型。

      • PTYP: 记录当前NMI/异常发生前处理器所处Trap的类型

      • PTYP1: 第一级NMI/异常状态堆栈记录第一级嵌套NMI/异常发生前的Machine Sub Mode,亦即当前NMI/异常发生前的PTYP,退出NMI/异常时可从PTYP1恢复PTYP的值。

      • PTYP2:第二级NMI/异常状态堆栈记录第二级嵌套NMI/异常发生前的Machine Sub Mode,亦即当前NMI/异常发生前的PTYP1,退出NMI/异常时可从PTYP2恢复PTYP1的值。

  • NMI/异常处理是在机器模式(Machine Mode)下完成的,所以在进入NMI/异常时,处理器内核的特权模式(Privilege Mode)切换成机器模式。

4.6.2. 退出NMI/异常嵌套

当程序完成NMI/异常处理之后,最终需要从NMI/异常服务程序退出,返回上级NMI/异常或者主程序,退出之前需要从相关寄存器恢复处理器状态,这是通过mret指令完成的,处理器执行mret指令后的硬件行为如图 4‑4,可以简述如下。

  • 停止执行当前程序流,转而从CSR寄存器mepc定义的PC地址开始执行。

  • 更新相关CSR寄存器,分别是以下几个寄存器及其相关的域:

    • mepc: 恢复为存储在msaveepc1中第一级嵌套NMI/异常发生前的PC。

    • msaveepc1:第一级NMI/异常状态堆栈,mret发生时从第二级NMI/异常状态堆栈msaveepc2恢复寄存器msaveepc1值,即恢复为存储在msaveepc2中的第二级嵌套NMI/异常发生前的PC。

    • mstatus

      • MPIE:恢复为存储在MPIE1中的第一级嵌套NMI/异常发生前的MIE 。

      • MPP: 恢复为存储在MPP1中的第一级嵌套NMI/异常发生前的Privilege Mode。

    • msavestatus:

      • MPIE1:第一级NMI/异常状态堆栈,mret发生时从第二级NMI/异常状态堆栈MPIE2恢复寄存器域msavestatus.MPIE1的值,即恢复为存储在MPIE2中的第二级嵌套NMI/异常发生前的MIE。

      • MPP1:第一级NMI/异常状态堆栈,mret发生时从第二级NMI/异常状态堆栈MPP2恢复寄存器域msavestatus.MPP1的值,即恢复为存储在MPP2中的第二级嵌套NMI/异常发生前的Privilege Mode。

    • mcause: 恢复为存储在msavecause1中的第一级嵌套NMI/异常的原因。

    • msavecause1:第一级NMI/异常状态堆栈,mret发生时从第二级NMI/异常状态堆栈msavecause2恢复寄存器msavecause1的值,即恢复为存储在msavecause2中的第二级嵌套NMI/异常的原因。

    • mdcause: 恢复为存储在msavedcause1中的第一级嵌套异常的详细原因。

    • msavedcause1:第一级NMI/异常状态堆栈,mret发生时从第二级NMI/异常状态堆栈msavedcause2恢复寄存器msavedcause1的值,即恢复为存储在msavedcause2中的第二级嵌套异常的详细原因。

    • msubm(Machine Sub-Mode Register)

      • TYP:恢复为存储在msubm.PTYP中的当前NMI/异常发生前处理器的Trap类型。

      • PTYP: 恢复为存储在msubm.PTYP1中第一级嵌套NMI/异常发生前处理器的Trap类型。

      • PTYP1: 第一级NMI/异常状态堆栈,mret发生时从第二级NMI/异常状态堆栈PTYP2恢复寄存器域msubm.PTYP1的值,即恢复为存储在msubm.PTYP2中的第二级嵌套NMI/异常发生前处理器的Trap类型。

  • 根据mstatus.MPP域的值更新处理器的Privilege Mode。

5. 内核中断机制介绍

5.1. 中断概述

中断(Interrupt)机制,即处理器内核在顺序执行程序指令流的过程中突然被别的请求打断而中止执行当前的程序,转而去处理别的事情,待其处理完了别的事情,然后重新回到之前程序中断的点继续执行之前的程序指令流。

中断的若干基本知识要点如下:

  • 打断处理器执行的“别的请求”便称之为中断请求(Interrupt Request),“别的请求”的来源便称之为中断源(Interrupt Source),中断源通常来自于内核外部(称之为外部中断源),也可以来自于内核内部(成为内部中断源)。

  • 处理器转而去处理的“别的事情”便称之为中断服务程序(Interrupt Service Routine,ISR)。

  • 中断处理是一种正常的机制,而非一种错误情形。处理器收到中断请求之后,需要保存当前程序的现场,简称为“保存现场”。等到处理完中断服务程序后,处理器需要恢复之前的现场,从而继续执行之前被打断的程序,简称为“恢复现场”。

  • 可能存在多个中断源同时向处理器发起请求的情形,需要对这些中断源进行仲裁,从而选择哪个中断源被优先处理。此种情况称为“中断仲裁”,同时可以给不同的中断分配级别和优先级以便于仲裁,因此中断存在着“中断级别”和“中断优先级”的概念。

5.2. 中断控制器ECLIC

如第7.4.17节中所述,通过软件的不同配置,N级别处理器内核支持“默认中断模式”和“ECLIC中断模式”,推荐使用“ECLIC中断模式”,本文仅对“ECLIC中断模式”进行介绍。

N级别处理器内核实现了一个“改进型内核中断控制器(Enhanced Core Local Interrupt Controller,ECLIC)”,可用于多个中断源的管理。N级别处理器内核中的所有类型(除了调试中断之外)的中断都由ECLIC统一进行管理,有关ECLIC的详情请参见第6.2节。有关N级别处理器内核支持的所有中断类型的介绍请参见第5.3节。

5.3. 中断类型

N级别处理器内核支持的中断类型如图 5‑1中所示。

图 5‑1 中断类型示意图

下文将分别予以详述。

5.3.1. 外部中断

外部中断是指来自于处理器核外部的中断。外部中断可供用户连接外部中断源,譬如外部设备UART、GPIO等产生的中断。

注意:N级别处理器内核支持多个外部中断源,所有外部中断都由ECLIC进行统一管理。

5.3.2. 内部中断

N级别处理器内核有几种内核私有的内部中断,分别为:

  • 软件中断(Software Interrupt)

  • 计时器中断(Timer Interrupt)

注意:N级别处理器内核的内部中断也都由ECLIC进行统一管理。

5.3.2.1 软件中断

软件中断要点如下:

  • N级别处理器内核实现了一个TIMER单元,TIMER单元里定义了一个msip寄存器,通过其可以产生软件中断,请参见第6.1.6节了解其详情。

  • 注意:软件中断也由ECLIC进行统一管理。

5.3.2.2 计时器中断

计时器中断要点如下:

  • N级别处理器内核实现了一个TIMER单元,TIMER单元里定义了一个计时器,通过其可以产生计时器中断,请参见第6.1.5节了解其详情。

  • 注意:计时器中断也由ECLIC进行统一管理。

5.4. 中断屏蔽

5.4.1. 中断全局屏蔽

N级别处理器内核的中断可以被屏蔽掉,CSR寄存器mstatus的MIE域控制中断的全局使能。请参见第7.4.9节了解详情。

5.4.2. 中断源单独屏蔽

对于不同的中断源而言,ECLIC为每个中断源分配了各自的中断使能寄存器,用户可以通过配置ECLIC寄存器来管理各个中断源的屏蔽,请参见第6.2.6节了解其详情。

5.5. 中断级别、优先级与仲裁

当多个中断同时出现时,需要进行仲裁。对于N级别处理器内核处理器而言,ECLIC统一管理所有的中断。ECLIC为每个中断源分配了各自的中断级别和优先级寄存器,用户可以通过配置ECLIC寄存器来管理各个中断源的级别和优先级,当多个中断同时发生时,ECLIC会仲裁出级别和优先级最高的中断,如图 5‑2中所示。请参见第6.2.9节了解其详情。

图 5‑2 中断仲裁示意图

5.6. 进入中断处理模式

响应中断时,N级别处理器内核的硬件行为可以简述如下。注意,下列硬件行为在一个时钟周期内同时完成:

  • 停止执行当前程序流,转而从新的PC地址开始执行。

  • 进入中断不仅会让处理器跳转到上述的PC地址开始执行,还会让硬件同时更新其他几个CSR寄存器,如图 5‑4所示,分别是以下几个寄存器:

    • mepc(Machine Exception Program Counter)

    • mstatus(Machine Status Register)

    • mcause(Machine Cause Register)

    • mintstatus (Machine Interrupt Status Register)

  • 除此之外,进入中断还会更新处理器内核的Privilege Mode以及Machine Sub-Mode。

  • 总体过程如图 5‑3中所示。

图 5‑3 响应中断总体过程

下文将分别予以详述。

5.6.1. 从新的PC地址开始执行

ECLIC的每个中断源均可以设置成向量或者非向量处理(通过寄存器clicintattr[i]的shv域),其要点如下:

  • 如果被配置成为向量处理模式,则该中断被处理器内核响应后,处理器直接跳入该中断的向量入口(Vector Table Entry)存储的目标地址。有关中断向量表的详细介绍,请参见第5.8节,有关向量处理模式的详细介绍,请参见第5.13.2节。

  • 如果被配置成为非向量处理模式,则该中断被处理器内核响应后,处理器直接跳入所有中断共享的入口地址。有关中断非向量处理模式的详细介绍,请参见第5.13.1节。

5.6.2. 更新Privilege Mode

在进入中断时,处理器内核的Privilege Mode被更新为Machine Mode。

5.6.3. 更新Machine Sub-Mode

N级别处理器内核的Machine Sub-Mode实时反映在CSR寄存器msubm.TYP域中。在进入中断时,处理器内核的Machine Sub-Mode被更新为中断处理模式,因此:

  • CSR寄存器msubm.PTYP域的值被更新为中断发生前的Machine Sub-Mode(msubm.TYP域的值),如图 5‑4所示。msubm.PTYP域的作用是在中断结束之后,能够使用msubm.PTYP的值恢复出中断发生之前的Machine Sub-Mode值。

  • CSR寄存器msubm.TYP域的值则被更新为“中断处理模式”,如图 5‑4所示,以实时反映当前的模式已经是“中断处理模式”。

5.6.4. 更新CSR寄存器mepc

N级别处理器内核退出中断时的返回地址由CSR寄存器mepc指定。在进入中断时,硬件将自动更新mepc寄存器的值,该寄存器将作为退出中断的返回地址,在中断结束之后,能够使用它保存的PC值回到之前被停止执行的程序点。

注意:

  • 出现中断时,中断返回地址mepc被指向一条指令,此指令因为中断的出现而未能完成执行。那么在退出中断后,程序便会回到之前的程序点,从mepc所存储的未执行完的指令开始重新执行。

  • 虽然mepc寄存器会在中断发生时自动被硬件更新,但是mepc寄存器本身也是一个可读可写的寄存器,因此软件也可以直接写该寄存器以修改其值。

5.6.5. 更新CSR寄存器mcause和mstatus

mcause寄存器的详细格式如表 7‑6所示。N级别处理器内核在进入中断时,CSR寄存器mcause被同时(硬件自动)更新,如图 5‑4所示,详情如下:

  • 当前的中断被响应后,需要有一种机制能够记录当前这个中断源的ID编号。

    • N级别处理器内核在进入中断时,CSR寄存器mcause.EXCCODE域被更新以反映当前响应的ECLIC中断源的ID编号,因此软件可以通过读此寄存器查询中断源的具体ID。
  • 当前的中断被响应,有可能是打断了之前正在处理的中断(中断级别相对低,因此可以被打断),需要有一种机制能够记录被打断中断的中断级别(Interrupt Levels)。

    • N级别处理器内核在进入中断时,CSR寄存器mcause.MPIL域被更新以反映被打断的中断级别(mintstatus.MIL域的值)。mcause.MPIL域的作用是在中断结束之后,能够使用mcause.MPIL的值恢复出中断发生之前的mintstatus.MIL值。
  • 当前的中断被响应后,需要有一种机制能够记录响应中断之前的中断全局使能状态和特权模式。

    • N级别处理器内核在进入中断时,CSR寄存器mstatus.MPIE域的值被更新为中断发生前中断的全局使能状态(mstatus.MIE域的值)。mstatus.MIE域的值则被更新成为0(意味着进入中断服务程序后中断被全局关闭,所有的中断都将被屏蔽不响应)。

    • N级别处理器内核在进入中断时,处理器的当前特权模式(Privilege Mode)切换到机器模式(Machine Mode),而CSR寄存器mstatus.MPP域的值被更新为中断发生前特权模式(Privilege Mode)。

  • 当前响应的中断如果是向量处理模式,则处理器响应中断后会直接跳入该中断的向量入口(Vector Table Entry)存储的目标地址。有关中断向量处理模式的详细介绍,请参见第5.13.2节。从硬件实现上来说,处理器需要分“两步走”,第一步从中断向量表中取出存储的目标地址,然后第二步再跳转到目标地址中去。那么,在第一步“从中断向量表中取出存储的目标地址”这个存储器访问操作的过程中有可能会发生存储器访问错误,需要有一种机制能够记录这种特殊的存储器访问错误。

    • N级别处理器内核在进入中断时,如果该中断是向量处理模式,CSR寄存器mcause.minhv域的值被更新为1,直到上述“两步走”操作彻底成功完成后mcause.minhv域的值清除为0。假设中途发生了存储器访问错误,则最终处理器会发生指令访问错误(Instruction access fault),且mcause.minhv域的值为1(没有被清除)。
  • 注意:mstatus.MPIE域和mstatus.MPP域的值与mcause.MPIE域和mcause.MPP域的值是镜像关系,即,在正常情况下,mstatus.MPIE域的值与mcause.MPIE域的值总是完全一样,mstatus.MPP域的值与mcause.MPP域的值总是完全一样。

图 5‑4 进入/退出中断时CSR寄存器的变化

5.7. 退出中断处理模式

当程序完成中断处理之后,最终需要从中断服务程序中退出,并返回主程序。由于中断处理处于Machine Mode下,所以退出中断时,软件必须使用mret指令。处理器执行mret指令后的硬件行为如下。注意,下列硬件行为在一个时钟周期内同时完成:

  • 停止执行当前程序流,转而从CSR寄存器mepc定义的PC地址开始执行。

  • 执行mret指令不仅会让处理器跳转到上述的PC地址开始执行,还会让硬件同时更新其他几个CSR寄存器,如图 5‑4所示,分别是以下几个寄存器:

    • mstatus(Machine Status Register)

    • mcause(Machine Cause Register)

    • mintstatus(Machine Interrupt Status Register)

  • 除此之外,进入中断还会更新处理器内核的Privilege Mode以及Machine Sub-Mode。

图 5‑5 退出中断总体过程

下文将分别予以详述。

5.7.1. 从mepc定义的PC地址开始执行

在进入中断时,mepc寄存器被同时更新,以反映当时遇到中断时的PC值。软件必须使用mret指令退出中断,执行mret指令后处理器将从mepc定义的pc地址重新开始执行。通过这个机制,意味着mret指令执行后处理器回到了当时遇到中断时的PC地址,从而可以继续执行之前被中止的程序流。

5.7.2. 更新CSR寄存器mcause和mstatus

mcause寄存器的详细格式如表 7‑6所示,执行mret指令后,硬件将自动更新CSR寄存器mcause的某些域:

  • 在进入中断时,mcause.MPIL的值曾经被更新为中断发生前的mintstatus.MIL值。而使用mret指令退出中断后,硬件将mintstatus.MIL的值恢复为mcause.MPIL的值。通过这个机制,则意味着退出中断后,处理器的mintstatus.MIL值被恢复成中断发生之前的值。

  • 在进入中断时,mcause.MPIE的值曾经被更新为中断发生前的mstatus.MIE值。而使用mret指令退出中断后,硬件将mret指令执行后,将mstatus.MIE的值恢复为mcause.MPIE的值,如图 5‑4所示。通过这个机制,则意味着退出中断后,处理器的mstatus.MIE值被恢复成中断发生之前的值。

  • 在进入中断时,mcause.MPP的值曾经被更新为中断发生前的特权模式(Privilege Mode)。而使用mret指令退出中断后,硬件将处理器特权模式(Privilege Mode)恢复为mcause.MPP的值,如图 5‑4所示。通过这个机制,则意味着退出中断后,处理器的特权模式(Privilege Mode)被恢复成中断发生之前的模式。

  • 注意:mstatus.MPIE域和mstatus.MPP域的值与mcause.MPIE域和mcause.MPP域的值是镜像关系,即,在正常情况下,mstatus.MPIE域的值与mcause.MPIE域的值总是完全一样,mstatus.MPP域的值与mcause.MPP域的值总是完全一样。

5.7.3. 更新Privilege Mode

在执行mret指令后,硬件将自动更新处理器的Privilege Mode为mcause.MPP域的值:

  • 在进入中断时,mcause.MPP的值曾经被更新为中断发生前的特权模式(Privilege Mode)。而使用mret指令退出中断后,硬件将处理器特权模式(Privilege Mode)恢复为mcause.MPP的值。通过这个机制,则意味着退出中断后,处理器的特权模式(Privilege Mode)被恢复成中断发生之前的模式。

5.7.4. 更新Machine Sub-Mode

N级别处理器内核的Machine Sub-Mode实时反映在CSR寄存器msubm.TYP域中。在执行mret指令后,硬件将自动恢复处理器的Machine Sub-Mode为msubm.PTYP域的值:

  • 在进入中断时,msubm.PTYP域的值曾经被更新为中断发生前的Machine Sub-Mode值。而使用mret指令退出中断后,硬件将处理器Machine Sub-Mode的值恢复为msubm.PTYP域的值,如图 5‑4所示。通过这个机制,则意味着退出中断后,处理器的Machine Sub-Mode被恢复成中断发生之前的Machine Sub-Mode。

5.8. 中断向量表

如图 5‑6中所示,中断向量表是指在存储器里面开辟的一段连续的地址空间,该地址空间的每个字(Word)用于存储ECLIC每个中断源对应的中断服务程序(Interrupt Service Routine,ISR)函数的PC地址。

中断向量表的起始地址由CSR寄存器mtvt指定,通常可以将mtvt寄存器设置为整个代码段的起始位置。

中断向量表的作用非常重要,当处理器响应某个中断源后,无论中断是向量处理模式还是非向量处理模式,硬件最终都将通过查询中断向量表中存储的PC地址跳转到其对应的中断服务程序函数中去,请参见第5.13节了解更多详细介绍。

图 5‑6 中断向量表示意图

5.9. 进出中断的上下文保存和恢复

RISC-V架构的处理器在进入和退出中断处理模式时没有硬件自动保存和恢复上下文(通用寄存器)的操作,因此需要软件明确地使用(汇编语言编写的)指令进行上下文的保存和恢复。根据中断是向量处理模式还是非向量处理模式,上下文的保存和恢复涉及到的内容会有所差异,请参见第5.13节了解更多详细介绍。

5.10. 中断响应延迟

中断响应延迟的概念通常是指,从“外部中断源拉高”到“处理器真正开始执行该中断源对应的中断服务程序(Interrupt Service Routine,ISR)中的第一条指令”所消耗的指令周期数。因此,中断响应延迟通常会包含如下几个方面的周期开销:

  • 处理器内核响应中断后进行跳转的开销

  • 处理器内核保存上下文所花费的周期开销

  • 处理器内核跳转到中断服务程序(Interrupt Service Routine,ISR)中去的开销。

取决于中断是向量处理模式还是非向量处理模式,中断响应延迟会有所差异,请参见第5.13节了解更多详细介绍。

5.11. 中断嵌套

处理器内核正在处理某个中断的过程中,可能有一个级别更高的新中断请求到来,处理器可以中止当前的中断服务程序,转而开始响应新的中断,并执行其“中断服务程序”,如此便形成了中断嵌套(即前一个中断还没响应完,又开始响应新的中断),并且嵌套的层次可以有很多层。

以图 5‑7中的示例为例:

  • 假设处理器正在处理计时器中断,突然有另外一个按键1中断到来(级别比计时器中断高),那么处理器会暂停处理计时器中断,开始处理按键1中断。

  • 但是突然又有另外一个按键2中断到来(级别比按键1中断更高),那么处理器会暂停处理按键1的中断,开始处理按键2中断。

  • 之后再没有其他更高级别的中断到来,则按键2中断不会再被打断,处理器能够顺利处理完毕按键2的中断,然后重新回到按键1中断的处理程序中去,完成按键1中断的处理。

  • 完成按键1中断的处理之后,处理器会重新回到计时器中断的处理程序中去,完成计时器中断的处理。

图 5‑7 中断嵌套示意图

注意:假设新来的中断请求的优先级比正在处理的中断级别低(或者相同),则处理器不应该响应这个新的中断请求,处理器必须完成当前的中断服务程序之后才考虑响应新的中断请求(因为新中断请求的级别并不比当前正在处理的中断级别高)。有关中断级别的设定请参见第6.2.9节了解更多信息。

在N级别处理器内核中,取决于中断是向量处理模式还是非向量处理模式,中断嵌套的支持方法会有所差异,请参见第5.13节了解更多详细介绍。

5.12. 中断咬尾

处理器内核正在处理某个中断的过程中,可能有新中断请求到来,但是“新中断的级别”低于或者等于“当前正在处理的中断级别”,因此,新中断不能够打断当前正在处理的中断(因此不会形成嵌套)。

当处理器完成当前中断之后,理论上需要恢复上下文,然后退出中断回到主应用程序,然后重新响应新的中断,响应新的中断又需要再次保存上下文。因此,存在着一次背靠背的“恢复上下文”和“保存上下文”操作,如果将此背靠背的“恢复上下文”和“保存上下文”省略掉,则称之为“中断咬尾”,如图 5‑8中所示,显而易见,中断咬尾可以加快多个中断的背靠背处理速度。

图 5‑8 中断咬尾示意图

在N级别处理器内核中,只有非向量处理模式才支持中断咬尾,请参见第5.13.1.3节了解更多详细介绍。

5.13. 中断的向量处理模式和非向量处理模式

如第6.2.10节中所述,ECLIC的每个中断源均可以设置成向量或者非向量处理(通过寄存器clicintattr[i]的shv域),向量处理模式和非向量处理模式二者有较大的差别,分别介绍如下。

5.13.1. 非向量处理模式

5.13.1.1 非向量处理模式的特点和延迟

如果被配置成为非向量处理模式,则该中断被处理器内核响应后,处理器会直接跳入到所有非向量中断共享的入口地址,该入口地址可以通过软件进行设置:

  • 如果配置CSR寄存器mtvt2的最低位为0(上电复位默认值),则所有非向量中断共享的入口地址由CSR寄存器mtvec的值(忽略最低2位的值)指定。由于mtvec寄存器的值也指定异常的入口地址,因此,意味着在这种情况下,异常和所有非向量中断共享入口地址。

  • 如果配置CSR寄存器mtvt2的最低位为1,则所有非向量中断共享的入口地址由CSR寄存器mtvt2的值(忽略最低2位的值)指定。为了让中断以尽可能快的速度被响应和处理,推荐将CSR寄存器mtvt2的最低位设置为1,即,由mtvt2指定一个独立的入口地址供所有非向量中断专用,和异常的入口地址(由mtvec的值指定)彻底分开。

在进入所有非向量中断共享的入口地址之后,处理器会开始执行一段共有的软件代码,如图 5‑9中所示的例子,这段软件代码内容通常如下:

  • 首先保存CSR寄存器mepc、mcause、msubm入堆栈。保存这几个CSR寄存器是为了保证后续的中断嵌套能够功能正确,因为新的中断响应会重新覆盖mepc、mcause、msubm的值,因此需要将它们先保存入堆栈。

  • 保存若干通用寄存器(处理器的上下文)入堆栈。

  • 然后执行一条特殊的指令“csrrw ra, CSR_JALMNXTI, ra”。如果没有中断在等待(Pending),则该指令相当于是个Nop指令不做任何操作;如果有中断在等待(Pending),执行该指令后处理器会:

    • 直接跳入该中断的向量入口(Vector Table Entry)存储的目标地址,即该中断源的中断服务程序(Interrupt Service Routine,ISR)中去。

    • 在跳入中断服务程序的同时,硬件也会同时打开中断的全局使能,即,设置mstatus寄存器的MIE域为1。打开中断全局使能后,新的中断便可以被响应,从而达到中断嵌套的效果。

    • 在跳入中断服务程序的同时,“csrrw ra, CSR_JALMNXTI, ra”指令还会达到JAL(Jump and Link)的效果,硬件同时更新Link寄存器的值为该指令的PC自身作为函数调用的返回地址。因此,从中断服务程序函数返回后会回到该“csrrw ra, CSR_JALMNXTI, ra”指令重新执行,重新判断是否还有中断在等待(Pending),从而达到中断咬尾的效果。

    • 在中断服务程序的结尾处同样需要添加对应的恢复上下文出栈操作。并且在CSR寄存器mepc、mcause、msubm出堆栈之前,需要将中断全局使能再次关闭,以保证mepc、mcause、msubm恢复操作的原子性(不被新的中断所打断)。

图 5‑9 中断的非向量处理模式示例(总是支持嵌套)

由于非向量处理模式时处理器在跳到中断服务程序之前需要先执行一段共有的软件代码进行上下文的保存,因此,从中断源拉高到处理器开始执行中断服务程序中的第一条指令,需要经历以下几个方面的时钟周期开销:

  • 处理器内核响应中断后进行跳转的开销。理想情况下约4个时钟周期。

  • 处理器内核保存CSR寄存器mepc、mcause、msubm入堆栈的开销。

  • 处理器内核保存上下文所花费的周期开销。如果是RV32E的架构,则需要保存8个通用寄存器,如果是RV32I的架构,则需要保存16个通用寄存器。

  • 处理器内核跳转到中断服务程序(Interrupt Service Routine,ISR)中去的开销。理想情况下约需要5个时钟周期。

5.13.1.2 非向量处理模式的中断嵌套

如上文所述,非向量处理模式总是能够支持中断嵌套,如图 5‑10中所示的示例:假设中断源30、31、32这三个中断源先后到来,且“中断源32的级别” > “中断源31的级别”> “中断源30的级别”,那么后来的中断便会打断之前正在处理的中断形成中断嵌套。

图 5‑10 三个先后到来的(非向量处理模式)中断形成嵌套

5.13.1.3 非向量处理模式的中断咬尾

对于非向量处理模式的中断而言,由于在跳入和退出中断服务程序之前,处理器要进行上下文的保存和恢复,因此进行“中断咬尾”能够节省显著的时间(节省一次背靠背的保存上下文和恢复上下文)。

如上文所述,在所有非向量中断共享的共有代码段中,在跳入中断服务程序的同时,“csrrw ra, CSR_JALMNXTI, ra”指令还会达到JAL(Jump and Link)的效果,硬件同时更新Link寄存器的值为该指令的PC自身作为函数调用的返回地址。因此,从中断服务程序函数返回后会回到该“csrrw ra, CSR_JALMNXTI, ra”指令重新执行,重新判断是否还有中断在等待(Pending),从而达到中断咬尾的效果。

如图 5‑11中所示的示例:假设中断源30、29、28这三个中断源先后到来,且“中断源30的级别” >= “中断源29的级别”>= “中断源28的级别”,那么后来的中断不会打断之前正在处理的中断(不会形成中断嵌套),但是会被置于等待(Pending)状态。当中断源30完成处理后,将会直接开始中断源29的中断处理,省掉中间的“恢复上下文”和“保存上下文”过程。

图 5‑11 中断咬尾示意图

5.13.2. 向量处理模式

5.13.2.1 向量处理模式的特点和延迟

如果被配置成为向量处理模式,则该中断被处理器内核响应后,处理器会直接跳入该中断的向量入口(Vector Table Entry)存储的目标地址,即该中断源的中断服务程序(Interrupt Service Routine,ISR),如图 5‑12中所示的例子。

图 5‑12 中断的向量处理模式示例

向量处理模式具有如下特点:

  • 向量处理模式时处理器会直接跳到中断服务程序,并没有进行上下文的保存,因此,中断响应延迟非常之短,从中断源拉高到处理器开始执行中断服务程序中的第一条指令,基本上只需要硬件进行查表和跳转的时间开销,理想情况下约6个时钟周期。

  • 对于向量处理模式的中断服务程序函数,一定要使用特殊的__attribute__((interrupt))来修饰中断服务程序函数。

  • 向量处理模式时,由于在跳入中断服务程序之前,处理器并没有进行上下文的保存,因此,理论上中断服务程序函数本身不能够进行子函数的调用(即,必须是Leaf Function)。

    • 如果中断服务程序函数不小心调用了其他的子函数(不是Leaf Function),如果不加处理则会造成功能的错误。为了规避这种不小心造成的错误情形,只要使用了特殊的__attribute__ ((interrupt)) 来修饰该中断服务程序函数,那么编译器会自动的进行判断,当编译器发现该函数调用了其他子函数时,便会自动的插入一段代码进行上下文的保存。注意:这种情况下虽然保证了功能的正确性,但是由于保存上下文造成的开销,又会事实上还是增大中断的响应延迟(与非向量模式相当)并且造成代码尺寸(Code Size)的膨胀。因此,在实践中,如果使用向量处理模式,那么不推荐在向量处理模式的中断服务程序函数中调用其他的子函数。
  • 向量处理模式时,由于在跳入中断服务程序之前,处理器并没有进行任何特殊的处理,且由于处理器内核在响应中断后,mstatus寄存器中的MIE域将会被硬件自动更新成为0(意味着中断被全局关闭,从而无法响应新的中断)。因此向量处理模式默认是不支持中断嵌套的,为了达到向量处理模式且又能够中断嵌套的效果,如图 5‑13中所示,需要在中断服务程序的开头处添加特殊的入栈操作:

    • 首先保存CSR寄存器mepc、mcause、msubm入堆栈。保存这几个CSR寄存器是为了保证后续的中断嵌套能够功能正确,因为新的中断响应会重新覆盖mepc、mcause、msubm的值,因此需要将它们先保存入堆栈。

    • 重新打开中断的全局使能,即,设置mstatus寄存器的MIE域为1。打开中断全局使能后,新的中断便可以被响应,从而达到中断嵌套的效果。

    • 在中断服务程序的结尾处同样需要添加对应的恢复上下文出栈操作。并且在CSR寄存器mepc、mcause、msubm出堆栈之前,需要将中断全局使能再次关闭,以保证mepc、mcause、msubm恢复操作的原子性(不被新的中断所打断)。

图 5‑13 中断的向量处理模式示例(支持中断嵌套)

5.13.2.2 向量处理模式的中断嵌套

如上文所述,向量处理模式的中断经过特殊处理也可以支持中断嵌套,如图 5‑14中所示的示例:假设中断源30、31、32这三个中断源先后到来,且“中断源32的级别” > “中断源31的级别”> “中断源30的级别”,那么后来的中断便会打断之前正在处理的中断形成中断嵌套。

图 5‑14 三个先后到来的(向量处理模式)中断形成嵌套

5.13.2.3 向量处理模式的中断咬尾

对于向量处理模式的中断而言,由于在跳入中断服务程序之前,处理器并没有进行上下文的保存,因此进行“中断咬尾”的意义不大,因此,向量处理模式的中断,没有“中断咬尾”处理能力。

6. 内核TIMER和ECLIC介绍

6.1. TIMER介绍

6.1.1. TIMER简介

计时器单元(Timer Unit,TIMER),在N级别处理器内核中主要用于产生计时器中断(Timer Interrupt)和软件中断(Software Interrupt)。请参见第5.3.2.1节和第5.3.2.2节了解计时器中断与软件中断的详细信息。

6.1.2. TIMER寄存器

TIMER是一个存储器地址映射的单元:

  • TIMER单元在N级别处理器内核中的基地址请参见《Nuclei_N级别处理器内核简明数据手册》中的介绍。

  • TIMER单元内寄存器和地址偏移量如表 6‑1中所示。

表 6‑1 TIMER寄存器的存储器映射地址

模块内偏移地址 读写属性 寄存器名称 复位默认值 功能描述
0x0 可读可写 mtime_lo 0x00000000 反映计时器mtime的低32位值,参见第6.1.3节了解其详细介绍。
0x4 可读可写 mtime_hi 0x00000000 反映计时器mtime的高32位值,参见第6.1.3节了解其详细介绍。
0x8 可读可写 mtimecmp_lo 0xFFFFFFFF 配置计时器的比较值mtimecmp低32位,参见第6.1.5节了解其详细介绍。
0xC 可读可写 mtimecmp_hi 0xFFFFFFFF 配置计时器的比较值mtimecmp高32位,参见第6.1.5节了解其详细介绍。
0xFF0 可读可写 msftrst 0x00000000 生成软件复位请求,参见第6.1.7节了解其详细介绍。
0xFF8 可读可写 mtimectl 0x00000000 控制计时器计数,参见第6.1.4节了解其详细介绍。
0xFFC 可读可写 msip 0x00000000 生成软件中断,参见第6.1.6节了解其详细介绍。
  • 注意:

    • TIMER的寄存器只支持操作尺寸(Size)为word的对齐读写访问。

    • TIMER的寄存器区间为0x0 \~ 0xFFF,除了上表中列出的寄存器之外的其他地址内的值为常数0。

下文对各寄存器的功能和使用进行详细描述。

6.1.3. 通过mtime寄存器进行计时

TIMER可以用于实时计时,要点如下:

  • TIMER中实现了一个64位的mtime寄存器,由{mtime_hi,mtime_lo}拼接而成,该寄存器反映了64位计时器的值。计时器根据低速的输入节拍信号进行自增计数,计时器默认是打开的,因此会一直进行计数。

  • 在N级别处理器内核中,此计数器的自增频率由处理器的输入信号mtime_toggle_a或着内核常开时钟core_aon_clk控制,请参见文档《Nuclei_N级别处理器内核简明数据手册》了解mtime_toggle_a的详情。

6.1.4. 通过mtimectl寄存器控制计时器计数

由于TIMER的计时器上电后默认会一直进行自增计数, TIMER中实现了一个mtimectl寄存器来控制计数器的行为。mtimectl寄存器中实现了三个域分别用于在某些特殊情况下关闭计时器计数,选择计时器的计数触发时钟,控制计时器中断触发后计数器是否清零。具体功能如表 6‑2中所示。

表 6‑2 寄存器mtimectl的比特域

域名 比特位 属性 复位值 描述
Reserved 31:3 只读,写忽略 N/A 未使用的域,值为常数0
CLKSRC 2 可读可写 0 控制计数器的时钟。如果该域为1,使用常开时钟core_aon_clk触发TIMER计数,否者使用mtime_toggle_a信号触发TIMER的计数。
CMPCLREN 1 可读可写 0 控制计时器自动清零的开启或者关闭。如果该域的值为1,则在触发了计时器中断后,TIMER会自动清零mtimer寄存器,否则继续正常自增计数。
TIMESTOP 0 可读可写 0 控制计时器运行或者暂停。如果该域的值为1,则计时器暂停计数,否则正常自增计数。

6.1.5. 通过mtime和mtimecmp寄存器生成计时器中断

TIMER可以用于生成计时器中断,要点如下:

  • TIMER中实现了一个64位的mtimecmp寄存器,由{mtimecmp_hi,mtimecmp_lo}拼接而成,该寄存器作为计时器的比较值,假设计时器的值mtime大于或者等于mtimecmp的值,则产生计时器中断。软件可以通过改写mtimecmp或者mtime的值(使得mtimecmp大于mtime的值)来清除计时器中断。

注意:计时器中断是连接到ECLIC单元进行统一管理,有关ECLIC的详情请参见第6.2节。

6.1.6. 通过msip寄存器生成软件中断

TIMER可以用于生成软件中断。TIMER中实现了一个msip寄存器,如表 6‑3中所示,msip寄存器只有最低位为有效位,该有效位直接作为软件中断,因此:

  • 软件写通过写1至msip寄存器产生软件中断;

  • 软件可通过写0至msip寄存器来清除该软件中断。

注意:软件中断是连接到ECLIC单元进行统一管理,有关ECLIC的详情请参见第6.2节。

表 6‑3 寄存器msip的比特域

域名 比特位 属性 复位值 描述
Reserved 7:1 只读,写忽略 N/A 未使用的域,值为常数0
MSIP 0 可读可写 0 该域用于产生软件中断

6.1.7. 通过msftrst寄存器生成软件复位请求

TIMER可以用于生成软件复位请求。TIMER中实现了一个msftrst寄存器,如所示,msftrst寄存器只有最高位为有效位,该有效位直接作为软件复位请求。详细的功能描述如下:

  • 软件通过写0x80000a5f至msftrst,才能使msftrst最高位更新为1,产生软件复位请求。此写法是为了防止软件误操作导致的不预期的软件复位请求。

  • msftrst的最高位只能通过复位操作生效来清零,所以当软件复位请求生效后,msftrst的最高位会被清除。

注意:软件复位请求是发送给Core外SOC,再由Core外的SOC生成复位信号发回给Core。

表 6‑4寄存器msftrst的比特域

域名 比特位 属性 复位值 描述
MSFTRST 31 可读可写 0 该域用于产生软件复位请求
Reserved 30:0 只读,写忽略 N/A 未使用的域,值为常数0

6.2. ECLIC介绍

N级别处理器内核支持在RISC-V标准CLIC基础上优化而来的“改进型内核中断控制器(Enhanced Core Local Interrupt Controller,ECLIC)”,用于管理所有的中断源。

注意:

  • ECLIC只服务于一个处理器内核,为该处理器内核私有。

  • ECLIC的软件编程模型也向后兼容标准的CLIC。

6.2.1. ECLIC简介

图 6‑1 ECLIC逻辑结构示意图

ECLIC用于对多个内部和外部中断源进行仲裁、发送请求并支持中断嵌套。ECLIC的寄存器如表 6‑5所述,逻辑结构如图 6‑1所示,相关概念如下:

  • ECLIC中断目标

  • ECLIC中断源

  • ECLIC中断源的编号

  • ECLIC的寄存器

  • ECLIC中断源的使能位

  • ECLIC中断源的等待标志位

  • ECLIC中断源的电平或边沿属性

  • ECLIC中断源的级别和优先级

  • ECLIC中断源的向量或非向量处理

  • ECLIC中断目标的阈值级别

  • ECLIC中断的仲裁机制

  • ECLIC中断的响应、嵌套、咬尾机制

下文将分别予以详述。

6.2.2. ECLIC中断目标

ECLIC单元生成一根中断线,发送给处理器内核(作为中断目标),其关系结构如图 6‑2所示。

图 6‑2 ECLIC关系结构图

6.2.3. ECLIC中断源

如图 6‑2所示,ECLIC理论上从编程模型上可以支持多达4096个中断源(Interrupt Source)。ECLIC为每个中断源定义了如下特性和参数:

  • 编号(ID)

  • 使能位(IE)

  • 等待标志位(IP)

  • 电平或边沿属性(Level or Edge-Triggered)

  • 级别和优先级(Level and Priority)

  • 向量或非向量处理(Vector or Non-Vector Mode)

下文分别予以介绍。

6.2.4. ECLIC中断源的编号(ID)

ECLIC为每个中断源分配了一个独一无二的编号(ID)。譬如,假设某ECLIC的硬件实现真正支持4096个ID,则ID应为0至4095。注意:

  • 在N级别处理器内核中,中断ID编号0至18的中断被预留作为了内核特殊的内部中断。

  • 普通外部中断分配的中断源ID从19开始,用户可以用于连接外部中断源。

详细介绍如表 6‑4中所示。

表 6‑5 ECLIC中断源编号和分配

ECLIC中断编号 功能 中断源介绍
0 预留 N级别处理器内核没有使用该中断。
1 预留 N级别处理器内核没有使用该中断。
2 预留 N级别处理器内核没有使用该中断。
3 软件中断 N级别处理器内核的TIMER单元生成的软件中断。
4 预留 N级别处理器内核没有使用该中断。
5 预留 N级别处理器内核没有使用该中断。
6 预留 N级别处理器内核没有使用该中断。
7 计时器中断 N级别处理器内核的TIMER单元生成的计时器中断。
8 预留 N级别处理器内核没有使用该中断。
9 预留 N级别处理器内核没有使用该中断。
10 预留 N级别处理器内核没有使用该中断。
11 预留 N级别处理器内核没有使用该中断。
12 预留 N级别处理器内核没有使用该中断。
13 预留 N级别处理器内核没有使用该中断。
14 预留 N级别处理器内核没有使用该中断。
15 预留 N级别处理器内核没有使用该中断。
16 预留 N级别处理器内核没有使用该中断。
17 预留 N级别处理器内核没有使用该中断。
18 预留 N级别处理器内核没有使用该中断。
19 \~ 4096 外部中断 普通外部中断供用户连接使用。注意:
  • 虽然ECLIC从编程模型上支持最多4096个中断源,但是实际硬件支持的中断源数目反映在信息寄存器clicinfo.NUM_INTERRUPT中。

6.2.5. ECLIC的寄存器

ECLIC是一个存储器地址映射的单元:

  • ECLIC单元在N级别处理器内核中的基地址请参见《Nuclei_N级别处理器内核简明数据手册》中的介绍。

  • ECLIC单元内的寄存器和地址偏移量如表 6‑5中所示。

表 6‑6 ECLIC寄存器的单元内地址偏移量

属性 名称 宽度
0x0000 可读可写 cliccfg 8位
0x0004 只读,写忽略 clicinfo 32位
0x000b 可读可写 mth 8位
0x1000+4*i 可读可写 clicintip[i] 8位
0x1001+4*i 可读可写 clicintie[i] 8位
0x1002+4*i 可读可写 clicintattr[i] 8位
0x1003+4*i 可读可写 clicintctl[i] 8位
注意:
  • 上述的i表示中断的ID编号,带有[i]后缀的寄存器表示这是针对每个中断源会有一份独立的寄存器。

  • ECLIC的寄存器支持操作尺寸(Size)为byte、half-word、或word的对齐读写访问。

  • 向上述“只读”寄存器进行写操作会被忽略,但是不会产生总线出错异常。

  • 实际的ECLIC可能没有配置4096个中断源,那么不存在的中断源对应寄存器的值为常数0。

  • ECLIC单元内寄存器区间为0x0000 \~ 0xFFFF,除了上表中列出的寄存器之外的其他地址内的值为常数0。

下文对各个寄存器进行详细介绍。

6.2.5.1 寄存器cliccfg

cliccfg寄存器是全局性的配置寄存器,软件可以通过改写该寄存器配置若干全局性的参数,其具体比特域的信息请参见表 6‑6中所示。

表 6‑7 寄存器cliccfg的比特域

域名 比特位 属性 复位值 描述
Reserved 7:5 只读,写忽略 N/A 未使用的域,值为常数0
nlbits 4:1 可读可写 0 用于指定clicintctl[i]寄存器中Level域的比特数,参见第6.2.9节了解其详细介绍。
Reserved 0 只读,写忽略 N/A 未使用的域,值为常数1

6.2.5.2 寄存器clicinfo

clicinfo寄存器是全局性的信息寄存器,软件可以通过读该寄存器查看若干全局性的参数,其具体比特域的信息请参见表 6‑7中所示。

表 6‑8 寄存器clicinfo的比特域

域名 比特位 属性 复位值 描述
Reserved 31:25 只读,写忽略 N/A 未使用的域,值为常数0
CLICINTCTLBITS 24:21 只读,写忽略 N/A 用于指定clicintctl[i]寄存器中有效位的比特数,参见第6.2.9节了解其详细介绍。
VERSION 20:13 只读,写忽略 N/A 硬件实现的版本号
NUM_INTERRUPT 12:0 只读,写忽略 N/A 硬件支持的中断源数目

6.2.5.3 寄存器mth

mth寄存器是中断目标的阈值级别寄存器,软件可以通过改写该寄存器配置中断目标的阈值级别,其具体比特域的信息请参见表 6‑8中所示。

表 6‑9 寄存器mth的比特域

域名 比特位 属性 复位值 描述
mth 7:0 可读可写 N/A 中断目标的阈值级别寄存器,参见第6.2.11节了解其详细介绍。

6.2.5.4 寄存器clicintip[i]

clicintip[i]寄存器是中断源的等待标志寄存器,其具体比特域的信息请参见表 6‑9中所示。

表 6‑10 寄存器clicintip[i]的比特域

域名 比特位 属性 复位值 描述
Reserved 7:1 只读,写忽略 N/A 未使用的域,值为常数0
IP 0 可读可写 0 中断源的等待标志位,参见第6.2.7节了解其详细介绍。

6.2.5.5 寄存器clicintie[i]

clicintie[i]寄存器是中断源的使能寄存器,其具体比特域的信息请参见表 6‑10中所示。

表 6‑11 寄存器clicintip[i]的比特域

域名 比特位 属性 复位值 描述
Reserved 7:1 只读,写忽略 N/A 未使用的域,值为常数0
IE 0 可读可写 0 中断源的使能位,参见第6.2.6节了解其详细介绍。

6.2.5.6 寄存器clicintattr[i]

clicintattr[i]寄存器是中断源的属性寄存器,软件可以通过改写该寄存器配置中断源的若干属性,其具体比特域的信息请参见表 6‑11中所示。

表 6‑12 寄存器clicintattr[i]的比特域

域名 比特位 属性 复位值 描述
Reserved 7:6 只读,写忽略 N/A 未使用的域,值为常数3
Reserved 5:3 只读,写忽略 N/A 未使用的域,值为常数0
trig 2:1 可读可写 0 指定该中断源的电平或边沿属性,参见第6.2.8节了解其详细介绍。
shv 0 可读可写 0 指定该中断源使用向量处理模式还是非向量处理模式,参见第6.2.10节了解其详细介绍。

6.2.5.7 寄存器clicintctl[i]

clicintctl[i]寄存器是中断源的控制寄存器,软件可以通过改写该寄存器配置中断源的级别(Level)和优先级(Priority),Level和Priority域根据cliccfg.nlbits的值动态进行分配,参见第6.2.9节了解其详细介绍。

6.2.6. ECLIC中断源的使能位(IE)

如图 6‑2所示,ECLIC为每个中断源分配了一个中断使能位(IE),反映在寄存器clicintie[i].IE中,其功能如下:

  • 每个中断源的clicintie[i]寄存器是存储器地址映射的可读可写寄存器,从而使得软件可以对其编程。

  • 如果clicintie[i]寄存器被编程配置成为0,则意味着此中断源被屏蔽。

  • 如果clicintie[i]寄存器被编程配置成为1,则意味着此中断源被打开。

6.2.7. ECLIC中断源的等待标志位(IP)

如图 6‑2所示,ECLIC为每个中断源分配了一个中断等待标志位(IP),反映在寄存器clicintip[i].IP中,其功能如下:

  • 如果某个中断源的IP位为高,则表示该中断源被触发。中断源的触发条件取决于它是电平触发还是边沿触发的属性,请参见第6.2.8节的详细介绍。

  • 中断源的IP位软件可读可写,软件写IP位的行为取决于它是电平触发还是边沿触发的属性,请参见第6.2.8节的详细介绍。

  • 对于边沿触发的中断源,其IP还可能存在硬件自清的行为,请参见第6.2.8节的详细介绍。

6.2.8. ECLIC中断源的电平或边沿属性(Level or Edge-Triggered)

如图 6‑2所示,ECLIC的每个中断源均可以设置电平触发或者边沿触发的属性(通过寄存器clicintattr[i]的trig域),其要点如下:

  • 当clicintattr[i].trig[0] == 0时,设置该中断属性为电平触发的中断:

    • 如果该中断源被配置为电平触发,中断源的IP位会实时反映该中断源的电平值。

    • 如果该中断源被配置为电平触发,由于中断源的IP位实时反映该中断源的电平值,所以软件对该中断IP位的写操作会被忽略,即,软件无法通过写操作设置或者清除IP位的值。如果软件需要清除中断,只能通过清除中断的最终源头的方式进行。

  • 当clicintattr[i].trig[0] == 1和clicintattr[i].trig[1] == 0时,设置该中断属性为上升沿触发的中断:

    • 如果该中断源被配置为上升沿触发,则ECLIC检测到该中断源的上升沿时,该中断源在ECLIC中被触发,该中断源的IP位被置高。

    • 如果该中断源被配置为上升沿触发,软件对该中断IP位的写操作会生效,即,软件可以通过写操作设置或者清除IP位的值。

    • 注意:对于上升沿触发的中断而言,为了能够提高中断处理的效率,当该中断被响应,处理器内核跳入中断服务程序(Interrupt Service Routines,ISR)之时,ECLIC的硬件会自动清除该中断的IP位,从而无需软件在ISR内部对该中断的IP位进行清除。

  • 当clicintattr[i].trig[0] == 1和clicintattr[i].trig[1] == 1时,设置该中断属性为下降沿触发的中断:

    • 如果该中断源被配置为下降沿触发,则ECLIC检测到该中断源的下降沿时,该中断源在ECLIC中被触发,该中断源的IP位被置高。

    • 如果该中断源被配置为下降沿触发,软件对该中断IP位的写操作会生效,即,软件可以通过写操作设置或者清除IP位的值。

    • 注意:对于下降沿触发的中断而言,为了能够提高中断处理的效率,当该中断被响应,处理器内核跳入中断服务程序(Interrupt Service Routines,ISR)之时,ECLIC的硬件会自动清除该中断的IP位,从而无需软件在ISR内部对该中断的IP位进行清除。

6.2.9. ECLIC中断源的级别和优先级(Level and Priority)

如图 6‑2所示,ECLIC的每个中断源均可以设置特定的级别和优先级(通过寄存器clicintctl[i]),其要点如下:

  • 每个中断源的clicintctl[i]寄存器理论上有8位宽,其中硬件真正实现的位有效位数由clicinfo寄存器的CLICINTCTLBITS域来指定。譬如,假设clicinfo.CLICINTCTLBITS域的值为6,则表示clicintctl[i]寄存器只有高6位是真正的有效位,最低2位的值为常数1,如图 6‑3中示例所示。

    • 注意:CLICINTCTLBITS域的值是只读的固定常数,软件无法对其进行编程改写。其理论上的合理范围是2 \<= CLICINTCTLBITS \<= 8,具体的实际值由处理器内核的硬件实现决定。
  • 在clicintctl[i]寄存器的有效位中,包含两个动态的域,分别用于指定该中断源的级别(Level)和优先级(Priority)。Level域的宽度由cliccfg寄存器的nlbits域来指定。譬如,假设cliccfg.nlbits域的值为4,则表示clicintctl[i]寄存器有效位的高4位是Level域,其他的低位有效位为Priority域,如图 6‑3中示例所示。

    • 注意:cliccfg.nlbits域的值是可读可写域,软件可以对其进行编程改写。

图 6‑3寄存器clicintctl[i]的格式示例

  • 中断源的级别(Level)相关的要点如下:

    • Level的数字值采取左对齐的方式进行解读,有效位宽(由cliccfg.nlbits指定)之外的低位全部采用补常数1的方式填充,如图 6‑4中示例所示。

      • 注意:如果cliccfg.nlbits > clicinfo.CLICINTCTLBITS,则意味着nlbits指示的位数超出了clicintctl[i]寄存器的有效位,则超出的位全部采用补常数1的方式填充。

      • 注意:如果cliccfg.nlbits = 0,Level的数字值会被认为是固定的255。如图 6‑5中示例所示。

    • Level的数字值越大,则表示其级别越高,注意:

      • 高级别的中断可以打断低级别的中断处理,从而形成中断嵌套,请参见第5.11节的详细介绍。

      • 多个中断同时等待(IP位为高),ECLIC需要仲裁决定哪个中断被发送给内核进行处理,仲裁时需要参考每个中断源的Level数字值。请参见第5.5节的详细介绍。

图 6‑4 Level的数字值解读方式

图 6‑5 cliccfg设置的若干示例

  • 中断源的优先级(Priority)相关的要点如下:

    • Priority的数字值也采取左对齐的方式进行解读,有效位宽(clicinfo.CLICINTCTLBITS -cliccfg.nlbits)之外的低位全部采用补常数1的方式填充。

    • Priority的数字值越大,则表示其优先级越高,注意:

      • 中断优先级(Priority)不参与中断嵌套的判断,即中断能否嵌套与中断优先级(Priority)的数值大小没有关系,而是与中断级别(Level)的数值大小有关。

      • 多个中断同时Pending时,ECLIC需要仲裁决定哪个中断被发送给内核进行处理,仲裁时需要参考每个中断源的Priority数字值。请参见第6.2.12节的详细介绍。

6.2.10. ECLIC中断源的向量或非向量处理(Vector or Non-Vector Mode)

ECLIC的每个中断源均可以设置成向量或者非向量处理(通过寄存器clicintattr[i]的shv域),其要点如下:

  • 如果被配置成为向量处理模式,则该中断被处理器内核响应后,处理器直接跳入该中断的向量入口(Vector Table Entry)存储的目标地址。有关中断向量处理模式的详细介绍,请参见第5.13节。

  • 如果被配置成为非向量处理模式,则该中断被处理器内核响应后,处理器直接跳入所有中断共享的入口地址。有关中断非向量处理模式的详细介绍,请参见第5.13节。

6.2.11. ECLIC中断目标的阈值级别

如图 6‑1中所示,ECLIC可以设置特定的中断目标的阈值级别(mth),其要点如下:

  • mth寄存器是完整的8位寄存器,所有位均可读可写,软件可以通过写此寄存器配置目标阈值。注意:该阈值表征的是一种级别(Level)的数字值。

  • ECLIC最终仲裁出的中断的“级别(Level)数字值”只有高于“mth寄存器中的值”,该中断才能够被发送给处理器内核。

6.2.12. ECLIC中断的仲裁机制

如图 6‑2所示,ECLIC对其所有中断源进行仲裁选择的原则如下:

  • 只有满足下列所有条件的中断源才能参与仲裁:

    • 中断源的使能位(clicintie[i]寄存器)必须为1。

    • 中断源的等待标志位(clicintip[i]寄存器)必须为1。

  • 从所有参与仲裁的中断源中进行仲裁的规则为:

    • 首先判断级别(Level),Level数字值越大的中断源,其仲裁优先级越高。

    • 如果Level相等,则其次判断优先级(Priority),Priority数字值越大的中断源,其仲裁优先级越高。

    • 如果Level和Priority都相等,则再次判断判断中断ID,中断ID越大的中断源,其仲裁优先级越高。

  • 如果最后仲裁出的中断源的Level数字值高于中断目标的阈值级别(mth),则产生最终的中断请求,将通往处理器内核的中断请求信号拉高。

6.2.13. ECLIC中断的响应、嵌套、咬尾机制

ECLIC中断请求发送给处理器内核之后,处理器内核将对其进行响应。通过ECLIC和内核协同,可以支持中断嵌套、快速咬尾等等机制。请参见第5.6节、第5.11节、第5.12节的详细介绍。

7. 内核CSR寄存器介绍

7.1. N级别处理器内核CSR寄存器概述

RISC-V的架构中定义了一些控制和状态寄存器(Control and Status Register, CSR), 用于配置或者记录一些运行的状态。CSR寄存器是处理器核内部的寄存器,使用其专有的12位地址编码空间。

7.2. N级别处理器内核的CSR寄存器列表

Nuclei N级别处理器内核支持的CSR寄存器列表如表 7‑1所示,其中包括RISC-V标准的CSR寄存器(RV32IMAFDC架构支持Machine Mode和User Mode相关)和N级别处理器内核自定义扩展的CSR寄存器。

表 7‑1 N级别处理器内核支持的CSR寄存器列表

类型 CSR地址 读写属性 名称 全称
RISC-V标准CSR (Machine Mode) 0xF11 MRO mvendorid 商业供应商编号寄存器(Machine Vendor ID Register)
0xF12 MRO marchid 架构编号寄存器(Machine Architecture ID Register)
0xF13 MRO mimpid 硬件实现编号寄存器(Machine Implementation ID Register)
0xF14 MRO mhartid Hart编号寄存器(Hart ID Register)
0x300 MRW mstatus 异常处理状态寄存器
0x301 MRW misa 指令集架构寄存器(Machine ISA Register)
0x304 MRW mie 局部中断屏蔽控制寄存器(Machine Interrupt Enable Register)
0x305 MRW mtvec 异常入口基地址寄存器
0x307 MRW mtvt ECLIC中断向量表的基地址
0x340 MRW mscratch 暂存寄存器(Machine Scratch Register)
0x341 MRW mepc 异常PC寄存器(Machine Exception Program Counter)
0x342 MRW mcause 异常原因寄存器(Machine Cause Register)
0x343 MRW mtval 异常值寄存器(Machine Trap Value Register)
0x344 MRW mip 中断等待寄存器(Machine Interrupt Pending Register)
Ox345 MRW mnxti 标准寄存器用于使能中断,处理下一个中断并返回下一个中断的Handler入口地址
0x346 MRW mintstatus 标准寄存器用于保存当前中断Level
0x348 MRW mscratchcsw 标准寄存器用于在特权模式变化时交换mscratch与目的寄存器的值
0x349 MRW mscratchcswl 标准寄存器用于在中断Level变化时交换mscratch与目的寄存器的值
0xB00 MRW mcycle 周期计数器的低32位(Lower 32 bits of Cycle counter)
0xB80 MRW mcycleh 周期计数器的高32位(Upper 32 bits of Cycle counter)
0xB02 MRW minstret 完成指令计数器的低32位(Lower 32 bits of Instructions-retired counter)
0xB82 MRW minstreth 完成指令计数器的高32位(Upper 32 bits of Instructions-retired counter)
N/A MRW mtime 计时器寄存器(Machine-mode timer register)
N/A MRW mtimecmp 计时器比较寄存器(Machine-mode timer compare register)
N/A MRW msip 机器模式软件中断等待寄存器(Machine-mode Software Interrupt Pending Register)
N/A MRW mtimectl 用于停止计时器,切换计时时钟,控制TIMER中断发生时,计数器是否自动清零
0x3A0+x MRW pmpcfg\<x> PMP表项权限配置寄存器
0x3B0+x MRW pmpaddr\<x> PMP表项x的地址配置寄存器
RISC-V标准CSR (User Mode) 0x001 URW fflags 浮点累积异常(Floating-Point Accrued Exceptions)。 注意:此寄存器只有在配置了浮点指令(“F”或者“D”指令子集)时才会存在。
0x002 URW frm 浮点动态舍入模式(Floating-Point Dynamic Rounding Mode)。 注意:此寄存器只有在配置了浮点指令(“F”或者“D”指令子集)时才会存在。
0x003 URW fcsr 浮点控制和状态寄存器(Floating-Point Control and Status Register )。 注意:此寄存器只有在配置了浮点指令(“F”或者“D”指令子集)时才会存在。
0xC00 URO cycle mcycle寄存器的只读副本 注意:该寄存器在User Mode下是否可读由CSR寄存器mcounteren的CY比特域来控制,请参见第7.4.37节了解其详情。
0xC01 URO time mtime寄存器的只读副本 注意:该寄存器在User Mode下是否可读由CSR寄存器mcounteren的TM比特域来控制,请参见第7.4.37节了解其详情。
0xC02 URO instret minstret寄存器的只读副本 注意:该寄存器在User Mode下是否可读由CSR寄存器mcounteren的IR比特域来控制,请参见第7.4.37节了解其详情。
0xC80 URO cycleh mcycleh寄存器的只读副本 注意:该寄存器在User Mode下是否可读由CSR寄存器mcounteren的CY比特域来控制,请参见第7.4.37节了解其详情。
0xC81 URO timeh mtimeh寄存器的只读副本 注意:该寄存器在User Mode下是否可读由CSR寄存器mcounteren的TM比特域来控制,请参见第7.4.37节了解其详情。
0xC82 URO instreth minstreth寄存器的只读副本 注意:该寄存器在User Mode下是否可读由CSR寄存器mcounteren的IR比特域来控制,请参见第7.4.37节了解其详情。
0x801 URW ucode DSP运算溢出标志寄存器 注意:此寄存器只有在配置了“P”扩展指令集时才会存在。
N级别处理器内核自定义CSR 0x320 MRW mcountinhibit 自定义寄存器用于控制计数器的开启和关闭
0x7c3 MRW mnvec NMI处理入口基地址寄存器
0x7c4 MRW msubm 自定义寄存器用于保存Core当前的Trap类型,以及进入Trap前的Trap类型。
0x7c9 MRW mdcause 自定义寄存器,作为mcause的补充,用于保存详细的异常原因。
0x7ca MRW mcache_ctl 自定义寄存器用来控制I-CACHE 相关功能 注意:此寄存器只有配置了I-CACHE才会存在
0x7d0 MRW mmisc_ctl 自定义寄存器用于控制NMI,Misaligned Access和BPU的相关功能
0x7d6 MRW msavestatus 自定义寄存器用于保存mstatus值 注意:此寄存器只有配置了两级异常嵌套恢复才会存在
0x7d7 MRW msaveepc1 自定义寄存器用于保存第一级嵌套NMI或异常的mepc 注意:此寄存器只有配置了两级异常嵌套恢复才会存在
0x7d8 MRW msavecause1 自定义寄存器用于保存第一级嵌套 NMI或异常的mcause 注意:此寄存器只有配置了两级异常嵌套恢复才会存在
0x7d9 MRW msaveepc2 自定义寄存器用于保存第二级嵌套 NMI或异常的mepc 注意:此寄存器只有配置了两级异常嵌套恢复才会存在
0x7da MRW msavecause2 自定义寄存器用于保存第二级嵌套 NMI或异常的mcause 注意:此寄存器只有配置了两级异常嵌套恢复才会存在
0x7db MRW msavedcause1 自定义寄存器用于保存第一级嵌套异常的mdcause 注意:此寄存器只有配置了两级异常嵌套恢复才会存在
0x7dc MRW msavedcause2 自定义寄存器用于保存第二级嵌套异常的mdcause 注意:此寄存器只有配置了两级异常嵌套恢复才会存在
0x7eb MRW pushmsubm 自定义寄存器用于将msubm的值存入堆栈地址空间
0x7ec MRW mtvt2 自定义寄存器用于设定非向量中断处理模式的中断入口地址
0x7ed MRW jalmnxti 自定义寄存器用于使能ECLIC中断,该寄存器的读操作能处理下一个中断同时返回下一个中断Handler的入口地址,并跳转至此地址。
0x7ee MRW pushmcause 自定义寄存器用于将mcause的值存入堆栈地址空间
0x7ef MRW pushmepc 自定义寄存器用于将mepc的值存入堆栈地址空间
0x811 URW sleepvalue WFI的休眠模式寄存器
0x812 URW txevt 发送Event寄存器
0x810 URW wfe Wait for Event控制寄存器
注意:
  • MRW表示Machine Mode Readable/Writeable

  • MRO表示Machine Mode Read-Only

  • URW表示User Mode Readable/Writeable

  • URO表示User Mode Read-Only

7.3. N级别处理器内核的CSR寄存器的访问权限

Nuclei N级别处理器内核对于CSR寄存器的访问权限规定如下:

  • 无论是在Machine Mode还是User Mode下:

  • 如果向不存在的CSR寄存器地址区间进行读写操作,则会产生Illegal Instruction Exception。

  • 注意:fflags、frm、fcsr寄存器只有在配置了浮点指令(“F”或者“D”指令子集)时才会存在。

  • 在Machine Mode下:

    • 对MRW或者URW属性的CSR寄存器进行读写操作则一切正常。

    • 对MRO或者URO属性的CSR寄存器进行读操作则一切正常。

    • 如果向MRO或者URO属性的CSR寄存器进行写操作,则会产生Illegal Instruction Exception。

  • 在User Mode下:

    • 对URW属性的CSR寄存器进行读写操作均一切正常。

    • 对URO属性的CSR寄存器进行读操作则一切正常。

      • 注意:对于URO属性的cycle、cycleh、time、timeh、instret、instreth寄存器,其能否可读还受mcounteren的相关比特域来控制,请参见7.4.37了解其详情。
    • 如果向URO属性的CSR寄存器进行写操作,则会产生Illegal Instruction Exception。

    • 如果向MRW或者MRO属性的CSR寄存器进行读写操作,则会产生Illegal Instruction Exception。

7.4. N级别处理器内核支持的RISC-V标准CSR

本节介绍N级别处理器核支持的RISC-V标准CSR寄存器

7.4.1. misa

misa寄存器用于指示当前处理器所支持的架构特性。

misa寄存器的最高两位用于指示当前处理器所支持的架构位数:

  • 如果最高两位值为1,则表示当前为32位架构(RV32)。

  • 如果最高两位值为2,则表示当前为64位架构(RV64)。

  • 如果最高两位值为3,则表示当前为128位架构(RV128)。

misa寄存器的低26位用于指示当前处理器所支持的RISC-V ISA中不同模块化指令子集,每一位表示的模块化指令子集如图 7‑1中所示。该寄存器其他未使用到的比特域为常数0。

图 7‑1 misa寄存器低26位各域表示的模块化指令子集

注意:misa寄存器在RISC-V架构文档中被定义为可读可写的寄存器,从而允许某些处理器的设计能够动态地配置某些特性。但是在N级别处理器内核的实现中,misa寄存器为只读寄存器,恒定地反映不同型号处理器核所支持的ISA模块化子集,写此寄存器会被忽略。譬如N205核支持RV32IMC,则反映于此寄存器中,最高两位值为1,低26位中I/M/C对应域的值即为高。

7.4.2. mie

ECLIC中断模式下mie寄存器的控制位不起作用,读mie返回全0。

7.4.3. mvendorid

此寄存器是只读寄存器,用于反映该处理器核的商业供应商编号(Vendor ID)。

如果此寄存器的值为0,则表示此寄存器未实现。

7.4.4. marchid

此寄存器是只读寄存器,用于反映该处理器核的硬件实现微架构编号(Microarchitecture ID)。

如果此寄存器的值为0,则表示此寄存器未实现。

7.4.5. mimpid

此寄存器是只读寄存器,用于反映该处理器核的硬件实现编号(Implementation ID)。

如果此寄存器的值为0,则表示此寄存器未实现。

7.4.6. mhartid

此寄存器是只读寄存器,用于反映当前Hart的编号(Hart ID)。

Hart(取“Hardware Thread”之意)表示一个硬件线程,单个处理器核中可能实现多份硬件线程,譬如硬件超线程(Hyper-threading)技术,每套线程有自己独立的寄存器组等上下文资源,但大多数的运算资源均被所有硬件线程复用,因此面积效率很高。在这样的硬件超线程处理器中,一个核内便存在着多个硬件线程(Hart)。

N级别处理器内核中Hart编号值受输入信号core_mhartid控制。注意:RISC-V架构规定,如果在单Hart或者多Hart的系统中,起码要有一个Hart的编号必须是0。

7.4.7. mstatus

mstatus寄存器是机器模式(Machine Mode)下的状态寄存器。mstatus寄存器中各控制位域如表 7‑2所示。

表 7‑2 mstatus寄存器各控制位

复位值 描述
Reserved 0 N/A 未使用的域为常数0
SIE 1 0 参见第7.4.8节了解其详情
Reserved 2 N/A 未使用的域为常数0
MIE 3 0 参见第7.4.9节了解其详情
Reserved 4 N/A 未使用的域为常数0
SPIE 5 0 参见第7.4.10节了解其详情
Reserved 6 N/A 未使用的域为常数0
MPIE 7 0 参见第7.4.11节了解其详情
Reserved 10:8 N/A 未使用的域为常数0
MPP 12:11 0 参见第7.4.11节了解其详情
FS 14:13 0 参见第7.4.12节了解其详情
XS 16:15 0 参见第7.4.13节了解其详情
MPRV 17 0 参见第7.4.14节了解其详情
SUM 18 0 参见第7.4.15节了解其详情
Reserved 30:19 N/A 未使用的域为常数0
SD 31 0 参见第7.4.16节了解其详情

7.4.8. mstatus 的SIE域

mstatus寄存器中的SIE域只有在配置了TEE才有效,否则为常数0,具体信息请参见《Nuclei TEE Architecture》文档。

7.4.9. mstatus的MIE域

mstatus寄存器中的MIE域表示机器模式下的全局中断使能:

当MIE域的值为1时,表示中断的全局开关打开,中断能够被正常响应;

当MIE域的值为0时,表示全局关闭中断,中断被屏蔽,无法被响应。

当处理器内核处于用户模式下时,中断无法被屏蔽,一律正常响应。

注意:N级别处理器内核在进入异常、中断或者NMI处理模式时,MIE的值会被更新成为0(意味着进入异常、中断或者NMI处理模式后中断被屏蔽)。

7.4.10. mstatus的SPIE与SPP域

mstatus寄存器中的SPIE和SPP域只有在配置了TEE才有效,否则为常数0,具体信息请参见《Nuclei TEE Architecture》文档。

7.4.11. mstatus的MPIE和MPP域

mstatus寄存器中的MPIE和MPP域分别用于自动保存进入异常,NMI和中断之前mstatus.MIE、特权模式(Privilege Mode)时进行自动恢复。

N级别处理器内核进入异常时更新mstatus寄存器MPIE和MPP域的硬件行为,请参见3.4.5节了解其详情。

N级别处理器内核退出异常时(在异常处理模式下执行mret指令)更新mstatus寄存器MPIE和MPP域的硬件行为,请参见3.5.2节了解其详情。

N级别处理器内核进入NMI时更新mstatus寄存器MPIE和MPP域的硬件行为,请参见4.3.4节了解其详情。

N级别处理器内核退出NMI时(在异常处理模式下执行mret指令)更新mstatus寄存器MPIE和MPP域的硬件行为,请参见4.4.2节了解其详情。

N级别处理器内核进入中断时更新mstatus寄存器MPIE和MPP域的硬件行为,请参见5.6.5节了解其详情。

N级别处理器内核退出中断时(在异常处理模式下执行mret指令)更新mstatus寄存器MPIE和MPP域的硬件行为,请参见5.7.2节了解其详情。

注意: mstatus.MPIE域和mstatus.MPP域的值与mcause.MPIE域和mcause.MPP域的值是镜像关系,即,在正常情况下,mstatus.MPIE域的值与mcause.MPIE域的值总是完全一样,mstatus.MPP域的值与mcause.MPP域的值总是完全一样。

7.4.12. mstatus的FS域

mstatus寄存器中的FS域用于维护或反映浮点单元的状态。

注意:此域只有在配置了浮点指令(“F”或者“D”指令子集)时才会存在。

FS域由两位组成,其编码如下图所示。

图 7‑2 FS域表示的状态编码

FS域的更新准则如下:

  • FS上电后的默认值为0,意味着浮点单元的状态为Off。因此为了能够正常使用浮点单元,软件需要使用CSR写指令将FS的值改写为非0值以打开浮点单元(FPU)的功能。

  • 如果FS的值为1或者2,当执行了任何的浮点指令之后,FS的值会自动切换为3,表示浮点单元的状态为Dirty(状态发生了改变)。

  • 如果处理器不想使用浮点运算单元(譬如将浮点单元关电以节省功耗),可以使用CSR写指令将mstatus寄存器的FS域设置成0,将浮点单元的功能予以关闭。当浮点单元的功能关闭之后,任何访问浮点CSR寄存器的操作或者任何执行浮点指令的行为都将会产生非法指令(Illegal Instruction)异常。

除了用于上述功能,FS域的值还用于操作系统在进行上下文切换时的指引信息,感兴趣的用户请参见RISC-V“特权架构文档版本1.10”原文。

7.4.13. mstatus的XS域

mstatus寄存器中的XS域与FS域的作用类似,但是其用于维护或反映用户自定义的扩展指令单元状态。

在标准的RISC-V“特权架构文档版本1.10”中定义XS域为只读域,其用于反映所有自定义扩展指令单元的状态总和。但请注意:在N级别处理器内核的硬件实现中,将XS域设计成可写可读域,其作用完全与FS域类似,软件可以通过改写XS域的值达到打开或者关闭协处理器扩展指令单元的目的。

与FS域类似,XS除了用于上述功能之外还用于操作系统在进行上下文切换时的指引信息,感兴趣的用户请参见RISC-V“特权架构文档版本1.10”原文。

7.4.14. mstatus的MPRV域

mstatus寄存器中的MPRV域用于控制在Machine Mode下存储器的数据读写(Load和Store)操作是否被当作User Mode下的操作来进行PMP的保护。

如果MPRV域的值为1,并且MPP域的值表示User Mode,则PMP将存储器的数据读写(Load和Store)操作被当作发生在User Mode下来进行保护,并根据表项配置的R/W属性进行权限确认,如果违反了权限,则触发异常。请参见8.4节了解其详情。

7.4.15. mstatus的SUM域

mstatus寄存器中的SUM域用于控制在Supervisor Mode下是否被允许读写(Load和Store)用户(User)存储区域的数据。

7.4.16. mstatus的SD域

mstatus寄存器中的SD域是一个只读域,其反映了XS域或者FS域处于脏(Dirty)状态。其逻辑关系表达式为:SD = ((FS==11) OR (XS==11))。

之所以设置此只读的SD域是为了方便软件快速的查询XS域或者FS域是否处于脏(Dirty)状态,从而在上下文切换时可以快速判断是否需要对浮点单元或者扩展指令单元进行上下文的保存。感兴趣的用户请参见RISC-V“特权架构文档版本1.10”原文。

7.4.17. mtvec

mtvec寄存器用于配置中断和异常处理程序的入口地址。

  • 当mtvec配置中断的异常处理程序入口地址时要点如下:

    • 异常处理程序采用4byte对齐的mtvec地址(将mtvec的低2bit用0代替)作为入口地址。
  • 当mtvec配置中断程序的入口地址时要点如下:

    • 当mtvec.MODE != 6’b000011 时,处理器使用“默认中断模式”。

    • 当mtvec.MODE = 6’b000011时,处理器使用“ECLIC中断模式”,推荐使用此模式。

      • 中断为非向量处理模式时的入口地址和要点如第5.13.2节中所述。

      • 中断为向量处理模式时的入口地址和要点如第5.13.1节中所述。

mtvec寄存器各地址位域如表 7‑3所示。

表 7‑3 mtvec寄存器各控制位

描述
ADDR 31:6 mtvec地址
MODE 5:0 MODE域为中断处理模式控制域:
  • 000011: ECLIC中断模式(推荐模式)

  • Others: 默认中断模式

7.4.18. mtvt

mtvt寄存器用于保存ECLIC中断向量表的基地址,此基地址至少为64byte对齐。

为了提升性能减少硬件门数,硬件根据实际实现的中断的个数来决定mtvt的对齐方式,具体如表 7‑4所示。

表 7‑4 mtvt对齐方式

最大中断个数 mtvt对齐方式
0 to 16 64-byte
17 to 32 128-byte
33 to 64 256-byte
65 to 128 512-byte
129 to 256 1KB
257 to 512 2KB
513 to 1024 4KB

7.4.19. mscratch

mscratch寄存器用于Machine Mode下的程序临时保存某些数据。mscratch寄存器可以提供一种保存和恢复机制,譬如,在进入中断或者异常处理模式后,将应用程序的用户堆栈指针(SP)寄存器临时存入mscratch寄存器中,然后在退出异常处理程序之前,将mscratch寄存器中的值读出恢复至用户堆栈指针(SP)寄存器。

7.4.20. mepc

mepc寄存器用于保存进入异常之前处理器正在执行指令的PC值,作为异常的返回地址。

为了理解此寄存器,请先参见第3章系统地了解异常的相关信息。

注意:

  • 处理器进入异常时,mepc寄存器被同时更新以反映当前遇到异常的指令的PC值。

  • 值得注意的是,虽然mepc寄存器会在异常发生时自动被硬件更新,但是mepc寄存器本身也是一个(在Machine Mode下)可读可写的寄存器,因此软件也可以直接写该寄存器以修改它的值。

mepc寄存器各地址位域如表 7‑5所示。

表 7‑5 mepc寄存器各控制位

描述
EPC 31:1 保存异常发生前处理器正在执行的指令的PC值
Reserved 0 未使用的域为常数0

7.4.21. mcause

mcause寄存器,用于保存进入NMI、异常和中断之前的出错原因,以便于对Trap原因进行诊断和调试。

mcause寄存器各地址位域如表 7‑6所示。

表 7‑6 mcause寄存器各控制位

描述
INTERRUPT 31 表示当前Trap种类:
MINHV 30 表示处理器正在读取中断向量表
MPP 29:28 进入中断之前的特权模式,与mstatus.mpp相同
MPIE 27 进入中断之前的中断使能,与mstatus.mpie相同
Reserved 26:24 未使用的域为常数0
MPIL 23:16 前一个中断级别
Reserved 15:12 未使用的域为常数0
EXCCODE 11:0 异常/中断编码
  • 0:异常或者NMI

  • 1:中断

注意:

  • mstatus寄存器的MPIE和MPP域与mcause的MPIE和MPP域为镜像关系。

  • NMI的mcause.EXCCODE可能为0x1或者0xfff,实际值由mmisc_ctl控制,详情请参考7.5.5节。

7.4.22. mtval (mbadaddr)

mtval寄存器(又名mbadaddr,有些版本的工具链只识别此名称),用于保存进入异常之前的出错指令的编码值或者存储器访问的地址值,以便于对异常原因进行诊断和调试。

为了理解此寄存器,请先参见第3章系统地了解异常的相关信息。

N级别处理器内核进入异常时,mtval寄存器被同时更新以反映当前遇到异常的信息。

7.4.23. mip

ECLIC中断模式下mip寄存器的控制位不起作用,读mip返回全0。

7.4.24. mnxti

mnxti (Next Interrupt Handler Address and Interrupt-Enable CSR)可以被软件访问用来处理处于相同Privilege Mode下的下一个中断,同时不会造成冲刷流水线以及上下文保存恢复。

mnxti寄存器可通过CSRRSI/CSRRCI指令来访问,读操作返回值是下一个中断的handler地址,而mnxti的写回操作会更新中断使能的状态。

注意:

  1. 对于不同Privilege Mode的中断,硬件会以中断嵌套的方式处理,因此mnxti只会处理相同Privilege Mode下的下一个中断。

  2. mnxti寄存器与常规的CSR指令不一样,其返回值与常规寄存器的RMW(read-modify-write)操作的值不同:

    • mnxti的CSR读操作的返回值有以下两种情况:

      • 当出现以下情况时,返回值为0。

        • 没有可以响应的中断

        • 当下最高优先级的中断是向量中断

      • 当中断为非向量中断时,返回此中断的中断处理程序入口地址。

    • mnxti的CSR写操作会更新以下寄存器及寄存器域:

      • mstatus是当前RMW(read-modify-write)操作的目的寄存器

      • mcause.EXCCODE域和会被分别更新为当前响应中断的中断id

      • mintstatus.MIL域被更新为当前响应中断的中断级别(Level)。

7.4.25. mintstatus

mintstatus寄存器保存每个Privilege Mode下的有效中断的中断level。注意,写此寄存器会被忽略。

表 7‑7 minstatus寄存器的控制位

描述
MIL 31:24 Machine Mode的有效中断level
Reserved 23:8 未使用的域为常数0
UIL 7:0 User Mode的有效中断level

7.4.26. mscratchcsw

mscratchcsw寄存器用于在不同特权模式间切换时,交换目的寄存器与mscratch的值来加速中断处理。

使用带读操作的CSR指令访问mscratchcsw,在出现中断前后特权模式不一致时,有以下伪指令所示的寄存器操作:

csrrw rd, mscratchcsw, rs1

// Pseudocode operation.

if (mcause.mpp!=M-mode) then {

t = rs1; rd = mscratch; mscratch = t;

} else {

rd = rs1; // mscratch unchanged.

}

// Usual use: csrrw sp, mscratchcsw, sp

处理器在低特权模式(Privilege Mode)时发生中断,处理器进入高特权模式处理中断,在处理中断时,需要使用堆栈来保存进入中断前的处理器状态。此时如果继续使用低特权模式下的堆栈指针(SP), 则高特权模式下堆栈的数据会存储在低特权模式可以访问的区间,导致出现高特权模式的数据泄露给低特权模式这一安全漏洞。为避免此安全漏洞,RISC-V架构规定当处理器处于低特权模式时,需要将高特权模式的堆栈指针(SP)保存至mscratch寄存器,这样在进入高特权模式后,处理器可以用mscratch寄存器的值来恢复高特权模式的堆栈指针(SP)。

使用常规指令来执行以上的程序需要耗费较多的cycle, 为此RISC-V架构定义mscratchcsw寄存器,在进入中断后立刻执行mscratchcsw寄存器指令,交换mscratch与SP的值,用来恢复高特权模式的堆栈指针(SP),同时备份低特权模式的堆栈指针(SP)至mscratch寄存器。在执行mret指令退出中断前,也加上一条mscratchcsw指令, 交换mscratch寄存器和堆栈指针(SP)的值,将高特权模式的堆栈指针(SP)再次备份到mscratch,同时恢复低特权模式的堆栈指针(SP)。这样,只需要两条指令便可以解决不同特权模式的堆栈指针(SP)切换问题,加速了中断处理。

注意:为了避免虚拟化的漏洞,软件不能直接读取处理器当前的特权模式(Privilege Mode)。如果软件试图在更低的特权模式下访问给定特权模式下的mscratchcsw做寄存器swap操作会导致处理器进入Trap,因此mscratchcsw不会导致虚拟化漏洞。

7.4.27. mscratchcswl

mscratchcswl寄存器用于支持同一特权模式下出现中断程序与应用程序切换时,快速交换堆栈指针。

使用带读操作的CSR指令访问mscratchcsw,当特权模式不变,在出现中断程序和应用程序的切换时,有以下伪指令所示的寄存器操作:

csrrw rd, mscratchcswl, rs1

// Pseudocode operation.

if ( (mcause.mpil==0) != (mintstatus.mil == 0) ) then {

t = rs1; rd = mscratch; mscratch = t;

} else {

rd = rs1; // mscratch unchanged.

}

// Usual use: csrrw sp, mscratchcswl, sp

在单一特权模式下,将中断处理程序任务与应用程序任务的堆栈空间分离可以增强健壮性、减少空间使用并有助于系统调试。中断处理程序任务具有非零中断级别,而应用程序任务具有零中断级别,根据这一特性RISC-V架构定义了mscratchcswl寄存器。与mscratchcsw类似,在中断程序入口和出口分别添加一条mscratchcswl可以实现中断处理程序与应用程序之间的快速的堆栈指针切换,保证中断处理程序和应用程序的堆栈空间分离。

7.4.28. mcycle和mcycleh

RISC-V架构定义了一个64位宽的时钟周期计数器,用于反映处理器执行了多少个时钟周期。只要处理器处于执行状态时,此计数器便会不断自增计数。

mcycle寄存器反映了该计数器低32位的值,mcycleh寄存器反映了该计数器高32位的值。

mcycle和mcycleh寄存器可以用于衡量处理器的性能,且具备可读可写属性,因此软件可以通过CSR指令改写mcycle和mcycleh寄存器中的值。

由于考虑到此计数器计数会消耗某些动态功耗,因此在N级别处理器的实现中,在自定义CSR寄存器mcountinhibit中额外增加了一位控制域,软件可以配置此控制域将mcycle和mcycleh对应的计数器停止计数,从而在不需要衡量性能之时停止计数器以达到省电的作用。请参见7.5.1节了解更多mcountinhibit寄存器信息。

注意:如果在调试模式下时,此计数器并不会计数,只有在正常功能模式下,计数器才会进行计数。

7.4.29. minstret和minstreth

RISC-V架构定义了一个64位宽的指令完成计数器,用于反映处理器成功执行了多少条指令。只要处理器每成功执行完成一条指令,此计数器便会自增计数。

minstret寄存器反映了该计数器低32位的值,minstreth寄存器反映了该计数器高32位的值。

minstret和minstreth寄存器可以用于衡量处理器的性能,且具备可读可写属性,因此软件可以通过CSR指令改写minstret和minstreth寄存器中的值。

由于考虑到此计数器计数会消耗某些动态功耗,因此在N级别处理器内核的实现中,在自定义的CSR寄存器mcountinhibit中额外增加了一位控制域,软件可以配置此控制域将minstret和minstreth对应的计数器停止计数,从而在不需要衡量性能之时停止计数器以达到省电的作用。请参见7.5.1节了解更多mcountinhibit寄存器信息。

注意:如果在调试模式下时,此计数器并不会计数,只有在正常功能模式下,计数器才会进行计数。

7.4.30. mtime、mtimecmp、msip和mtimectl

RISC-V架构定义了一个64位的计时器,按照系统的低速实时时钟(Real Time Clock)频率进行计时。该计时器的值实时反映在mtime寄存器中。RISC-V架构还定义了一个64位的mtimecmp寄存器,该寄存器作为计时器的比较值,假设计时器的值mtime大于或者等于mtimecmp的值,则产生计时器中断。注意:RISC-V架构没有将mtime和mtimecmp寄存器定义为CSR寄存器,而是定义为存储器地址映射(Memory Address Mapped)的系统寄存器,具体的存储器映射地址RISC-V架构并没有规定,而是交由内核设计者自行实现。

RISC-V架构定义了一种软件中断,可以通过软件写1至msip寄存器来触发。有关软件中断的信息请参见6.1.6节。注意:RISC-V架构并没有定义msip寄存器为CSR寄存器,而定义为存储器地址映射的系统寄存器,具体的存储器映射地址RISC-V架构并没有规定,而是交由内核设计者自行实现。

在N级别处理器内核的实现中,mtime/mtimecmp/msip/mtimectl均由TMR单元实现,有关TMR实现要点以及mtime/mtimecmp/msip/mtimectl分配的存储器地址区间,请参见各个系列的简明数据手册。

注意:由于mtime/mtimecmp/msip/mtimectl的地址是存储器地址空间,因此其访问权限由PMP进行设定和保护。

由于考虑到计时器计数会消耗某些动态功耗,因此在N级别处理器的实现中,在自定义mtimectl寄存器中额外增加了一位控制域,软件可以配置此控制域将mtime对应的计时器停止计数,从而在不需要之时停止计时器达到省电的作用。

注意:如果在调试模式下时,此计数器并不会计数,只有在正常功能模式下,计数器才会进行计数。

7.4.31. fcsr

此寄存器只有在配置了浮点指令(“F”或者“D”指令子集)时才会存在。

RISC-V架构规定,如果支持单精度浮点指令或者双精度浮点指令,则需要增加一个浮点控制状态寄存器。该寄存器包含了浮点异常标志位域(Accrued Exceptions)和浮点舍入模式(Rounding Mode)域。

fcsr寄存器中各控制位域如表 7‑8所示。

表 7‑8 fcsr寄存器各控制位

描述
Reserved 31:8 未使用的域为常数0
Rounding Mode(frm) 7:5 浮点舍入模式
Accrued Exceptions(fflags) NV 4 非法操作(Invalid Operation)异常标志位
DZ 3 除零(Divide by Zero)异常标志位
OF 2 上溢出(Overflow)异常标志位
UF 1 下溢出(Underflow)异常标志位
NX 0 非精确(Inexact)异常标志位

7.4.32. fflags

此寄存器只有在配置了浮点指令(“F”或者“D”指令子集)时才会存在。

fflags寄存器为浮点控制状态寄存器(fcsr)中浮点异常标志位(Accrued Exceptions)域的别名。之所以单独定义一个fflags寄存器,是为了方便使用CSR指令直接地单独读写浮点异常标志位域。

如表 7‑8所示, fcsr寄存器包含浮点异常标志域(fflags),不同的异常标志位所表示的异常类型。如果浮点运算单元在运算中出现了相应的异常,则会将fcsr寄存器中对应的异常标志位设置为高,且会一直保持累积。软件可以通过写0的方式单独清除某个异常标志位。

注意:在很多处理器架构中,浮点运算产生结果异常都会触发异常跳转(Trap)从而进入异常模式。但是RISC-V架构的浮点指令在产生结果异常时并不会跳转进入异常模式,而是如上所述仅设置fcsr寄存器中的异常标志位。

7.4.33. frm

此寄存器只有在配置了浮点指令(“F”或者“D”指令子集)时才会存在。

frm寄存器为浮点控制状态寄存器中浮点舍入模式(Rounding Mode)域的别名。之所以单独定义一个frm寄存器,是为了方便使用CSR指令直接地单独读写浮点舍入模式。

根据IEEE-754标准,浮点数运算需要指定舍入模式(Rounding Mode), RISC-V架构浮点运算的舍入模式可以通过两种方式指定。

  • 静态舍入模式:浮点指令的编码中有3位作为舍入模式域,有关浮点指令列表以及指令编码请参见“指令集文档版本2.2”(riscv-spec-v2.2.pdf)。RISC-V架构支持如

  • 表 7‑9所示的五种合法的舍入模式。

    • 如果舍入模式编码为101或者110,则为非法模式。

    • 如果舍入模式编码为111,则意味着使用动态舍入模式。

  • 动态舍入模式:如果使用动态舍入模式,则使用fcsr寄存器中的舍入模式域。如表 7‑8所示,fcsr寄存器包含舍入模式域。不同的舍入模式编码同样如

  • 表 7‑9所示,仅支持物种合法的舍入模式。如果fcsr寄存器中的舍入模式域指定为非法的舍入模式,则后续浮点指令会产生非法指令异常。

表 7‑9 舍入模式位

舍入模式编码 舍入模式(Rounding Mode) 描述
000 RNE 最近舍入,朝向偶数方向(round to nearest, ties to even)
001 RTZ 朝零舍入(round towards zero)
010 RDN 向下舍入(round down)
011 RUP 向上舍入(round up)
100 RMM 最近舍入,朝向最大幅度方向(round to nearest, tiest to max magnitude)
101 非法值
110 非法值
111 动态舍入模式

7.4.34. cycle和cycleh

cycle和cycleh分别是mcycle和mcycleh的只读副本。该寄存器在User Mode下是否可读由CSR寄存器mcounteren的CY比特域来控制,请参见第7.4.37节了解其详情。

7.4.35. instret和instreth

instret和instreth分别是minstret和minstreth的只读副本。该寄存器在User Mode下是否可读由CSR寄存器mcounteren的IR比特域来控制,请参见第7.4.37节了解其详情。

7.4.36. time和 timeh

time和timeh分别是mtime和mtimeh的只读副本。该寄存器在User Mode下是否可读由CSR寄存器mcounteren的TM比特域来控制,请参见第7.4.37节了解其详情。

7.4.37. mcounteren

该寄存器只有在支持User Mode的配置下才会存在。mcounteren寄存器中各控制位域如表 7‑10所示。

表 7‑10 mcounteren寄存器各控制位

描述
CY 0 此位控制在User Mode下是否能够访问cycle和cycleh寄存器:
TM 1 此位控制在User Mode下是否能够访问time和timeh寄存器:
IR 2 此位控制在User Mode下是否能够访问instret和instreth寄存器:
Reserved 3\~31 其他未使用的域为常数0
  • 如果此位为1,则在User Mode下能够正常访问cycle和cycleh。

  • 如果此位为0,则在User Mode下访问cycle和cycleh会触发illegal instruction exception。

此位复位默认值为0

  • 如果此位为1,则在User Mode下能够正常访问time和timeh。

  • 如果此位为0,则在User Mode下访问time和timeh会触发illegal instruction exception。

此位复位默认值为0

  • 如果此位为1,则在User Mode下能够正常访问instret和instreth。

  • 如果此位为0,则在User Mode下访问instret和instreth会触发illegal instruction exception。

此位复位默认值为0

7.4.38. pmpcfg\<x>寄存器

pmpcfg\<x>寄存器用于指定PMP表项的权限配置寄存器,每个pmpcfg\<x>寄存器可以管理四个PMP表项目,譬如pmpcfg0包含了四个不同的域(pmp0cfg\~pmp4cfg),分别管理表项0到表项4。有关pmpcfg\<x>寄存器的详细信息请参见8.2.2节。

7.4.39. pmpaddr\<x>寄存器

pmpaddr\<x>寄存器用于指定PMP每个表项的地址配置寄存器。有关pmpaddr\<x>寄存器的详细信息请参见8.2.3节。

7.4.40. ucode

此寄存器只有配置了数字信号处理单元(“P”扩展)才存在。

ucode寄存器主要用来记录DSP运算的结果是否有溢出,如果有溢出则将溢出标志位置1,可以通过软件写入的方式将溢出标志位清0。ucode寄存器各控制位域如表 7‑11所示。

表 7‑11 ucode寄存器各控制位

描述
OV 0 DSP溢出标志位,如果有溢出则将溢出标志位置1,写0可清除。
Reserved 31:1 未使用的域为常数0

7.5. N级别处理器内核自定义的CSR

本节介绍N级别处理器核自定义的CSR寄存器。

7.5.1. mcountinhibit

mcountinhibit寄存器用于控制mcycle和minstret的计数,各控制位域如表 7‑12所示。

表 7‑12 mcountinhibit寄存器各控制位

描述
Reserved 31:3 未使用的域为常数0
IR 2 IR为1时minstret的计数被关闭
Reserved 1 未使用的域为常数0
CY 0 CY为1时mcycle的计数被关闭

7.5.2. mnvec

mnvec寄存器用于配置NMI的入口地址,写此寄存器会被忽略。

为了理解此寄存器,请先参见第4章系统地了解NMI的相关信息。

在处理器的程序执行过程中,一旦遇到NMI发生,则终止当前的程序流,处理器被强行跳转到一个新的PC地址,N级别处理器内核进入NMI后跳入的PC地址即由mnvec寄存器指定。

注意:mnvec的值由mmisc_ctl控制,更多细节请参考7.5.5节。

7.5.3. msubm

N级别处理器内核自定义msubm寄存器用于保存进入Trap前后的Trap类型。

msubm寄存器中各控制位域如表 7‑13所示。

表 7‑13 msubm寄存器各控制位

描述
Reserved 31:10 未使用的域为常数0
PTYP 9:8 保存进入Trap之前的Trap 类型:
TYP 7:6 指示Core当前的Trap类型:
Reserved 5:0 未使用的域为常数0
  • 0:非Trap状态

  • 1:中断

  • 2:异常

  • 3:NMI

  • 0:非Trap状态

  • 1:中断

  • 2:异常

  • 3:NMI

7.5.4. mdcause

mdcause主要用来提供更加详细的异常信息,以区别具有相同mcause值的不同异常。

mdcause寄存器中各控制位域如表 7‑14所示。

表 7‑14 mdcause寄存器各控制位

描述
Reserved 31:2 未使用的域为常数0
mdcause 1:0 更详细的异常信息,以区别具有相同mcause的不同异常。 当mcause=1 (指令访问错误):
  • 0:Reserved

  • 1: PMP检测指令访问出错

  • 2:指令访问返回总线错误

  • 3: Reserved

当mcause=5(读取存储器访问错误)

  • 0:Reserved

  • 1: PMP检测读操作访问存储器出错

  • 2: 读操作访问存储器返回总线错误

  • 3: NICE长指令错误

当mcause=7(写入存储器访问错误)

  • 0: Reserved

  • 1: PMP检测写操作访问存储器出错

  • 2: 写操作访问存储器返回总线错误

  • 3: Reserved

7.5.5. mcache_ctl

此寄存器只有配置了I-CACHE才会存在。

mcache_ctl主要用来控制I-CACHE的开启关闭以及Scratchpad的模式。具体的控制位域如表 7‑15所示。

表 7‑15 mcache_ctl 寄存器各控制位

描述
Reserved 31:2 未使用的域为常数0
IC_SCPD_MOD 1 Scratchpad的模式:
IC_EN 0 I-CACHE使能:
  • 0:Scratchpad作为ICache的Data Ram,默认值

  • 1:Scratchpad作为ILM的SRAM

  • 0:I-CACHE 关闭,默认值。

  • 1:I-CACHE 开启。

注意:只有在mcache_ctl[1:0]为2’b10时,Scratchpad作为ILM的SRAM,否则Scratchpad是被当作ICache的Data Ram使用。

7.5.6. mmisc_ctl

N级别处理器内核自定义mmisc_ctl寄存器用于控制NMI,Misaligned Access和BPU的相关功能。

mmisc_ctl寄存器中各控制位域如表 7‑15所示。

表 7‑16 mmisc_ctl寄存器各控制位

描述
Reserved 31:10 未使用的域为常数0
NMI_CAUSE_FFF 9 控制mnvec及NMI的mcause.EXCCODE:
Reserved 8:7 未使用的域为常数0
MISALIGN 6 控制内核是否支持Misaligned Access功能:
Reserved 5:4 未使用的域为常数0
BPU 3 控制动态分支预测器是否开启:
Reserved 2:0 未使用的域为常数0
  • 0:mnvec的值等于处理器reset后的PC, NMI的mcause.EXCCODE为0x1,此为默认值。

  • 1:mnvec的值与mtvec一致,NMI的mcause.EXCCODE为0xfff

  • 0:Misaligned Access 功能关闭,Misaligned Access操作会产生异常

  • 1:Misaligned Access 功能开启, 此为默认值

注意:此域只有配置了非对齐数据访问功能才有效,否则为常数0

  • 0:动态分支预测器关闭,使用静态分支预测器

  • 1:动态分支预测器开启,此为默认值

注意:此域只有配置了动态分支预测器才有效,否则为常数0

7.5.7. msavestatus

msavestatus用于存储mstatus和msubm的值,以保证mstatus和msubm的各个域的状态不会被NMI或者异常冲刷掉。msavestatus有两级堆栈,最多可支持3级异常/NMI状态保存。更多两级NMI/异常状态堆栈,请参见4.6节。

msavestatus寄存器各控制位如表 7‑16所示。

表 7‑17 msavestatus寄存器各控制位

描述
Reserved 31:16 未使用的域为常数0
PTYP2 15:14 第二级嵌套NMI/异常发生前的Trap类型
Reserved 13:11 未使用的域为常数0
MPP2 10:9 第二级嵌套NMI/异常发生前的Privilege mode
MPIE2 8 第二级嵌套NMI/异常发生前的中断使能状态
PTYP1 7:6 第一级嵌套NMI/异常发生前的Trap类型
Reserved 5:3 未使用的域为常数0
MPP1 2:1 第一级嵌套NMI/异常发生前的Privilege mode
MPIE1 0 第一级嵌套NMI/异常发生前的中断使能状态

7.5.8. msaveepc1和msaveepc2

msaveepc1和msaveepc2分别作为一级NMI/异常状态堆栈和二级NMI/异常状态堆栈,用来存储第一级嵌套NMI/异常发生前的PC,以及第二级嵌套NMI/异常发生前的PC。

  • msaveepc2 \<= msaveepc1 \<= mepc \<= interrupted PC \<= NMI/exception PC

当执行mret指令,同时mcause.INTERRUPT为0(例如NMI,或者异常),msaveepc1和msaveepc2分别通过一级和两级NMI/异常状态堆栈来恢复处理器的PC。

  • msaveepc2 => msaveepc1 => mepc => PC

7.5.9. msavecause1和msavecause2

msavecause1和msavecause2分别作为一级NMI/异常状态堆栈和二级NMI/异常状态堆栈,用来存储第一级嵌套NMI/异常发生前的mcause,以及第二级嵌套NMI/异常发生前的mcause。

  • msavecause2 \<= msavecause1 \<= mcause \<= NMI/exception cause

当执行mret指令,同时mcause.INTERRUPT为0(例如NMI,或者异常),msavecause1和msavecause2分别通过一级和两级NMI/异常状态堆栈来恢复mcause状态。

  • msavecause2 => msavecause1 => mcause

7.5.10. msavedcause1和msavedcause2

msavedcause1和msavedcause2分别作为一级NMI/异常状态堆栈和二级NMI/异常状态堆栈,用来存储第一级嵌套NMI/异常发生前的mdcause,以及第二级NMI/异常发生前的mdcause。

  • msavedcause2 \<= msavedcause1 \<= mdcause \<= exception dcause

当执行mret指令,同时mcause.INTERRUPT为0(例如NMI,或者异常),msavedcause1和msavedcause2分别通过一级和两级NMI/异常状态堆栈来恢复mcause状态。

  • msavedcause2 => msavedcause1 => mdcause

7.5.11. pushmsubm

N级别处理器定义了通过pushmsubm寄存器csrrwi操作实现的 CSR指令,存储msubm的值到堆栈指针作为基地址的memory空间.

以如下指令为例介绍此CSR指令:

csrrwi x0, PUSHMSUBM, 1

该指令的操作是将msubm寄存器的值存到SP(堆栈指针)+1*4的地址。

7.5.12. mtvt2

mtvt2用于指定ECLIC非向量模式的中断common-code入口地址。

mtvt2寄存器中各控制位域如表 7‑17所示。

表 7‑18 mtvt2寄存器各控制位

描述
CMMON-CODE-ENTRY 31:2 在mtvt2.MTVT2EN=1时,此域决定ECLIC非向量模式中断common-code入口地址。
Reserved 1 未使用的域为常数0
MTVT2EN 0 mtvt2使能位:
  • 0:ECLIC非向量模式中断common-code入口地址由mtvec决定

  • 1: ECLIC非向量模式中断common-code入口地址由mtvt2.COMMON-CODE-ENTRY决定

7.5.13. jalmnxti

N级别处理器定义了jalmnxti寄存器用于减少中断延迟,加速中断咬尾。

jalmnxti除了包含mnxti的开启中断使能,处理下一个中断,返回下一个中断的入口地址等功能之外,还有跳转至中断handler的功能,因此可以缩短中断处理的指令个数,达到减少中断延迟,加速中断咬尾的目的。有关jalmnxti的更多细节请参见5.13.1.3节。

7.5.14. pushmcause

N级别处理器定义了通过pushmcause寄存器csrrwi操作实现的 CSR指令,存储mcause的值到堆栈指针作为基地址的memory空间.

以如下指令为例介绍此CSR指令:

csrrwi x0, PUSHMCAUSE, 1

该指令的操作是将mcause寄存器的值存到SP(堆栈指针)+1*4的地址。

7.5.15. pushmepc

N级别处理器定义了通过pushmepc寄存器csrrwi操作实现的 CSR指令,存储mepc的值到堆栈指针作为基地址的memory空间.

以如下指令为例介绍此CSR指令:

csrrwi x0, PUSHMPEC, 1

该指令的操作是将mepc寄存器的值存到SP(堆栈指针)+1*4的地址。

7.5.16. sleepvalue

N级别处理器内核自定义了一个CSR寄存器sleepvalue用于控制不同的休眠模式,请参见第9.1节了解更多详情。sleepvalue寄存器中各控制位域如表 7‑18所示。

表 7‑19 sleepvalue寄存器各控制位

描述
SLEEPVALUE 0 控制WFI的休眠模式
Reserved 31:1 未使用的域为常数0
  • 0:浅度休眠模式(执行WFI后,处理器内核主工作时钟core_clk被关闭)

  • 1:深度休眠模式(执行WFI后,处理器内核主工作时钟core_clk和处理器内核的常开时钟core_aon_clk都被关闭)

此位复位默认值为0

7.5.17. txevt

N级别处理器内核自定义了一个CSR寄存器txevt,用于对外发送Event。

txevt寄存器中各控制位域如表 7‑19所示。

表 7‑20 txevt寄存器各控制位

描述
TXEVT 0 控制发送Event:
Reserved 31:1 未使用的域为常数0
  • 如果向此位写1,则会触发N级别处理器内核的输出信号tx_evt产生一个单周期脉冲信号,作为对外的Event信号。

  • 该比特位为自清比特位,即,向此位写入1之后,下一个周期其被自清为0。

  • 向此位写入0则无任何反应和操作。

此位复位默认值为0

7.5.18. wfe

N级别处理器内核自定义了一个CSR寄存器wfe,用于控制WFI指令的唤醒条件是使用中断还是使用Event。请参见第9.2.3节了解更多详情。

wfe寄存器中各控制位域如表 7‑20所示。

表 7‑21 wfe寄存器各控制位

描述
WFE 0 控制WFI指令的唤醒条件是使用中断还是使用Event。内核低功耗机制介绍
Reserved 31:1 未使用的域为常数0
  • 0: 处理器内核进入休眠模式时,可以被中断和NMI唤醒。

  • 1:处理器内核进入休眠模式时,可以被Event和NMI唤醒。

此位复位默认值为0。

8. 内核内核低功耗机制介绍

8.1. PMP简介

由于N级别处理器内核是面向微控制器领域的低功耗内核,其不支持虚拟地址管理单元(MMU),因此所有的地址访问操作都是使用的物理地址。为了根据不同的物理地址区间和不同的Privilege Mode进行权限隔离和保护,N级别处理器内核(可配置地)支持PMP(Physical Memory Protection)单元。

注意:具体每一款处理器内核(譬如N203,N205等)是否支持PMP可能略有差异,请参见各个系列的简明数据手册了解其详情。

PMP的相关要点如下:

  • PMP支持若干个表项(Entries),每个表项设定某个地址区间的权限(是否可读、写、执行)。PMP每个表项的权限设定需要通过CSR寄存器配置完成,相关CSR寄存器的详解请参见第8.2节。

    • 注意:N级别处理器内核的PMP支持的表项数目可以配置,请参见各个系列的简明数据手册了解其配置详情。
  • PMP可以支持最小4个字节(32bits)大小的地址区间进行权限设定。相关CSR寄存器的详解请参见第8.2节。

8.2. PMP的CSR寄存器

PMP的CSR寄存器列表

Nuclei N级别处理器内核的PMP支持的CSR寄存器列表如表 8‑1所示。

表 8‑1 PMP的CSR寄存器列表

类型 CSR 地址 读写属性 名称 全称
PMP相关CSR寄存器 0x3A0 MRW pmpcfg0 PMP表项权限配置寄存器
0x3B0 MRW pmpaddr0 PMP表项0的地址配置寄存器
0x3B1 MRW pmpaddr1 PMP表项1的地址配置寄存器
0x3B2 MRW pmpaddr2 PMP表项2的地址配置寄存器
0x3B3 MRW pmpaddr3 PMP表项3的地址配置寄存器
…… MRW …… ……
(0x3B0+n) MRW pmpaddrn PMP表项n的地址配置寄存器
注意:
  • 在N级别处理器内核中,如果没有配置User Mode,则相应的不会有PMP特性和配套的CSR寄存器。

  • MRW表示只有在Machine Mode下才可以对其进行读写。

8.2.1. pmpcfg\<x>寄存器

pmpcfg\<x>寄存器用于指定PMP表项的权限配置寄存器,每个pmpcfg\<x>寄存器可以管理四个PMP表项目,譬如pmpcfg0包含了四个不同的域(pmp0cfg\~pmp4cfg),分别管理表项0到表项4。其他一次类推,如图 8‑1中所示。

图 8‑1 CSR寄存器pmpcfg0的格式

每个表项的权限配置寄存器pmp\<x>cfg中各控制位域如表 8‑2所示。

表 8‑2 pmp\cfg中各控制域

描述
R 0 此位控制该表项的数据可读权限:
W 1 此位控制该表项的数据可写权限:
X 2 此位控制该表项的代码可执行权限:
A 4:3 此位控制该表项的地址区间模式,参见表 8‑3了解其详情。 此位复位默认值为0。
Reserved 6:5 未使用的域为常数0
L 7 此位控制是否锁定该PMP表项:
  • 如果此位为1,则表示此表项设定的地址区间具备数据可读的权限。

  • 如果此为为0,则表示此表项设定的地址区间不具备数据可读的权限。

此位复位默认值为0。

  • 如果此位为1,则表示此表项设定的地址区间具备数据可写的权限。

  • 如果此为为0,则表示此表项设定的地址区间不具备数据可写的权限。

此位复位默认值为0。

  • 如果此位为1,则表示此表项设定的地址区间具备代码可执行的权限。

  • 如果此为为0,则表示此表项设定的地址区间不具备代码可执行的权限。

此位复位默认值为0。

  • 如果此位为1,则该表项被锁定

  • 如果此为为0,则该表项没有被锁定

请参见第8.3节了解表项锁定的详情。

此位复位默认值为0。

每个表项的权限配置寄存器pmp\<x>cfg中的“A”比特域(第4\~3位)用于控制该表项的地址区间模式,其编码和模式的含义如表 8‑3所示。

表 8‑3 寄存器pmp\cfg中的“A”比特域

编码 模式名称 英文全称 中文解释
0 OFF Null region (disabled) 空模式,即:没有配置此表项
1 注意:N级别处理器内核不支持此编码的模式,如果向此配置域写入1,硬件上得到的结果仍然是0
2 NA4 Naturally aligned four-byte region 4个字节大小的区域(地址对齐),参见表 8‑5了解其详情。
3 NAPOT Naturally aligned power-of-two region (>= 8 bytes) 2的幂次方大小的区域(区域大小起码大于等于8个字节,并且地址对齐),参见表 8‑5了解其详情。

8.2.2. pmpaddr\<x>寄存器

pmpaddr\<x>寄存器用于指定PMP每个表项的地址配置寄存器。每个pmpaddr\<x>寄存器的格式如表 8‑4中所示。

表 8‑4 pmpaddr\中各控制域

描述
ADDR 29:0 配置地址和地址模式,参见表 8‑5了解其详情。
Reserved 31:30 未使用的域为常数0

PMP每个表项设定的地址区间大小需要由其对应的地址配置寄存器pmpaddr\<x>的“ADDR”比特域和权限配置寄存器pmp\<x>cfg的“A”比特域联合设置,如表 8‑5中所示。

表 8‑5 PMP表项设定的地址区间大小

表项地址寄存器pmpaddr\<x>的“ADDR”比特域设置的值 表项权限寄存器pmp\<x>cfg的“A”比特域设置的模式 表项设定的地址区间大小
aa….aaaa NA4 4个字节的区域(地址对齐),该区域的基地址为“aa…aaaa00”
aa….aaa0 NAPOT “2的3次方(8)”个字节的区域(地址对齐),该区域的基地址为“aa…aaa000”
aa….aa01 NAPOT “2的4次方(16)”个字节的区域(地址对齐),该区域的基地址为“aa…aa0000”
aa….a011 NAPOT “2的5次方(32)”个字节的区域(地址对齐),该区域的基地址为“aa…a00000”
………… ………… …………
01….1111 NAPOT “2的32次方(4G)”个字节的区域(地址对齐)。由于N级别处理器内核的地址空间为32位,则意味着整个32位地址空间都被覆盖。
注意:上表中的字母a表示任意值。

8.3. PMP表项的锁定

pmp\<x>cfg寄存器的“L”比特域(第7位)用于控制是否将PMP表项\<x>进行锁定,其要点如下:

  • 如果pmp\<x>cfg寄存器的“L”比特域(第7位)被写为1,则表项\<x>被锁定。

  • 当表项\<x>被锁定后,则表项\<x>对应的权限配置寄存器pmp\<x>cfg和地址配置寄存器pmpaddr\<x>将不能够再被改写。

  • 当表项\<x>被锁定后,由于对应的权限配置寄存器pmp\<x>cfg不能够再次被改写,因此软件无法通过清零“L”比特域进行解锁。唯一解锁PMP表项的方法便是整个处理器内核的复位。

  • 当表项\<x>没有锁定时,该表项设定的权限和地址区间保护仅对User Mode下的操作进行保护;当表项\<x>被锁定后,该表项设定的权限和地址区间保护同样适用于Machine Mode下的操作。

8.4. PMP保护触发异常

任何存储器访问操作都需要进行PMP权限检测,PMP权限检测原则请参见第8.7节。

如果访问存储器操作违反了PMP设定的权限,则处理器会触发异常,PMP触发的异常种类根据情形分为:

  • 如果是取指令操作造成的异常为“Instruction access fault”。

  • 如果是Load指令(数据存储器读操作)造成的异常为“Load access fault”。

  • 如果是Store指令(数据存储器写操作)或者AMO(原子操作指令)造成的异常为“Store/AMO access fault”。

有关“异常”的详情,请参见第3章。

8.5. PMP表项匹配的优先级

由于N级别处理器内核支持多个PMP表项,因此当一个操作同时匹配多个表项时,则编号数目小的表项具有更高的优先级。

8.6. PMP表项匹配的跨边界情况

由于N级别处理器PMP的pmp\<x>cfg中 “A”比特域能够设置的地址模式都是2的幂次方(或者4个字节)对齐的地址区间,因此,如非对齐的存储器读写访问可能跨越了一个PMP设定的地址区间边界,其处理方式如下:

  • 如果是普通数据存储器读写操作(Load、Store指令):

    • 如果N级别处理器内核被配置为硬件不支持地址非对齐的数据存储器读写操作,那么当访问地址非对齐时会产生此“地址非对齐”异常,从而没有机会产生PMP触发的异常。

    • 如果N级别处理器内核被配置为硬件支持地址非对齐的数据存储器读写操作,那么当访问地址非对齐时,硬件会将原本的操作拆分为逐个字节的访问操作,譬如,一个“半字(half-word)”的非对齐操作会被拆分为2个“字节(Byte)”的操作。那么,对于每一个“字节”的操作,会单独进行PMP检查,如果违反了PMP设定的权限,就会触发异常。注意:此类异常属于非精确异常。

  • 如果是AMO数据存储器读写操作(AMO指令):

    • RISC-V架构规定AMO指令不支持地址非对齐的数据存储器读写操作,因此当访问地址非对齐时会产生此“地址非对齐”异常,从而没有机会产生PMP触发的异常。
  • 如果是取指令操作,可能一条32位宽的指令正好位于一个非对齐的边界。由于N级别处理器内核总是以32位对齐的方式进行指令的预取,即,一个非对齐的32位指令预取会被拆分成为两个32位对齐的读操作,因此,这两个独立的读操作(32位对齐)会分别进行PMP的权限检测,只要任何一部分违反了PMP设定的权限,都会触发异常。

8.7. PMP权限检测的原则

PMP权限检测的原则和要点如下:

  • 如果当前的操作处于Machine Mode下,则:

    • 如果当前操作没有匹配到任何PMP表项,则该操作不会触发异常。

    • 如果当前的操作匹配到了PMP表项:

      • 首先判断mstatus.MPRV域的值是否为1:

        • 如果mstatus.MPRV域的值为1,并且mstatus.MPP域的值表示User Mode,则PMP用User Mode下的权限检测规则对存储器的数据读写(Load和Store)操作做权限检测,并根据表项配置的R/W属性进行权限确认,如果违反了权限,则触发异常。
      • 否则继续判断表项是否被锁定:

        • 如果表项没有被锁定,则意味着权限检测不适用于Machine Mode,无需对当前操作进行权限检测。

        • 如果表项被锁定,则意味着权限检测同样适用于Machine Mode,需根据表项配置的X/R/W属性进行权限确认,如果违反了权限,则触发异常。

  • 如果当前的操作处于User Mode下,则:

    • 如果当前操作没有匹配到任何PMP表项,则该操作一定会触发异常。这意味着在User Mode下,如果PMP没有配置合适的表项,任何访问操作都不能够成功。

    • 如果当前的操作匹配到了PMP表项,则需根据表项配置的X/R/W属性进行权限确认,如果违反了权限,则触发异常。

9. 内核低功耗机制介绍

N级别处理器内核可以支持休眠模式实现较低的静态功耗。

9.1. 进入休眠状态

N级别处理器内核可以通过WFI指令进入休眠状态。当处理器执行到WFI指令之后,将会:

  • 立即停止执行当前的指令流;

  • 等待处理器内核完成任何尚未完成的滞外操作(Outstanding Transactions),譬如取指令和数据读写操作,以保证发到总线上的操作都完成;

    • 注意:如果在等待总线上的操作完成的过程中发生了存储器访问错误异常,则会进入到异常处理模式,而不会休眠。
  • 当所有的滞外操作(Outstanding Transactions)都完成后,处理器会安全地进入一种空闲状态,这种空闲状态可以被称之为“休眠”状态。

  • 当进入休眠模式后:

    • N级别处理器内核内部的各个主要单元的时钟将会被门控关闭以节省静态功耗;

    • N级别处理器内核的输出信号core_wfi_mode会拉高,指示此处理器核处于执行WFI指令之后的休眠状态;

    • N级别处理器内核的输出信号core_sleep_value会输出CSR寄存器sleepvalue的值(注意:该信号只有在core_wfi_mode信号为高电平时生效;core_wfi_mode信号为低电平时core_sleep_value的值一定是0)。软件可以通过事先配置CSR寄存器sleepvalue来指示不同的休眠模式(0或者1)。注意:

      • 对于不同的休眠模式而言,N级别处理器内核的行为完全一样。此休眠模式只是仅供SoC系统层面的PMU(Power Management Unit)进行相应不同的控制。

9.2. 退出休眠状态

N级别处理器内核处理器退出休眠模式的要点如下:

  • N级别处理器内核的输出信号core_wfi_mode会相应拉低。

  • N级别处理器内核处理器可以通过以下四种方式被唤醒:

    • NMI

    • 中断

    • Event

    • Debug请求

下文将予以详细介绍 。

9.2.1. NMI唤醒

NMI总能够唤醒处理器内核。当处理器内核检测到输入信号nmi的上升沿,处理器内核被唤醒,进入到NMI服务程序开始执行。

9.2.2. 中断唤醒

中断也可以唤醒处理器内核:

  • 如果CSR寄存器wfe.WFE域被配置为0,则:

    • 如果mstatus.MIE域被配置为1(表示全局中断被打开),则:

      • 当ECLIC(通过将外部请求的中断进行仲裁)向处理器内核发送了中断,处理器内核被唤醒,进入到中断服务程序开始执行。
    • 如果mstatus.MIE域被配置为0(表示全局中断被关闭),则:

      • 当ECLIC(通过将外部请求的中断进行仲裁)向处理器内核发送了中断,处理器内核被唤醒,继续顺序执行之前停止的指令流(而不是进入到中断服务程序)。
  • 如果CSR寄存器wfe.WFE域被配置为1,则等待Event唤醒,请参见下节描述。

9.2.3. Event唤醒

当满足如下条件时,Event可以唤醒处理器内核:

  • 如果CSR寄存器wfe.WFE域被配置为1,则:

    • 当处理器内核检测到输入信号rx_evt(称之为Event信号)为高电平时,处理器内核被唤醒,继续执行之前停止的指令流(而不是进入到中断服务程序)。

9.2.4. Debug唤醒

Debug请求总能够唤醒处理器内核,如果调试器(Debugger)接入,也会将处理器内核唤醒而进入调试模式。

9.3. Wait for Interrupt机制

Wait for Interrupt机制,是指将处理器内核进入休眠模式,然后等待中断唤醒处理器内核,醒来后进入相应中断的处理函数中去。

如第9.1节和第9.2节所述,Wait for Interrupt机制可以直接通过WFI指令(配合mstatus.MIE域被配置为1)完成。

9.4. Wait for Event机制

Wait for Event机制,是指将处理器内核进入休眠模式,然后等待Event唤醒处理器内核,醒来后继续先前停止的程序(而不是进入中断的处理函数中去)。

如第9.1节和第9.2节所述,Wait for Event机制可以直接通过WFI指令,配合如下指令序列完成:

第1步:配置wfe.WFE域为1

第2步:调用WFI指令。调用此指令后处理器会进入休眠模式,当Event或者NMI将其唤醒后将会继续向下执行。

第3步:恢复wfe.WFE域为0