转自 : http://mips.eefocus.com/article/10-11/2467421291101225.html
异常向量:异常处理开始的地方
大多数CISC理器由硬件(或微代码)来分析异常,根据发生的异常类烈把CPU发送到不同的入口点。甚至连中断都根据哪个中断输入信号激活而在不同的入口点处理时,就叫做向量化中断(vectored interrupts)。历史上,MIPS CPU对此做得很少。如果觉得这像是一个严重的疏忽,请考虑下面的因素。首先,在实践中向量化中断并不像想象的那样有用。在大多数操作系统中,中断处理程序共享代码(为了节省寄存器等原因),常见的是CISC微代码辛辛苦苦花费时间将中断发送到不同的入口点,操作系统软件得到一个代号然后又花更多的时间跳转到公共的处理程序。
其次,很难想象如果不用微代码而完全由硬件能做多少异常分析;在RISC CPU上,选用通常的代码就已经足够快了。
在此处和别处,你应该对RISC这代CPU相对其外围设备有多快心里有数。一个有用的中断处理例程要读写一些外设寄存器,在21世纪初期的CPU ,一个外部总线周期可能要花费50—200个内部时钟周期。很容易在MIPS CPU上写出比单个外设访问快的中断处理代码,所以这不大可能成为一个性能瓶颈。2003年版的MIPS32标准中加入的向量化中断选项几乎没人用的事实,进一步证明了这一点。
然而,即使在MIPS中,也不是所有异常都是平等的。在体系结构发展过程中也出现了一些差别。所以我们可以做些区分。
用户态地址的TLB重填充:在受保护的操作系统中有个与地址转换系统相关的异常特别频繁。TLB硬件只能保存适度数最的地址转换,在一个运行着虚拟存储器的操作系统的负担很重的机器上,经常遇到应用程序运行到一个TLB未记录其转换的地址——个称为TLB未命中的事件(因为TLB被当作一个由软件管理的高速缓存)。
当RISC CPU刚刚推出的时候,用软件来处理这种情况引发了很多争议,MIPS CPU为一个选中的TLB重填方案提供了重要的支持。硬件提供了足够的帮助使得为选中的重填方案设计的异常处理程序能够在13个时钟周期完成。
作为该方案其中的一部分,常见的一类TLB重填被赋予了一个不同于其它异常的入口点,这样精心优化过的重填代码不用再花费时间判断到底发生的是哪种异常。
64位地址空间的TLB重填:想要利用64位CPU上巨大的地址空间的任务,其地址转换采用略有不同的寄存器布局和另外的TLB重填例程;MIPS称其为XTLB(我猜“X”代表扩展)。与上面一样,为了保证高效率用了一个独立的入口点。
不经过高速缓存的替代入口点:为了在异常处理时获得良好性能,中断入口点必须位于使用高速缓存的存储区。但是在系统启动的时候不希望这样。复位或者上电后,高速缓存在初始化之前不能使用。如果你需要一个健壮的和能够自我诊断的启动代码,对于引导早期检测到的异常,你只得用不经过高速缓存的只读存储器的入口点。在MIPS CPU里,没有不用高速缓存的“模式”― 只有不经过高速缓存的程序地址区域——所以有一个模式位SR ( BEV)用来把异常入口点另行分配到不用高速缓存的、启动安全的ksegl区。
奇偶/ECC出错:MIPS32 CPU可以检测一个数据错误(通常在来自主存的数据中,但是直到在高速缓存中用到才会发现)然后自陷。在高速缓存区来处理高速缓存的错误显然很蠢,所以不管SR (BEV)状态是什么,高速缓存出错异常的入口点总是位于不经过高速缓存的地址空间。
复位:对很多目的来说,把复位看作另外一种异常是有道理的,尤其是当许多CPU对于冷复位(此时CPU彻底重新配置;和重新上电不可区分)和热启动(此时软件彻底重新初始化)使用相同的入口点的时候.其实,不可屏蔽中断NMI的结果相当于热复位的稍弱的版本,区别仅在于它要等待当前指令和尚未完成的存取操作结束才生效。
中断:作为MIPS32(还有IDT PMC—Sierra的早期的一些CPU)的一个可选项,可以设置CPU把中断异常发送到单独的入口点。这很方便但很少人用:可能软件作者不能让自己为一个并不普遍的特性而让操作系统作为特例进行处理。
进一步,在其中某些CPU上,你可以使能向量化的中断操作----不同的中断使用多个不同的入口点。这是更具实质性的变化;在本章别的地方讲到过,MIPS的传统是中断只在软件处理时才有优先级。但当你有两个活动的中断而不得不选择一个中断入口点时,硬件必须决定到底哪个优先级更高。因而这个变化对软件的影响要大得多,因为软件丧失了对中断优先级的控制;操作系统维护人员和硬件工程师得紧密配合才行。
所有的异常入口点都位于MIPS存储器映像中不作地址转换的区域,不要高速缓存的入口点位于ksegl,需要高速缓存的位于kseg0。不要高速缓存的入口点当SR ( BEV)置位时是固定的,但是当SR ( BEV)清零时,就可以对EBase寄存器进行编程来平移所有入口点——一起----到别的内存块。当你的CPU是共享kseg0存储器的多处理器系统的一部分,但是想要和系统中其它CPU分开的单独异常入口点的时候,可以移动中断基址的能力就特别有用。
在这些地址区,下表给出的32位地址通过符号扩展到64位内存映像。在32位中的程序地址0x8000 0000和64位中的0xFFFF FFFF 8000 0000相同。下表描述了仅有32位地址时的入口点― 表中的BASE代表编程到EBase寄存器中的异常基址。
最初的异常向量间的距离缺省为128 ( 0x80)字节空间,可能是因为最初的MIPS体系结构师觉得32条指令足够编码基本的异常处理例程了,不浪费太多内存而且省下了一条分支指令!现代的程序员很少有这么节省的。
表:异常入口点
下面是MIPS CPU决定处理一个异常时所采取的步骤:
1.设置EPC指向重新开始的地址。
2.设置SR ( EXL)位,强制CPU进入内核态(高特权级)并且禁止中断。
3.设置Cause寄存器这样软件可以看到发生异常的原因。在地址异常时,也要设置BadVAddr。存储器管理系统异常还要设置某些MMU寄存器;
4.然后CPU开始从异常入口点取指,此后一切交给软件处理。
很短的异常处理例程可以全程运行在SR ( EXL)置位的状态(即我们所说的异常模式),从来不会需要碰SR的其余部分。对于那些先保存状态然后将控制交给复杂软件的较传统的异常处理程序,异常特权级提供一个保护伞让系统软件可以在其下安全地保存必要的状态― 包括老的SR值。