See MIPS Run Linux

整数乘除操作从不产生异常:即使除以 0 也不会发生异常(当然这样得到的结果是不确定的)。编译器常常会产生额外的指令检查和捕获错误,特别是零除的错误。

通用寄存器的习惯命名和用法:

寄存器编号 助记符 用法
1 at (assembly temporary 汇编暂存) 保留给汇编器使用
26, 27 k0, k1 保留给中断或自陷的处理程序使用;其值可能在你眼皮底下改变
28 gp (global pointer 全局指针);一些运行系统维护这个指针以便存取 staticextern 变量

有些 C 程序包含了许多对 staticextern 变量的引用,对它们加载 / 存储用两条指令开销太大,因此一些编译系统在编译 / 汇编时选择一些变量,并将它们一起存放到最后大小在 64k 字节以内的一个内存区。然后运行时系统初始化一个寄存器——习惯上用 $28 也就是 gp ——来指向该内存区的中间位置。

k0k1 (通用寄存器 $26-27)是两个(由软件约定)预留下来的用于异常处理代码中的通用寄存器。预留至少一个是必要的;而具体预留哪一个是随意的,但预留的寄存器必须是被现存的 MIPS 工具包和二进制程序中都接受的。

可能出现的异常类型:

  • add, sub, addi 溢出
  • sw, sh, lw, lh 地址对齐错误
  • im, dm 地址访问超范围
  • j/b 类指令跳转有误

第 3 章 协处理器0:MIPS 处理器控制

协处理器 CP0 的功能

  • CPU 配置:选择 CPU 的主要特性,或改变系统接口的工作方式
  • 高速缓存控制:CP0 的 cache 指令用来——以多种不同方式——操纵高速缓存的数据块。
  • 异常/中断控制:CP0 控制寄存器和几条特殊指令定义和控制了在异常或者中断发生时的行为以及怎样处理。
  • 存储管理单元控制
  • 杂项

MIPS CPU 控制寄存器

寄存器助记符 CP0 寄存器编号 描述
SR 12 状态寄存器(Status Register),一反常规的基板上由可写的控制位域组成。包括确定 CPU 特权等级、哪些中断引脚势能和其它 CPU 模式等位域。
Cause 13 什么原因导致异常或者中断?
EPC 14 异常程序计数器(Exception Program Counter): 异常 / 中断结束后从哪里重新开始执行
Count 9 > 这两个寄存器一起形成了一个简单而有用的高分辨率定时器
Compare 11 > ,频率(通常)为 CPU 流水线频率的一半
BadVaddr 8 导致最近的地址相关异常的程序地址。即使没有 MMU,各种地址错误也都会设置它。
Conext 4 > 对存储器管理和地址转换硬件 (TLB) 编程的寄存器
EntryHi 10 >
EntryLo0-1 2-3 >
Index 0 >
PageMask 5 >
Random 1 >
Wired 6 >
PRId 15 CPU 类型和版本号。类型号由 MIPS 公司管理
Config 16 > CPU 参数设置,通常由系统决定:一次额可写,另一些只读。有些 CPU 由高编号的专用寄存器。
Config1-3 16.1-3 >
Ebase 15.1 异常入口点基址和——多 CPU 系统的—— CPUID
IntCtl 12.1 设置中断向量和中断优先级
SPSCtl 12.2 > 影子寄存器控制
SRSMap 12.3 > 当 CPU 使用 “向量化中断“特性时,八个自己存其编号到八个可能中断原因之间的映射
CacheERR 27 > 分析(甚至可能恢复)内存错误的域,适用于在数据通路上使用错误校验码的 CPU
ECC 26 >
ErrorEPC 30 >
TagLo 28.0 > 用于高速缓存控制的寄存器
DataLo 28.1 >
TagHi 29.0 >
DataHi 29.1 >
Debug 23.0 > 用于 EJTAG 调试单元的寄存器
DEPC 24.0 >
DESAVE 31.0 >
WatchLo 18.0 数据观察点工具,当 CPU 试图在该地址存取书籍时会发生异常——对调试由潜在的用途
WatchHi 19.0  
PerfCtl 25.0 性能计数寄存器(后续的奇偶号可以选择更多的控制/计数寄存器对)
PerfCnt 25.1  
LLAddr 17.0 有些 CPU (主要是那些带有一致性缓存或者多哦线程扩展的 CPU) 存放一个 ll(load-linked) 指令相关的地址;地址存入后是可见的,虽然只有诊断软件会去读它
HWREna 7.0 一个可写的位图,决定哪些硬件寄存器可以被用户态程序访问。

CPU 控制指令

mtc0

