MIPS体系结构的特点

  1. MIPS指令集的限制
    • 所有的指令长度都是32位
    • 指令操作必须适合流水线
    • 三操作数的指令
    • 32个寄存器
    • 寄存器零(?作用)
    • 没有条件码(很强大)?没有标志位,但是所有信息保存在寄存器堆中
  2. 寻址和访存
    • 访问内存只能通过简单的寄存器加载和存储(对内存变量进行算术运算会打乱流水线)
    • 只有一种数据寻址方式(单个寄存器基址加上一个16位的常数偏移量寻址内存)
    • 字节地址访问(?)
    • load/store必须对齐
    • 跳转指令(有228=256MB的地址空间)(超过的可以使用一个寄存器跳转指令做到,可以跳到任意32位地址)(条件分支只有128KB的范围)
  3. MIPS的特性
    • 没有字节或半字数据的运算:所有的算术和逻辑操作都在32位的数据上进行。(int类型32位的整数)
    • 没有对堆栈的特殊支持(堆栈弹出不能适应流水线(因为有两个寄存器要写:来自堆栈的数据和增加指针值))
    • 最少的子程序支持(?很大的好处?(返回地址存入$31(返回地址寄存器)))
    • 最少的中断处理
    • 最少的异常处理(保留了两个寄存器给用于异常,这样异常处理程序可以自举?)

MIPS架构中的PC(程序计数)

MIPS架构中没有X86中的PC(程序计数)寄存器,它的程序计数器不是一个寄存器。因为在MIPS这样具有流水线结构的CPU中,程序计数器在同一时刻可以有多个给定的值,如jal指令的返回地址跟随其后的第二条指令。

... 
jal printf 
move $4, $6 
xxx # return here after call 

MIPS32中也没有条件码,比如在X86中常见的状态寄存器中的Z、C标志位在MIPS32中是没有的,但是MIPS32中是有状态寄存器

MIPS32中整数乘法部件

MIPS32中不同于其它的RISC架构的地方是其有 整数乘法部件 ,这个部件使用两个特殊的寄存器HI、LO,并且提供相应的指令 mfhi/mthi,mthi/mtlo来实现整数乘法结果--hi/lo寄存器与通用寄存器之间的数据交换

CP0 (协处理器0)--MIPS处理器控制

MIPS支持最多4个协处理器。协处理器CP0为CPU的控制协处理器,是体系结构中必须实现的。CP1为浮点处理器。CP2保留,各生产厂商往往用来实现一些自己的特色功能,例如RMI的Fast Message Ring等。CP3原本也是保留共扩展用,但很多MIPS III和MIPS IV时代的扩展指令集使用了它。为适应处理器向多核时代的演进,MIPS引入了多核操作必要的原子指令(Atomic operation)、内存屏障(Barrier)等操作。在SMP或AMP等多核架构中,这些指令是并行计算同步的保障。

CP0用于控制和设置MIPS CPU,里面包含了一些寄存器,同过对这些寄存器的不同的位的操作可以实现对处理器的设置。CP0类似于X 86只能有内核 (高优先级权限)访问的一些处理器资源,而提到的通用寄存器GPR和FPR则可以有由所有的优先级权限访问。CP0提供了 中断异常处理、内存管理(包括CACHE、TLB)、外设管理 等途径(而这些只能由高优先级的内核才能访问到)。

起作用的寄存器及其作用:加电后,你需要设置SR和Config寄存器,以确保CPU进入正确的引导状态,并且SR寄存器还设置了中断码。以决定系统响应哪些中断。

进入任何异常:处理任何例外都会调用一个“通用异常处理程序”。MIPS体系并没有为进入异常作任何的寄存器保存,也没有关于堆栈方面的任何支持,进入异常时唯一保存的就是异常返回地址保存在EPC中。所以这些都需要操作系统的软件实现。操作系统可以使用K0或者K1寄存器作为堆栈指针,指向某个位置,并且在需要保存的寄存器保存到这个栈上。然后通过Cause寄存器找到发生异常的原因,这样才能跳转到对应的中断处理程序中。从异常返回:从异常返回到EPC制定的地址之前要把CPU的状态回复到异常之前,好象什么事情都没有发生一样。在R3000中使用rfe指令作这样的事情,但是着条指令仅仅恢复了一些寄存器中的内容,但是并没有转移控制指令,你需要把EPC内容保存到一个通用寄存器中,然后调用jr指令返回到EPC指向的地址处,

