所属类别:Linux
文章作者:hylpro
特别推荐:免费发布信息 承包关键词~~抢爆了!HOT!
2008.9.25
作者提醒大家, Architecture 不是用来描述一个实现的,而是一个接口. 但是如果你对性能或者其他某些特别的地方有苛刻要求,那么就无法忽略实现的细节了. 另外Architucture几乎和ISA同义.
MISP32/MIPS64 是MIPS公司最近推出的标准, 是和MIPSI, II, III, IV,V 并列的.
MIPS I: R2000/3000, every MIPS CPU there ever was will run these instructions. (每个曾经存在的MIPS CPU都要运行这些指令)
MIPS II: MIPS R6000, MIPS6000 虽然没有广泛应用, 但是有许多和他非常类似的CPU广泛应用于嵌入式市场. MIPS32 直接派生于MIPS II .
MIPS III: R4000, 64bit.
MIPS IV: 和MIPSIII比较, 增加了几个指令(多数是浮点运算), 出现于R10000, R5000.
MIPS V: 增加了一些SIMD 浮点运算(令人吃惊), 并且实际上没有MIPS V的CPU. 许多指令只是MIPS 64的一个可选部分: 称为“paired-single.” MIPS32, MIPS64: MIPS离开SGI后宣布了新的标准, 其中包括了CP0的标准. 1999年以后的CPU都遵从这个标准(他也有不同的版本,2003年出版了第二版. MIPS32 是MIPSII的超集, MIPS64是MIPS IV的超集,MIPS V大部分作为可选项出现. 这些标准足够让应用程序顺利从一个cpu移植到另一个, 但是, 对于OS则稍显不足. 特别是早期的R3000,又有相当不同的控制指令和寄存器(和MIPS32比较).许多cpu还实现了自己特定的feature.
指令扩展标准
RISC 并不意味着这种实现要保持尽量小的指令集,实际上, 当MIPS在嵌入式领域广泛应用后,许多专用指令被开发出来. 一部分的发明被MIPS32/64所吸收, 并制定了一些标准,称为ASE(Application-Specific instruction set Extensions)). 通过CPU的配置寄存器来描述.
--- MIPS16e 最初由LSI 引入, 为了减小程序映像大小引入了16bit的指令, 后来觉得有点小,指定了MIPS16e. 有点类似于ARM Thumb.
---MDMX最初SGI 定义了一些FP(CP1)的SIMD指令, 许多指令都是饱和指令, 主要用于音频视频处理,常见于特殊的CPU, DSP. MDMX是早期MMX的一些变种, 但是SGI并没有实现MDMX, Broadcom好像是唯一个支持MDMX的.
---Smart MIPS 一个为智能卡扩展的小的指令集, 但是智能卡业务并不是很繁荣.
---MT 相当小但是很重要的一个扩展, 多线程扩展. 2005年发布于34k
--- DSP 类似MDMX, 也是2005年发布的, 24k,34k系列. 事实证明比MDMX更好些.
下面是另外一些MIPS32/64的可选实现, 也许不算是指令集扩展:
--- Floating point, CP1 --- CP2 : 开放给用户的编码区(RFC). --- CorExten: 自定义指令扩展, 2003/2002年的时候被广泛的宣传, 最初由ARM,Tensilica提出. MIPS也比较适合这种扩展. --- EJTAG : 用于加强调试功能. --- Paired single: FP 的SIMD扩展, 同时操作两个单精度(32bit)值 --- MIPS-3D: 一般和Paired single 配合使用, 提供许多浮点矩阵运算指令.
小议MIPS 汇编
稍微关注下MIPS汇编器: # 注释 name: 标号 直接使用寄存器编号, 如$1,如果要用名字, 需要C 预处理器支持
下面开始寄存器描述:
只有两个比较特殊: $0 总是返回0, 丢失写入值. 多么像 /dev/null $31 jal用他来存储返回地址, 而jalr可以使用任意寄存器存放返回值, 你最好还是用$31.
不要用认为MIPS有个PC, 如下, 返回的指令是跳转后的第二条指令( branch slot), 同时提醒下, delay slot内的指令在跳转target指令的前面执行完成. ... jal printf mov $4, $6 xxx # return here after call
为乘除法准备的hi,lo其实不是寄存器, 但为了能恢复状态, 可以向内部写入特定值. 再次提一下, MIPS没有状态码寄存器. FPU 有自己的32个寄存器, 简单的汇编中叫做$f0 - $f31. 早期MIPS只有偶数号的浮点寄存器可用, 但是提供单精度和双精度两种数值. 只有在load store或者在普通寄存器和Fpu寄存器间搬运数据的时候才会见到奇数号的寄存器. MIPS32/64 虽然可以使用奇数号寄存器,但是不推荐这么做,CPU有控制位可以模拟老的MIPS FPU的行为.
MIPS 寄存器约定
SGI 于1996年提出了新的约定,称为n32,n64, 这里暂时先不考虑. 一些简单注释:
--- at 给汇编语言预留的, 用来支持宏语句, 如果你要显式的使用at,需要禁止汇编器使用at,这样以来macro语句将不能使用.
--- v0,v1, a0-a3 都是给非浮点用的 ---- gp PIC(位置无关)应用中用于指向GOT. 普通应用中可以用于存取静态或者extern变量. 要求数据在64kb范围内. 没有gp,存取一个变量需要两条指令, 一条加载其高位地址到一个base寄存器,另一条用地位地址做偏移存取数据本身.
---- sp 堆栈调整完全由子函数控制, 一般只需要在入口和出口调整即可, 然后用sp做base来访问临时变量. 反而效率较高(?)
---- fp/s8 frame pointer(图片来自wikipedia.org, 上边是低地址) 考虑下图, 如果实现知道sp的调整量, 就不需要fp了, 入口减一个常量,出口加一下. 但是如果不知道, 还是用fp记录一下方便. 比如用alloca,
以上的约定是MIPS发布的原始约定, 称为o32. 整数乘除法运算单元在1980s许多RISC的设计不太重视乘除, 但MIPS 认为乘除法比较重要, 值得有个专门的指令. MIPS 采用了一个特殊用途的乘法运算单元, 没有集成到标准的pipeline里. 乘法单元的基本操作是把两个寄存器大小的值相乘得到一个两倍于寄存器的结果,存放在内部的hi,和lo内. 通过mfhi, mflo来访问.
如果乘法运算结束前去访问hi,lo, cpu讲stop下来等待, 就是说乘法结果寄存器是interlocked的. 做除法的时候,lo存放商, hi存放余数. 乘法约需要4-12cycle, 除法20-80 cycle.
也有的CPU实现了pipeline的乘法单元, 可以1或者两个cycle开始一个乘法,虽然不能每两个cycle产生一个结果.
MIPS32/64 包含了一个三操作数的mul指令,但是CPU必须停下来等待乘法完成. MIPS32/64还有乘法累加操作, 就是每次相乘的结果都累加到lo/hi.
乘除不会产生异常, 即使div0, 所以许多编译器产生一些代码来检查像div0这种情况.
唯一的寻址模式
这个爽阿. lw $1, offset($2) 但是, MIPS 要求严格的按照数据类型对齐. 对于加载非对齐数据, 有汇编宏指令来处理, 如ulw, ulh. 大部分情况下C编译器可以很好的处理对齐问题. 在夸系统通讯的时候可以用类似gcc的pack(1)来强制编译器产生特殊指令来处理这中情况.
对于浮点寄存器的加载, FPU 不会产生任何异常(有问题运算的时候才能知道). 在32bit CPU 上加载双精度的浮点数有宏指令的帮助,如:
l.d $f2, 24(t1) 这条指令被扩展为(c1: 应该是coprocessor 1的意思) lwc1 $f2, 24(t1) lwc1 $f3, 28(t1)
在64位系统上, l.d 就是ldc1. 符合SGI/MIPS 的32位的编译器,会把双精度浮点数安排到8byte对齐的地址上,这样可以和64bit兼容. Data Type
lb t2, 0(t1) 符号扩展 lbu t3, 0(t1) 无符号扩展
MIPS 是没有字节和halfword(16bit)运算的, 如果在C中采用SHORT和BYTE类型的变量,C需要额外的指令来模拟溢出. 汇编语言中的合成指令
笔者认为汇编器因该一对一的产生机器码. 下面是一些有用的合成指令.
---- 32 bit load immediate : li, 你不用用两条指令来加载一个立即数了,汇编指令来帮助你.
---- load from memory location: 被拆成两条指令, 一个加载内存高地址到一个base寄存器,另一个用相对于这个base寄存器的偏移来load这个地址的内容.
---- Efficient access to memory variables: 有些编译器把常用的static 和 extern 变量放到一个特定的段内(64K 内) 然后用gp 和偏移来加速访问(无须两条指令了).
---- 多种扩展的条件跳转
---- 提供更简单的指令: 比如提供一元操作数的指令....
---- 隐藏delay slot 可以使用.set noreorder 来禁止汇编器处理延迟槽.
---- 隐藏load delay
---- 非对齐传输 如ulh ulw
-----其他pipeline 校正: 如对乘除法,一些老的CPU有特殊要求.
MIPS CPU 从32bit到64bit的扩展 MIPS 从32bit 到64bit的扩展是如此的干净利落, 看起来32bit的CPU倒像是64bit的一个有机部分. MIPS 指令集是完全向后兼容的, 新的CPU上,老的程序(至少是应用)可以完美的运行. 唯一例外就是MIPS V, MIPS64没有包含, 但是, MIPS V 根本就没有对应的CPU被生产出来. MIPSII也有少许特殊, 因为MIPS6000没有竞争过MIPS4000(MIPSIII).
在MIPS32/64之前的ISA是不包含MIPS CP0的内容的. CP0是逐步进入正式的ISA的, 有两个主要变种: R3000(过时了), 和派生于 MIPSIII(R4000)的MIPS64. (MIPS32? 派生于MIPSII) 1990年的R4000, 是RISC界第一量产的64bit CPU,直到1996年,64bit才成为每个CPU的标配. MIPS64下所有的寄存器都是64bit的,所有操作都产生64bit结果. 如果32bit的操作不适合64bit,就加入新的指令. MIPS32的CPU容许包含一个仅支持32bit的FPU,但是MIPS32/64的CPU其实都是64bit的FPU, 用不着用一对寄存器来放一个双精度的浮点数了. 为了兼容, 这些CPU可以切换到老的FPU模式下.
MIPS的本质是使用flat地址空间, 使用通用寄存器当作指针, 所以MIPS的64bit寄存器和64bit地址空间同时出现.同时到64bit后,分段已经没有什么必要了, 但是他们仍然是64bit的x86和PowerPC的负担.
x86和DEC的PDP-11->VAX都是定义一个模式切换来作这个事情. 但是这并不高明. MIPS采用了另外的办法.
----保留所有的32比特指令 ----只要你只运MIPS32的指令, 就能保证100%兼容. 64bit寄存器的低32bit和MIPS32的运行的结果相同.----As many as possible of the MIPS32 instructions are defined so as to be both compatible and still to be useful MIPS64 instructions.(翻译:) 一个重要的决定是MIPS64中高32bit应该包含什么值. 无论是未定义还是都是0, 都不能满足要求: 测试,和条件跳转会比较两个寄存器或者测试一个寄存器是否是负值(31bit或者63bit), 从而违反上面的第三条原则. 正确的做法是让高32bit的每一位都是31bit的copy. (这个和符号扩展类似,但是这中扩展其实不关符号的事情). 这个决定之下, 我们需要新的64bit的算术运算指令(比如addu, 为了和32bit兼容,溢出针对低half word, 不合适64bit操作), 另外还需要64bit load, 和移位指令. 这个修改比较适中. 64bit的指令都带有助记符'd'. 如daddu, dsub, dmult, ld. 64bit下, lw成为一个符号扩展的lw, 而新的lwu出现了.
MIPS32 Memory Map 3个CPU模式: kernel,user, supervisor 模式, 现在好像还没有OS 使用supervisor模式. MIPS从kernel模式切换到user模式的时候CPU的工作方法上没有什么不同,只是某些东西是非法的: 比如,user 模式下程序地址最高有效位置位就会引发trap, 一些其他指令在use mode下也会引发异常. 注意图中 unmapped/mapped是指这些空间是否需要MMU才能访问. 对于mapped的地址空间, 必须有MMU的介入, 但是对于没有MMU的CPU, 访问maaped的空间的行为需要查看具体的CPU手册才能确定. kuseg 从0x0000 0000 到0x8000 0000, 只有这些地址是最高bit没有置位的,所以他们是user mode的应用所处的地址空间.kseg1/kseg0 都映射到物理地址0-512M, 不同在于一个是通过cache访问(意味着必须先设置好cache才行), 一个是uncache的.CPU对于kseg0,kseg1的地址处理比较简单,对kseg0, CPU简单的把最高位bit忽略就是其物理地址, 对于kseg1,忽略最高的3个bit即可, 这中转换很简单, 有些地方也叫"untranslated"的地址(其实是比较MMU而言的). 现在可以理解为啥reset entry是0xBFC0.0000位于kseg1了吧,但是千万不要忘记,这个入口的物理地址是0x1FC0.0000.kseg2: 有的人强调kseg2分成两个部分, kseg2,kseg3, 因为kseg2的低512M(这个要考证下,和下面的图中不一样阿)是supervisor mode 的地址空间.有些简单的系统把任何地址都映射到物理的0-512M,这样CPU的设置就非常的简单.MIPS64 Memory Map 一个有趣的现象是, 64bit的地址空间从某种意义上讲,是包含在32bit的地址空间内的,呵呵有个东西, 里边比外边大的多.这个是这样形成的, MIPS64是兼容MIPS32的, 无须要模式切换,一个重要的动作就是, 高32bit的每个bit都是31bit的copy(如果运行32bit指令). 所以, MIPS32的4G空间位于了64bit 地址空间的高2G和低2G.Pipeline Visibility -- Again任何采用pipeline的CPU, 都会受限于单cycle操作的要求, 一旦有操作不能在一个cycle执行完成, 体系的设计者就要考虑是否暴露这些延迟给programer. 选择隐藏就方便了别人苦了自己. MIPS, 已经提过一次了, 暴露了一些可见的pipeline效应. 这里有些东西补充.-- Branch delay slotMIPSII 引入了一个"branch likely"指令, 让delay slot内的指令唯有在brache target 被采纳的时候才执行. 这样就给循环利用dealy slot 一个便利的条件.--- load to use delay硬件的工作是保证,下下一条指令就可以使用这个数据了. (一般编译器都会处理好这个延迟, 些程序的时候一般无须担心).--- Floating Point (CP1)比较耗费时间, 如果在计算完成前读取结果, CPU就会stall下来等待结果.---CPU 控制指令改变CP0的寄存器的时候,MIPS的行为比较值得注意. 到MIPS32/64, 事情变得比较清晰了, 与CP0的交互被分成两种. 一种会影响之后的指令预取的叫做instruction hazards, 剩下的叫execution hazards. 并为没有harzards提供了相应的barrier. 这个以后再讨论.在第二版的MIPS32/64发布前, 没有这些东西,你需要查看CPU手册来确定你需要多少填充性的(如nop)指令让这些影响有足够的时间传播.
相关信息· 对权限控制又很深入的讨论(1)
· 利用SOAPtest进行Web service测试
· SharpDevelop源码分析 (二、头绪)
· 电脑爱好者,计算机应用文摘,大众软件电子书下载
70467
9166
