MIPSCPU体系结构概述陈怀临1。序言本文介绍MIPS体系结构,着重于其寄存器约定,MMU及存储管理,异常和中断处理等等。通过本文,希望能提供一个基本的轮廓概念给对MIPSCPU及之上OS有兴趣的读者。并能开始阅读更详细的归约(SPECIFICATION)资料。MIPS是最早的,最成功的RISC(ReducedInstructionSetComputer)处理器之一,起源于StanfordUniv的电机系.其创始人JohnL.Hennessy在1984年在硅谷创立了MIPSINC.公司()。JohnL.Hennessy目前是StanfordUniv.的校长。在此之前,他是Stanford电子工程学院的Dean。CS专业的学生都知道两本著名的书:“ComputerOrganizationandDesign:TheHardware/SoftwareInterface”和”ComputerArchitecture:AQuantitativeApproach“。其Co-author就是Hennessy.MIPS的名字为“Microcomputerwithoutinterlockedpipelinestages的缩写。另外一个通常的非正式的说法是”Millionsofinstructionspersecond.MIPS芯片在工业界目前用的比较多的是:MIPSINC。的R10000;QED(。1996年从MIPSINC。分(SPINOFF)出来的)的R5000,R7000等。指令集详细的资料请参阅MIPS归约。一般而言,MIPS指令系统有:MIPSI;MIPSII;MIPSIII和MIPSIV。可想而知,指令系统是向后兼容的。例如,基于MIPSII的代码可以在MIPIII和MIPSIV的处理器上跑一跑:-)下面是当我们用gcc时,如何指定指令和CPU的选项。-mcpu=cputypeAssumethedefaultsforthemachinetypecputypewhenschedulinginstructions.Thechoicesforcputypeare`r2000',`r3000',`r4000',`r4400',`r4600',and`r6000'.Whilepickingaspecificcputypewillschedulethingsappropriatelyforthatparticularchip,thecompilerwillnotgenerateanycodethatdoesnotmeetlevel1oftheMIPSISA(instructionsetarchitecture)withoutthe`-mips2'or`-mips3'switchesbeingused.-mips1Issueinstructionsfromlevel1oftheMIPSISA.Thisisthedefault.`r3000'isthedefaultcputypeatthisISAlevel.-mips2Issueinstructionsfromlevel2oftheMIPSISA(branchlikely,squarerootinstructions).`r6000'isthedefaultcputypeatthisISAlevel.-mips3Issueinstructionsfromlevel3oftheMIPSISA(64bitinstructions).`r4000'isthedefaultcputypeatthisISAlevel.ThisoptiondoesnotchangethesizesofanyoftheCdatatypes.读者可能发现,对于大多数而言,我们应该是用MIPSIII或-mips3。要提醒的是R5000和R10000也都是R4000的延伸产品。下面是几点补充:*MIPS指令是32位长,即使在64位的CPU上。这对于局部跳转指令的理解很有帮助。比如:J(TARGET);JAL(TARGET)。J和JAL的OPERCODE是6位,剩下的26为存放跳转偏移量。由于任何一个指令都是32位(或4字节)对齐(ALIGN)的,所以J和JAL最大的伸缩空间是2^28=256M。如果你的程序要作超过256M的跳转,你就必须用JALR或JR,通过一个GPR寄存器来存放你的跳转地址。由于一个寄存器是32或64位的,你就没有任何限制了。*MIPSCPU的SR(STATUSREGISTER)中有几位是很重要的设置,当我们选择指令系统或要用64位的MIPS的CPUCORE在32模式下(绝大多数情况,弟兄们别告诉我你在写64位的程序:--))。SR[XX]:1:MIPSIVINSTRUCTIONSETUSABLE0:MIPSIVINSTRUCTIONSETUNUSABLESR[KX]SR[SX]SR[UX]:0:CPU工作在32位模式下1:CPU工作在64位模式下一般而言,如果你要从头写一个MIPS核心为32位程序,最好把上述值设为0。为什么最好呢?因为我在工作中没有去冒风险,设她们为1,whoknowswhatwouldhappen?:-)Andthenwhybother:--)?*在以后我们会单独的一章讲将流水线和指令系统,特别是跳转指令的关系。在这里,我们只简单提一下。对任何一个跳传指令后面,FORSIMPLITY,要加上一个空转指令(NOP)。从而使得CPU的PIPELINE不会错误的执行一个预取(PRE_FETCH)得指令。当然这个NOP可以替换为别的。以后再讲。放一个NOP是最简单和安全的。有兴趣的读者可以用mips64-elf-objdump-d来反汇编一个OBJECT文件。你就会一目了然了。*一定要记住:MIPSI,II,III和IV指令系统不包含PRIVILEDGEDINSTRUCTIONS。换句话说,都是那些在USERMODE下可以用的指令(当然KERNEL下也能用)。对于CPO的操作不属于指令系统。*有一点在MIPSCPU下,要千万注意:ALIGN。MIPS对ALIGN的要求是严厉的。这一点与POWERPC是天壤之别。指令必须是32位对齐。数据类型必须在她们的的大小边界对齐。简单的比如:WhenCPUrunningunder32bitmode,intmust32bitaligned;long32bitaligned;pointermustbe32bitaligned;charmust8bitaligned.longlongmust64bitaligned;关于这一点,我是吃过苦头的。当然我知道大家还会犯错在这里:--),即使知道了。有些事情学是没用的:--)。一定要注意。*我建议读者阅读SPECIFICATION时要花时间看一看指令系统的定义。其实不难。每一种指令不外乎几个域(FIELDS)。寄存器约定对于在一个CPU上进行开发,掌握其工作的CPU的寄存器约定是非常重要的。MIPS体系结构提供了32个GPR(GENERALPURPOSEREGISTER)。这32个寄存器的用法大致如下:REGISTERNAMEUSAGE$0$zero常量0(constantvalue0)$2-$3$v0-$v1函数调用返回值(valuesforresultsandexpressionevaluation)$4-$7$a0-$a3函数调用参数(arguments)$8-$15$t0-$t7暂时的(或随便用的)$16-$23$s0-$s7保存的(或如果用,需要SAVE/RESTORE的)(saved)$24-$25$t8-$t9暂时的(或随便用的)$28$gp全局指针(GlobalPointer)$29$sp堆栈指针(StackPointer)$30$fp帧指针(FramePointer)(BNN:fpisstaleacutally,andcanbesimplyusedas$t8)$31$ra返回地址(returnaddress)对一个CPU的寄存器约定的正确用法是非常重要的。当然对C语言开发者不需要关心,因为COMPILER会TAKECARE。但对于KERNEL的开发或DRIVER开发的人就**必须**清楚。一般来讲,你通过objdump-d可以清醒的看到寄存器的用法。下面通过我刚才写的一个简单例子来讲解:~/viHello.cHello.c[Newfile]/*Exampletoillustratemipsregisterconvention*-Author:BNN*11/29/2001*/intaddFunc(int,int);intsubFunc(int);voidmain(){intx,y,z;x=1;y=2;z=addFunc(x,y);}intaddFunc(intx,inty){intvalue1=5;intvalue2;value2=subFunc(value1);return(x+y+value2);}intsubFunc(intvalue){returnvalue--;}上面是一个C程序,main()函数调用一个加法的子函数。让我们来看看编译器是如何产生代码的。~/bnn:74/bin/mips-elf-gcc-cHello.oHello.c-mips3-mcpu=r4000-mgp32-mfp32-O1~/bnn:75/bin/mips64-elf-objdump-dHello.oHello.o:fileformatelf32-bigmipsDisassemblyofsection.text:/*mainFunction*/0000000000000000:/*createastackframebymovingthestackpointer8*bytesdownandmeantimeupdatethespvalue*/0:27bdfff8addiu$sp,$sp,-8/*Savethereturnaddresstothecurrentspposition.*/4:afbf0000sw$ra,0($sp)8:0c000000jal0/*nopisforthedelayslot*/c:00000000nop/*Filltheargumenta0withthevalue1*/10:24040001li$a0,1/*JumptheaddFunc*/14:0c00000ajal28/*NOTEHERE:Whywefillthesecondargument*behindtheaddFuncfunctioncall?*Thisisallaboutthe-O1compilationoptimizaiton.*Withmipsarchitecture,theinstrucitonafterjump*willalsobefetchedintothepiplineandget*exectuted.Therefore,wecanpromisethatthe*secondargumentwillbefilledwiththevalueof*integer2.*/18:24050002li$a1,2/*Loadthereturnaddressfromthestackpointer*Noteherethattheresultv0containstheresultof*addFuncfunctioncall*/1c:8fbf0000lw$ra,0($sp)/*Return*/20:03e00008jr$ra/*Restorethestackframe*/24:27bd0008addiu$sp,$sp,8/*addFuncFunction*/0000000000000028:/*Createastackframebyallocating16bytesor4*wordssize*/28:27bdfff0