【原创】COLDFIRE处理器的异常处理
0赞异常处理即指的是运算器在产生错误是的处理。ColdFire 系列处理器的异常处理在性能上做了诸多的改进,与早先的M68000 系列处理器相比增加以下功能:
⑴ 有一个简单的异常向量表。
⑵ 具有使用向量基址寄存器简化了的定位能力。
⑶ 具有简单的堆栈帧格式。
⑷ 使用了简单的有自动矫正功能的堆栈指针(这适用于对 ISA_A 的实现)。
从 V4 版本的内核开始,各个版本便对可选的虚拟内存管理单元提供了支持。对于 那些具有 MMU 的芯片,异常处理被做了一些改进。不同于早先的 ColdFire 系列处理 器,它还与译码故障(如 TLB 故障)和存取故障的指令重启模式息息相关。这扩充了先 前的 ColdFire 系列芯片的错误故障向量和异常堆栈结构。早先的 ColdFire 系列处理器(V2 和 V3 版)使用了一个指令重启的异常模式,但它需要额外的软件支持才能从特定的存取错误中恢复过来。
异常处理就是从检测到错误条件到第一个处理指令被调用的过程。它包括以下四个主要步骤:
⑴ 处理器执行一个状态寄存器(SR)的内部复制,然后通过设置 SR[S]进入管理 方式模式,通过清除 SR[T]进入禁止跟踪模式。中断异常的发生也会清除 SR[M]和设置 中断优先级字、SR[I]以符合当前的中断请求级别。
⑵ 处理器确定异常向量号。除了中断以外的所有错误,处理器都要考虑异常类型。而对于中断,处理器执行一个中断确认(IACK)总线周期,从外围设备获得向量号。 中断确认周期被映射到一个特殊的确认地址空间并与地址中的中断级别编码对应。
⑶ 处理器在系统堆栈中创建一个异常堆栈结构来保存当前内容。实现 ISA_A 的处 理器支持单一堆栈指针 A7,所以没有区分管理员和普通用户堆栈指针的概念,于是,异常堆栈结构被创建在当前系统栈顶部的以 0-4 为模的地址中。实现其他 ISA 的处理器 支持两个堆栈指针,异常堆栈被创建在当前系统堆栈顶部的以 0-4 为模的管理堆栈指针(SSP)指向的地方。所有的 ColdFire 处理器适用简化的固定长度的堆栈设计,如图 11-1, 适用于所有的异常。另外,处理器内核支持 MMU 使用相同的固定长度的堆栈设计,并使用异常状态(FS)编码来支持 MMU。某些异常类型中,异常堆栈结构中的程序计数器包含了错误指令的地址;而另一些异常类型中,它包含了下一条即将执行的指令的地 址。如果异常是由 FPU 指令引起的,且异常是预编译指令时,程序计数器包括下一条 浮点指令的地址,或者当异常是已处理的指令时,程序计数器包括下条要执行的指令的地址。
⑷ 处理器通过取得向量基址寄存器中的异常表中的一个值来取得异常处理的第一 条指令地址。表中的索引计算方法是 4*向量号。索引值一旦产生,就可以通过向量表 的内容确定需要的第一条指令的地址。初始化取得的第一条操作码后,异常处理结束, 管理者将能继续进行正常的指令处理。
向量基址寄存器(VBR)”异常向量表的基 址保存在存储器中。通过异常向量的偏移量与基址寄存器的值相加来访问向量表。 VBR[19-0]没有被执行并且假定为零,强制使向量表以 1Mb 为边界排成列。
ColdFire 处理器支持 1024 字节向量表,如表1 所示。表中包含 256 个异常向量,前 64 个向量是由 Motorola 定义的,其他是用户定义的中断向量。
表1异常向量分配
向量号 |
向量偏移(十六进制) |
栈程序计数器 1 |
分配 |
0 |
000 |
— |
初始化堆栈指针 (SSP 为主要的双向堆栈指 针) |
1 |
004 |
— |
初始化程序计数器 |
2 |
008 |
Fault |
访问出错 |
3 |
00C |
Fault |
地址错误 |
4 |
010 |
Fault |
非法指令 |
52 |
014 |
Fault |
0 作除数 |
6–7 |
018–01C |
— |
保留 |
8 |
020 |
Fault |
特例 |
9 |
024 |
Next |
描述 |
10 |
028 |
Fault |
无效的 a 行操作码 |
11 |
02C |
Fault |
无效的 f 行操作码 |
123 |
030 |
Next |
非 PC 断点编译中断 |
133 |
034 |
Next |
PC 断点编译中断 |
14 |
038 |
Fault |
尺度误差 |
15 |
03C |
Next |
为初始化的中断 |
16–23 |
040–05C |
— |
保留 |
24 |
060 |
Next |
假中断 |
25–314 |
064–07C |
Next |
Level 1–7 外向量中断 |
32–47 |
080–0BC |
Next |
Trap #0–15 指令 |
485 |
0C0 |
Fault tt |
浮点无序接通条件 |
495 |
0C4 |
NextFP or Fault |
浮点不精确结果 |
505 |
0C8 |
NextFP |
浮点除以零 |
515 |
0CC |
NextFP or Fault |
浮点溢出 |
525 |
0D0 |
NextFP or Fault |
浮点操作数出错 |
535 |
0D4 |
NextFP or Fault |
浮点溢出 |
545 |
0D8 |
NextFP or Fault |
浮点输入不是数字 |
555 |
0DC |
NextFP or Fault |
浮点输入非规格化数字 |
56–60 |
0E0–0F0 |
— |
保留 |
616 |
0F4 |
Fault |
不支持指令 |
62–63 |
0F8–0FC |
— |
保留 |
64–255 |
100–3FC |
Next |
用户定义指令 |
1Fault 指的是错误指令的 PC。Next 指的是错误指令后的指令的 PC。NextFP 指的是下条浮点指 令的 PC。
2 如果除法单元不使用(5202,5204,5206),向量 5 被保留。
3 在 V2 和 V3 中, 所有的调试中断使用向量 12,向量 13 被保留。
4 对外向量中断的支持依赖于中断控制器的执行。另外的详细描述参考特殊的芯片参考手册。
5 如果 FPU 不是能使用,向量 48-55 被保留。
6 有些芯片不支持异常处理;参考表 11-3。
ColdFire 处理器禁止在异常操作的第一条指令中发生中断。允许任何操作有效的禁止中断,如果必要可以提高 SR 中的中断级别。
1.1管理员/用户堆栈指针(A7和OTHER_A7)
一些 ColdFire 系列支持两个独立的栈指针(A7)寄存器:管理员堆栈指针(SSP) 和用户栈指针(USP)。这一支持在运行模式间提供了必要的独立。注意只有 SSP 是用 于创建异常堆栈框架的。这两个程序可见的 32 位寄存器的硬件执行不是唯一鉴别 SSP 和 USP 的标志。硬件 用一个 32 位寄存器作为当前活动的 A7 而另一个作为 OTHER_A7。因此,寄存器内容是处理器执行模式的函数:
if SR[S] = 1
then
A7=Supervisor Stack Pointer Other_A7=User Stack Pointer else
A7=User Stack Pointer
Other_A7=Supervisor Stack Pointer
BDM 编程模式支持直接对 A7 和 OTHER_A7 读写。外设的任务是基于设置 SR[S] 并决定将 A7 和 OTHER_A7 映射到两个程序可见的堆栈(SSP 和 USP)中。这个功能通过设定双栈指针使能位,CACR[DSPE]才能有效。如果该位被清零,只有 A7(用于 早先的 ColdFire 版本)可用。DSPE 在复位时为零。
如果 DSPE 被设置了,相应的堆栈指针寄存器(SSP 或者 USP)将被作为处理器运 行模式的函数来访问。为了支持双栈指针,以下两个特殊的用于装载/存储 USP 的 MC680x0 指令作为 ISA_B 的一部分被加载到 ColdFire 指令集体系中:
mov.l Ay,USP # move to USP: opcode = 0x4E6(0xxx)
mov.l USP,Ax # move from USP: opcode = 0x4E6(1xxx)
地址寄存器号编码到操作码的低三位。
1.2异常栈框架定义
异常栈框架中的第一个长字,用于保存 16 位格式/向量字(F/V)和 16位状态寄存器。第二个用于保存 32 位程序计数器的地址。
图1.堆栈异常
表 -2 描述 F/V 域。FS 编码用来支持 MMU。
虽然上面提到的是关于调试服务中的 I/O 中断,但是这也适用于其他类型的错误。如果在调试服务中发生了存取错误,当在指令存取时 FS 被设置成 0111,在数据存取时 FS 被设置成 1111。这只适 用于MMU 的存取错误。如果不是 MMU 的存取错误,FS 被设置成 0010。
1.3处理器异常
表 -3 描述了 ColdFire 内核异常。注意当处理其他错误时,要注意的是:如果当 ColdFire 处理器正在处理异常,同时又出现其他错误,它将作为一个致命错误来处理并立即停止执行。若想退出停止状态,那必须复位。
1.4浮点算法异常
本节描述浮点算法异常。表 -3 按优先级顺序列出了这些异常:
表3异常优先级
优先级 |
异常 |
1 |
分支/开始无序(BSUN) |
2 |
输入非数字(INAN) |
3 |
输入规格化数字(IDE) |
4 |
操作数错误(OPERR) |
5 |
溢出 (OVFL) |
6 |
下溢(UNFL) |
7 |
除数为零(DZ) |
8 |
不精确 (INEX) |
当遇到下个浮点算法指令时,往往会发生浮点异常(这就是预指令异常)。当存储一个浮点数到存储器或者到一个整数寄存器时会立即发生异常(这是已处理指令异常)。 注意因为结果是全面的,FMOVE 被认为是个算法指令。只有 FMOVE 的操作目的地不是浮点寄存器时(否则称为 FMOVE 溢出)能产生已处理指令异常。已处理指令异 常从不写目的地址。已处理指令异常发生后,继续处理下一条指令。当浮点指令结果设置了 FPSR[EXC]位和相应的 FPCR[ENABLE]位被设置后,浮点运算异常变成了未定的。用户写 FPSR 和 FPCR 会导致 FPSR[EXC]的异常位连同相应 的 FPCR 的异常使能的重新设置,而 FPU 处于异常未定状态。在开始执行下一条算法 指令时会发生相应的预指令异常。执行多个指令会产生多重异常。当多重异常发生并激活多个异常类时,优先级最高的异常将被执行。这决定于异常管理者对多重异常的检查。下面是可能发生的多重异常:
• 操作数错误(OPERR) 和不精确结果(INEX)
• 溢出(OVFL)和不精确结果(INEX)
• 下溢(UNFL)和不精确结果(INEX)
• 除数为零(DZ)和不精确结果(INEX)
• 输入规格化数字(IDE)和不精确结果(INEX)
• 输入非数字(INAN) 和输入规格化数字(IDE)
一般来说,所有的异常都是相似的。如果异常条件存在,而异常不可捕捉,没有发生异常,错误的结果将被写到目的地址(除 BSUN 异常没有目的地址),然后正常执行。如果有效的异常发生,与以上讲到的相同的默认结果将被写到预指令异常中,但没有结果写到已处理指令异常中。我们期望异常处理将 FSAVE 作为第一条浮点指令。并且会清 FPCR,FPCR 用来阻 止异常处理期间发生异常。因为目的地址是写在浮点寄存器目的地址中的,原始的浮点 目的寄存器值对 FSAVE 状态的管理是可用的。指令地址引起的异常在 FPIAR 中是可用的。当异常处理完成时,需要清空 FSAVE 状态中适当的 FPSR 异常位,再执行FRESTORE。如果状态中的异常状态位没有被清空,相同的异常会再次发生。相比较而言,执行 FSAVE 异常处理可以简单的清除适当的 FPSR 异常位,任意改变 FPCR,并从异常中返回。注意异常从来不会随 FMOVE 发生在状态和控制寄存器中,也不会随 FMOVEM 发生在浮点数据寄存器中。在异常管理完成时,RTE 指令必须被执行返回到正常的指令流中。
1.5分支开始无序(BSUN)
当无序条件存在时,IEEE 无意识的条件性测试与 FBcc 指令的联合会产生 BSUN。 在条件指令重启后任何一个浮点异常首先都是被预指令处理的。在执行条件指令之前条 件谓词会被估计和检查。当条件谓词是 IEEE 无意识的条件分支并且 FPCC[NAN]被置 位时就会发生 BSUN 异常发生。一旦条件被检测到,FPSR[BSUN]就被置位。表 11-4 显示了异常有效或是无效时的结果。
表4 BSUN异常有效/无效结果
条 件 |
BSUN |
描 述 |
异常无效 |
0 |
浮点条件估计为 IEEE 条件谓词。不发生任何异常。 |
异常有效 |
1 |
处理器发生浮点预指令异常。 BSUN 异常是唯一的,因为在条件谓词被估计之前异常已经发生。 如果用户 BSUN 异常管理在返回时没能够在异常指令之后更新 PC 到指令中,异常将会再次被执行。下面行为能够防止异常被再 次执行: •清空 FPSR[NAN] •使 FPCR[BSUN]失效 •在栈中的条件指令旁增加存储 PC。用在可能出现错误的 地方。注意要得到精确的 PC 增量 得到条件指令的大小。 |
1.6输入非数字(INAN)
INAN 异常是处理用户定义的机制,它不是 IEEE 的数据类型。输入的操作数不是 数字时,FPSR[INAN]被置位。有了 INAN 异常,用户可以忽略操作数为非数字的错误。 由于 FMOVEM、FMOVE、FPCR 和 FSAVE 指令不会改变状态位,所以不会产生异常。所以这些指令在操作非数字类型时是有效的。见表 11-5。
表5 INAN异常有效/无效结果
异常有效 1 除了异常发生在 FMOVE OUT 模式下写到目的地址中的结果和异常无效是一样的,否则目的地址不受影响。
1.7输入规格化数字(IDE)
输入标准位 FPCR[IDE],为规格化操作数提供软件支持。当 IDE 异常无效时,操 作数被视为零,FPSR[INEX]被置位,操作成功。如果 IDE 异常有效且操作数是规格化 的,则发生 IDE 异常,但 FPSR[INEX]不置位以允许适当的置位处理。见表 11-6。注意 FPU 从来不产生规格化数字。如果必要,软件会在下溢异常处理中创建。
表6 IDE异常有效/无效结果
条 件 |
IDE |
描 述 |
异常无效 |
0 |
任何规格化操作数被看作为 0,FPSR[INEX]置位,操作成功。 |
异常有效 |
1 |
除了异常发生在 FMOVE OUT 模式下写到目的地址中的结果和 异常无效是一样的,否则目的地址不受影响。FPSR[INEX]不置位 以允许管理可作适当的设置。 |
1.8操作数错误(OPERR)
操作数错误异常涵盖各种操作引发的问题,包括过少的或是过多的特殊异常条件的错误。基本上,当对操作数的操作没有准确的解释时,操作数错误通常会发生。表 11-7 列出了操作数错误。异常发生时,FPSR[OPERR]置位。
表7可能的操作数错误
指令 |
引起操作数错误的条件 |
FADD |
[(+∞) + (-∞)] 或 [(-∞) + (+∞)] |
FDIV |
(0 ÷ 0) 或 (∞ ÷ ∞) |
FMOVE OUT(B、W 或者 L) |
整数溢出,源数据是非数字或者±∞ |
FMUL |
一个操作数是 0 另一个是±∞ |
FSQRT |
源是小于 0 的或者是-∞ |
FSUB |
[(+∞) - (+∞)] 或者 [(-∞) - (-∞)] |
表 -8 描述了异常有效和无效时发生异常的结果。
表-8 OPERR异常有效/无效结果
异常无效是一样的,否则目的地址不受影响。如果需要,用户
OPERR 处理可以写入默认结果。
1.9溢出(OVFL)
当中间结果大于或等于已选择舍入精度的最大指数值时,目的地址是浮点寄存器或 存储器中的算法操作能检测到溢出异常。溢出只在目的地址为 S 或者 D 精度格式时发 生,对于其他格式管理类似操作数错误。在任何操作结束时,都可能发生溢出,检查中 间 结 果 以 防 下 溢 , 然 后 在 存 储 到 目 的 地 址 之 前 检 查 溢 出 。 如 果 溢 出 发 生 FPSR[OVFL,INEX]置位。即使中间结果足够小可以被看成双精度数字,如果中间结果的数量级超出了所选舍入精度格式的范围也会发生溢出。见表9。
表9 OVFL异常有效/无效结果
条 件 |
OVFL |
描 述 |
异常无效 |
0 |
目的地址中的值是基于 FPCR[MODE]的舍入模式定义。 RN 无穷大,带中间结果标志 RZ L 大数量级数字,带中间结果标志。 RM 正溢出,最大正规格化数字。负溢出,- RPF 正溢出,+。负溢出,最大负规格化数字。 |
异常有效 |
1 |
除了异常发生在 FMOVE OUT 模式下写到目的地址中的结果和异 常无效是一样的,否则目的地址不受影响。如果需要,用户 OVFL 管理可以写默认结果。 |
1.10下溢(UNFL)
下溢异常发生是由于算法指令的中间结果太小而不能在浮点寄存器或存储器中用 已选择的舍入精度表示一个规格化数字。也就是说发生在中间结果指数小于或等于已选舍入精度的最小指数值的情况下。下溢只在目的地址格式为单精度或双精度时发生。若目的地址是一个字节,字或长字,则将下溢处理为0,从而不会引起下溢或操作数错误。 在任何操作结束时,都可能发生溢出,检查中间结果以防下溢,然后在存储到目的地址 之前检查溢出。如果溢出发生则 FPSR[UNFL]被置位。如果下溢无效,FPSR[INEX]被 置位。即使中间结果足够大,可以被看成双精度数字,若它的数量级太小超出了所选舍入精度格式的范围还是会发生下溢。见表 10 描述了异常有效或无效的结果。
表10 UNFL异常有效或无效结果
条 件 |
UNFL |
描 述 |
异常无效 |
0 |
存储结果定义如下。如果 UNFL 异常无效,UNFL 异常也会设置 FPSR[INEX] RN 0,带中间结果标志 RZ 0,带中间结果标志 RM 正下溢,+0。负溢出,最小负规格化数字 |
RP 正下溢,最小正规格化数字。负溢出,-0 |
||
异常有效 |
1 |
除了异常发生在 FMOVE OUT 模式下,写入到目的地址的结果与 异常无效的情况是相同的,否则目的地址不受影响。如果需要, 用户 UNFL 处理 可 以 写 默 认 结 果 。 UNFL 异 常 不 置 位 FPSR[INEX],如果 UNFL 异常有效,异常处理可以基于产生的结 果情况来置位 FPSR[INEX]。 |
1.11除数为零(DZ)
若除法指令使用 0 作为除数会产生除数为零的异常。当检测到此异常时,FPSR[DZ]被置位。表-11 显示了异常有效或无效结果。
表11 DZ异常有效或无效结果
条 件 |
DZ |
描 述 |
异常无效 |
0 |
目标浮点数据寄存器写入无穷大标志,这种标志是唯一的标志或 者是输入操作数的标志。 |
异常有效 |
1 |
目标浮点数据寄存器的写入与异常无效的情况相同。 |
1.12不精确结果(INEX)
当浮点类型中间结果的无穷精度尾数的关键位能在舍入精度或在目的格式中准确 的表示,INEX 异常就有可能发生。如果异常发生,FPSR[INEX]置位,无穷精度结果参 考表 12。
表12不精确舍入模式值
模式 |
结果 |
RN |
最接近无穷精度的中间值作为结果。 如果两个最接近的值几乎相等,lsb 为 0(偶数)的是结果。通常被称 为就近取偶(round-to-nearest-even). |
RZ |
结果是一些中间值,这些值在级数上最接近并且不大于无穷精度。 有时被称为截断模式(chop-mode), 因为结果将清空当前舍入点的位。 |
RM |
结果为最接近并且不大于无穷精度的中间值(可能为-x)。 |
RP |
结果为最接近并且不小于无穷精度的中间值(可能为+x)。 |
FPSR[INEX]在以下任何的条件下也会被置位:
• 如果输入操作数是规格化数字并且 IDE 异常失效。
• 溢出结果
• 下溢异常失效的下溢结果
表 13 显示了当异常有效或失效时的结果
表13 INEX异常有效或无效结果
条 件 |
INEX |
描 述 |
异常无效 |
0 |
结果是舍入的并且被写入目的地址 |
异常有效 |
1 |
除了异常发生在 FMOVE OUT 模式下,写入到目的地址的结果与 异常无效的情况是相同的,否则目的地址不受影响。如果需要, 用户 INEX 管理可以写默认结果。 |
1.13 MMU转变成异常处理模式
当 MMU 模块出现在 ColdFire 芯片中,所有的存储器相关部件都需要对可恢复错误(recoverable faults)提供支持。本节将详细描述 ColdFire 系列在加入 MMU 后的异常处 理模式的变化。ColdFire 指令重启机制保证了错误的指令从执行开始重启,也就是说异常发生时没 有内部状态信息被保存,异常处理结束时没有被恢复。通过转变控制到给定的位置作为 RTE 指令的一部分,处理器使程序执行复原,给出异常堆栈结构中的 PC 地址定义。如果指令发生后继错误,指令重启恢复模式需要程序可见寄存器改变执行到未完成模式。
对于 V4 及以上核心,大多数指令中操作数执行管道(OEP)结构自然支持这个概 念;只有在 OEP 末级也就是当错误的集合完成时,程序可见寄存器才会更新。任何类型的异常发生,未定的寄存器更新都会被放弃。大多数指令已经支持精度出错和指令重启。而一些复杂的指令不支持。思考以下存储器到存储器的移动:mov.l (Ay)+,(Ax)+ #从源地址复制四个字节数到目的地址 这条指令用了一周期去读源操作数(Ay)加 1 并将数据写到(Ax)。源和目的地址指针都被更新作为执行的一部分。表 14 列出了执行过程中的操作。
表14 OEP EX周期操作
EX 周期 |
操 作 |
1 |
从存储器@(Ay)读源操作数,更新 Ay,新 Ay = 旧 Ay + 4 |
2 |
写操作数到目的存储器@(Ax),更新 Ax,新 Ax =旧 Ax + 4,更新 CCR |
第二个周期中报告错误被检测到并且写到目的存储器中。此时,第一个周期的操作执行完成,所以如果目的写操作发生任何类型的访问错误,Ay 被更新。访问错误管理 执行完成后,错误指令重启,处理器操作是错误的因为源地址寄存器错误(已经存在的增量)的值。为了恢复所有指令的设计模式的初始状态,V4 及以上核心添加了必要的硬件来支持全寄存器恢复。硬件允许程序可见寄存器存储多周期指令的原始状态,所以可支持指令重启机制。存储器到存储器的移动和重载代表复杂指令需要的特殊恢复支持。