mtc0 s, <n> # 把 GPR<s> 的数据传送到协处理器0 寄存器 n

mfc0

mfc0 d, <n> # 从协处理器0 取出数据,并装入 GPR<d>

eret

eret # to return from interrupt, exception, or error trap

什么时候要用到哪些寄存器?

处理任意异常:

  • 任何异常都调用一个固定入口地址的公共的”通用异常处理程序
  • 在异常入口处,不保存任何程序寄存器,只有返回地址被保存在 EPC
  • 你需要用 k0k1 中一个来指向为异常处理程序预留的一些内存空间。
  • 通过查看 Cause 寄存器可以找出发生的是哪种类型的异常并作出相应处理。

从异常返回:

  • 控制最终必须返回到在异常入口处保存到 EPC 中的地址。
  • 不管是什么异常,返回是都要把 SR 寄存器调整回原来的值、恢复用户态特权、允许中断以及消除异常的一般影响。
  • 最后,异常返回指令 eret 合并完成了返回用户空间和复位 SR(EXL)的功能。

中断:

  • SR 用来调整中断掩码,决定哪些(如果有的话)中断被赋予比当前更高的优先级。硬件没有提供中断优先级,但是软件可以随意。

纯粹为了引发异常的指令:

  • 这些指令常用于(系统调用、断点调试以及某些指令仿真等)。
  • syscall, break 指令

CPU 控制寄存器及其编码

状态寄存器 (SR)

image-20201222101737808

CPU3-0

  • CU3-0 每个分别代表协处理器 3-0 的 “协处理器使能”。协处理器 1 是浮点处理单元 FPU —— 设置为1 代表有浮点处理器并且使用,设置为 0 则代表进制 FPU。
  • 当设置为 0 的时候,所有 FPU 指令都会导致异常
  • 没有浮点硬件时把它设为 1 显然不行,但又浮点硬件时(设为 0 )关掉浮点部件可能回有用

RP(减小功耗)

  • RP(减小功耗 reduced power) ——具体怎样做取决于 CPU 。 比如,可以降低 CPU 的运行频率、电压或者同时降低两者。

FR(模式切换)

  • 设为 1 则全部 32 个双倍宽度的浮点寄存器软件都可见
  • 设为 0 则让它们模拟 MIPS I 的成对 32 位浮点寄存器的行为

RE(反转用户态下的尾端设置)

  • MIPS 处理器可以在复位时配置任何一种尾端【大尾端 / 小尾端】

MX

  • 使能 DSP 或者 MDMX ASE(指令集)——同一个 CPU 不可能两者都有。

    DSP(Digital Signal Processing) 数字信号处理是将信号以数字方式表示并处理的理论和技术。

PX

  • 参见下面对 SR(UX) 的描述

BEV

  • 启动时异常向量:
    • 当 BEV == 1 时,CPU 采用 ROM(kseg1) 空间的异常入口点。
    • 运行的操作系统里,BEV 通常设置为 0

TS

  • TLB 关闭

    TLB(Translation Lookaside Buffer) 转换检测缓冲区是一个内存管理单元,用于改进虚拟地址到物理地址转换速度的缓存。

    TLB 是一个小弟、虚拟寻址的缓存,其中每一行都保存着一个由单个 PTE(Page Table Entry, 页表项)组成的块,如果没有 TLB,则每次取数据都需要两次访问内存,即查页表获得物理地址和取数据。

    TLB 关闭是一个不可逆的过程,一旦关闭,只有硬件复位才能清除。

SR, NMI

  • 软复位或者不可屏蔽的中断发生了

    • 硬件复位:

      • 通过硬件给系统一个复位,比如在电路板上设计一个复位电路,通过按下按键就可以给系统实现一个复位,无论系统在执行什么样的程序

      • 复位启动需要重新加载 FPGA,DSP 等,也有可能在这个操作之前初始化 CPU,加载系统文件等操作,具体视需求而定。

      • 硬件复位的作用区域一般是全局的。

    • 软件复位:

      • 是通过软件给系统一个复位信号,如低电平或是高电平
      • 复位启动不需要进行 FPGA、DSP 等的加载,知识配置芯片的初始化
      • 软件复位一般是一些块结构
    • 上电复位:

      • 系统在上电的瞬间就执行复位操作,上电复位里面包括硬件复位和软件复位操作。
      • 复位需要初始化 CPU 系统,包括 CPU 和内存等,并加载系统,加载初始化操作系统以及 FPGA、配置芯片的初始化
  • MIPS CPU 配置寄存器 Config 在软复位过程中保持不变,但是在复位后必须重新编程。

  • MIPS 的复位有点类似异常——当然软复位或者硬复位是 CPU 永远不会返回的异常——复位复用了很多异常处理机制

  • SR(SR) 域在硬件复位(所有参数都要重新载入)后清零,而在软件复位或 NMI 后置位

  • SR(NMI) 位只在 NMI 异常之后才置位

    NMI(Non Maskable Interrupt) 不可屏蔽中断

    不可屏蔽中断请求信号NMI用来通知CPU,发生了”灾难性”的事件,如电源掉电、存储器读写出错、总线奇偶位出错等。NMI线上中断请求是不可屏蔽的(即无法禁止的)、而且立即被CPU锁存。因此NMI是边沿触发,不需要电平触发。NMI的优先级也比INTR高。不可屏蔽中断的类型指定为2,在CPU响应NMI时,不必由中断源提供中断类型码,因此NMI响应也不需要执行总线周期INTA