数据加载与存储

MIPS CPU可以在一个单一操作中存储1到8个字节。文档中和用来组成指令助记符的 命名约定如下:

C名字 MIPS名字 大小(字节) 汇编助记符
-----------+----------+--------+---------
long long dword 8 "d"代表ld
int/long word 4 "w"代表lw
short halfword 2 "h"代表lh
char byte 1 "b"代表lb

包括这样几条记载指令lb/lbu、lh/lhu、lw

byte和short的加载有两种方式。带符号扩展的lb和lh指令将数据值存放在32位寄存器的低位中,剩下的高位用符号位的值来扩充(位7如果是一个byte,位15如果是一 个short)。这样就正确地将一个带符号整数放入一个32位的带符号的寄存器中。

不带符号指令lbu和lhu用0来扩充数据,将数据存放纵32位寄存器的低位中,并将高位用零来填充。例如,如果一个byte字节宽度的存储器地址为t1,其值为0xFE(-2或254如果是非符号数),那么将会在t2中放入0xFFFFFFFE(-2作为一个符号数)。t3的值会是0x000000FE(254作为一个非符号数)

lb t2, 0(t1) lbu t3, 0(t1)

包括这样几条指令sb、sh、sw

由于加载就是把寄存器中的数据加载到内存中,所以不存在位扩展的问题。

MIPS的存储管理模型

MIPS32中的存储器模型被划分为四个大块,其中:

虚拟地址范围 命名 描述
------------------------------------+-------+-------------------------------------------------------------------------------------
0x0000,0000~0x7fff,ffff(0~2G-1) USEG must be mapped (set page table and TLB)and set cache before use
0x8000,0000~0x9fff,ffff(2G~2.5G-1) KSEG0 directly mapped(no need to set page table and TLB) but need to set cache before use
0xa000,0000~0xbfff,ffff(2.5G~3G-1) KSEG1 directly mapped(no need to set page table and TLB) and never use cache
0xc000,0000~0xffff,ffff(3G~4G-1) KSEG2 muse be mapped(set page table and TLB) and set cache before use

这样的存储器管理模型和X86差距比较大,X86有一个实模式,内核在启动保护模式之前,运行在实模式之下,直到设定了保护模式之后才能运行在保护模式下。在MIPS32中没有保护模式那么系统是如何启动的呢?

MIPS32中的 处理器启动向量 位于KSEG1中0xbf10,0000,由于KSEG1是directly mapped的,所以直接对应了物理地址0x1fc0,0000,你可以在内核中一直使用0xa000,0000~0xbfff,ffff之间的虚拟地址来访问物理地址0~512M-1,在设置了KSEG0的cache之后,也可一使用0x8000,0000~0x9fff,ffff之间的虚拟地址来访问0~512M-1之间的物理地址。对于512M以上的物理地址只能由KSEG2和USEG通过页表访问。

x86的处理器启动向量为0xffff,fff0
ARM的处理器启动向量为

MIPS32的C语言中参数传递和返回值的约定

caller至少使用16bytes 堆栈空间存放参数,然后把这16 bytes存放到通用寄存器a0~a3中, called subroutine 直接使用寄存器中的参数,同时caller 堆栈中的16bytes的数据可以不去理会了。

需要理解的是带有浮点参数和结构体的参数传递,对于带有浮点参数的传递需要看第一个参数是否是浮点,如果是浮点则将参数放到 \(f12和\)f14这两个浮点寄存器中,如果第一个参数不是浮点数,则不用浮点寄存器存放参数。对于结构体的参数传递和x86类似对于整数和指针类型的