IM7-0 中断屏蔽

  • 一个 8 位的域定义允许哪些中断源活动时产生异常。其中六个源由 CPU 核外部的信号产生(一个可能由 FPU 使用),其余两个是 Cause 寄存器中软件可写的中断位。

UX, SX, KX

  • 三个不同的(用户、管理、核心)特权各自有不同的位。当相应位置位后,最为常见的内存地址转换异常(TLB 未命中)被重定向到不同的入口点,那里软件将处理 64 位地址
  • 当 SR(UX) 为 0 时 CPU 将不再用户态下运行 MIPS64 ISA 中的 64 位指令。
  • 如果你想在用户态用 64 位指令但是让然坚持 32 位寻址,这时你可能要设置 SR(PX)

KSU CPU 特权级

  • 0——核心级
  • 1——管理级
  • 2——用户级
  • 如果紧跟异常之后 EXL 或者 ERL 被置位,不管这个域是什么值 CPU 都会自动处于核心态。

ERL 错误级

  • 在 CPU 收到错误的数据时该位置位。
  • MIPS CPU 可以选择对从高速缓存或者内存收到的数据块进行额外的奇偶校验位或者 ECC(纠错码)位检查。
    • 奇偶校验错一般都是致命错(除非已知有良好的数据可供替换)
    • ECC 的错误只要错误位不超过 1 位(或两位)则可以通过软件纠正
  • 当这种错误出现的时候,CPU 收到一个奇偶检验 / ECC 错误异常,并且该位置位。对这个异常的处理和标准异常是分开的,因为可纠正的 ECC 错误可能发生在任何地方——甚至发生在常规异常例程中最为敏感的部分。
  • 一旦置位,就会深度影响处理器的行为;对正常的用户空间转换的地址的所有访问都将消失,从 00x7FFF.FFFF 的程序地址变成了一个映射到相同物理地址不做高速缓存的窗口。目的是让高速缓存错误异常处理程序可以用 $zero 寄存器来做基址,用基址 + 偏移量来访问某些(由操作系统为此预留的)不经过高速缓存的内存空间,以便保存寄存器,并给自己腾出空间来运行。

EXL 异常级:

  • 任何异常发生时置位。这会强行进入核心态并禁止中断;目的是把 EXL 位维持足够长的时间以便软件决定新的 CPU 特权级和中断屏蔽该设成什么

IE 全局的中断使能位:

  • 不管这位是什么值,EXL 或 ERL 总是禁止所有的中断。

原因寄存器(Cause)

image-20201222152044095

BD 分支延迟:

  • EPC 保存的是异常处理完之后的返回地址。正常情况下,这也指向异常受害指令。
  • 但是如果发生异常的指令是在一条转移指令的延迟操里,EPC 得指向那条转移指令;重新执行转移指令没有什么害处,但如果返回到延迟槽指令本身,转移就不会发生,从而这个异常将破坏被中断的程序
  • 只要异常发生在延迟槽的指令,Cause(BD) 就会置位,EPC 就指向分支指令。即若 Cause(BD) == 1,那么该指令位于 EPC + 4

TI (仅适用于新的 MIPS32/64 CPU)

  • 该异常由内部定时器中断产生

CE 协处理器错误:

  • 如果异常是由于相应的 SR(CUx) 位没有使能某个协处理器格式的指令引起的,那么 Cause(CE) 保存这条指令的协处理器编号。

DC (仅适用于新的 MIPS 32/64 CPU)

  • 把该位写入 1 可以停止 Count 寄存器计数

PCI (仅适用于新的 MIPS 32/64 CPU)

  • CP0 性能计数器溢出而产生此中断

IV

  • 该位写入 1 表示中断将使用一个特殊的异常入口点

WP

  • 读出为 1 表示当 CPU 已经处于异常模式时(此时观察点异常被抑制)