首页 > 代码库 > 自己动手写CPU之第五阶段(3)——MIPS指令集中的逻辑、移位与空指令
自己动手写CPU之第五阶段(3)——MIPS指令集中的逻辑、移位与空指令
将陆续上传本人写的新书《自己动手写CPU》(尚未出版),今天是第17篇,我尽量每周四篇
5.4 逻辑、移位操作与空指令说明
MIPS32指令集架构中定义的逻辑操作指令有8条:and、andi、or、ori、xor、xori、nor、lui,其中ori指令已经实现了,本章要实现其余7条指令。
MIPS32指令集架构中定义的移位操作指令有6条:sll、sllv、sra、srav、srl、srlv。
MIPS32指令集架构中定义的空指令有2条:nop、ssnop。其中ssnop是一种特殊类型的空操作,在每个周期发射多条指令的CPU中,使用ssnop指令可以确保单独占用一个发射周期。OpenMIPS设计为标量处理器,也就是每个周期发射一条指令,所以ssnop的作用与nop相同,可以按照nop指令的处理方式来处理ssnop指令。
另外,MIPS32指令集架构中还定义了sync、pref这2条指令,其中sync指令用于保证加载、存储操作的顺序,对于OpenMIPS而言,是严格按照指令顺序执行的,加载、存储操作也是按照顺序进行的,所以可以将sync指令当作nop指令处理,在这里将其归纳为空指令。pref指令用于缓存预取,OpenMIPS没有实现缓存,所以也可以将pref指令当作nop指令处理,此处也将其归纳为空指令。
以上17条指令按照格式、作用的不用,又可分为几类,分别说明如下。
1、and、or、xor、nor
这4条指令的格式如图5-10所示。从图中可以发现这4条指令都是R类型指令,并且指令码都是6‘b000000,也就是MIPS32指令集架构中定义的SPECIAL类。此外,第6-10bit都为0,需要依据指令中0-5bit功能码的值进一步判断是哪一种指令。
- 当功能码是6‘b100100时,表示是and指令,逻辑“与”运算
指令用法为:and rd, rs, rt
指令作用为:rd <- rs AND rt,将地址为rs的通用寄存器的值,与地址为rt的通用寄存器的值进行逻辑“与”运算,运算结果保存到地址为rd的通用寄存器中。
- 当功能码是6‘b100101时,表示是or指令,逻辑“或”运算
指令用法为:or rd, rs, rt
指令作用为:rd <- rs OR rt,将地址为rs的通用寄存器的值,与地址为rt的通用寄存器的值进行逻辑“或”运算,运算结果保存到地址为rd的通用寄存器中。
- 当功能码是6‘b100110时,表示是xor指令,异或运算
指令用法为:xor rd, rs, rt
指令作用为:rd <- rs XOR rt,将地址为rs的通用寄存器的值,与地址为rt的通用寄存器的值进行逻辑“异或”运算,运算结果保存到地址为rd的通用寄存器中。
- 当功能码是6‘b100111时,表示是nor指令,或非运算
指令用法为:nor rd, rs, rt
指令作用为:rd <- rs NOR rt,将地址为rs的通用寄存器的值,与地址为rt的通用寄存器的值进行逻辑“或非”运算,运算结果保存到地址为rd的通用寄存器中。
2、andi、xori指令
这2条指令的格式如图5-11所示。从图中可以发现这2条指令都是I类型指令,可以依据指令中26-31bit指令码的值判断是哪一种指令。
- 当指令码是6‘b001100,表示是andi指令,逻辑“与”运算
指令用法为:andi rt, rs, immediate
指令作用为:rt <- rs AND zero_extended(immediate),将地址为rs的通用寄存器的值,与指令中立即数进行零扩展后的值进行逻辑“与”运算,运算结果保存到地址为rt的通用寄存器中。
- 当指令码是6‘b001110,表示是xori指令,异或运算
指令用法为:xori rt, rs, immediate
指令作用为:rt <- rs XOR zero_extended(immediate),将地址为rs的通用寄存器的值,与指令中立即数进行零扩展后的值进行逻辑“异或”运算,运算结果保存到地址为rt的通用寄存器中。
3、lui指令
lui指令的格式如图5-12所示。从图中可以发现lui指令是I类型指令,可以依据指令中26-31bit指令码的值是否为6‘b001111,从而判断是否是lui指令。
指令用法为:lui rt, immediate
指令作用为:rt <- immediate || 016,将指令中的16bit立即数保存到地址为rt的通用寄存器的高16位,另外,地址为rt的通用寄存器的低16位使用0填充。
4、sll、sllv、sra、srav、srl、srlv指令
这6条指令的格式如图5-13所示,从图中可以发现这6条指令都是R类型指令,并且指令码都是6‘b000000,也就是都是SPECIAL类,需要依据指令中0-5bit功能码的值进一步判断是哪一种指令。
- 当功能码是6‘b000000,表示是sll指令,逻辑左移
指令用法为:sll rd, rt, sa
指令作用为:rd <- rt << sa (logic),将地址为rt的通用寄存器的值,向左移sa位,空出来的位置使用0填充,结果保存到地址为rd的通用寄存器中。
- 当功能码是6‘b000010,表示是srl指令,逻辑右移
指令用法为:srl rd, rt, sa
指令作用为:rd <- rt >> sa (logic),将地址为rt的通用寄存器的值,向右移sa位,空出来的位置使用0填充,结果保存到地址为rd的通用寄存器中。
- 当功能码是6‘b000011,表示是sra指令,算术右移
指令用法为:sra rd, rt, sa
指令作用为:rd <- rt >> sa (arithmetic),将地址为rt的通用寄存器的值,向右移sa位,空出来的位置使用rt[31]的值填充,结果保存到地址为rd的通用寄存器中。
- 当功能码是6‘b000100,表示是sllv指令,逻辑左移
指令用法为:sllv rd, rt, rs
指令作用为:rd <- rt << rs[4:0](logic),将地址为rt的通用寄存器的值,向左移位,空出来的位置使用0填充,结果保存到地址为rd的通用寄存器中。移位位数由地址为rs的寄存器值的0-4bit确定。
- 当功能码是6‘b000110,表示是srlv指令,逻辑右移
指令用法为:srlv rd, rt, rs
指令作用为:rd <- rt >> rs[4:0](logic),将地址为rt的通用寄存器的值,向右移位,空出来的位置使用0填充,结果保存到地址为rd的通用寄存器中。移位位数由地址为rs的寄存器值的0-4bit确定。
- 当功能码是6‘b000111,表示是srav指令,算术右移
指令用法为:srav rd, rt, rs
指令作用为:rd <- rt >> rs[4:0](arithmetic),将地址为rt的通用寄存器的值,向右移位,空出来的位置使用rt[31]填充,结果保存到地址为rd的通用寄存器中。移位位数由地址为rs的寄存器值的0-4bit确定。
总结来说,这六条移位操作指令可以分为两种情况:sllv、srav、srlv这3条指令的助记符最后有“v”,表示移位位数是通过寄存器的值确定的,sll、sra、srl这3条指令的助记符最后没有“v”,表示移位位数就是指令中6-10bit的sa的值。
5、nop、ssnop、sync、pref指令
这4条指令的格式如图5-14所示。从图中可以发现nop、ssnop、sync这3条指令都是R类型指令,并且指令码都是6‘b000000,也就是都是SPECIAL类。
更进一步,可以发现nop、ssnop两条指令的功能码都是6‘b000000,与之前介绍的逻辑左移指令sll的功能码相同,这样在译码的时候会不会有冲突:nop指令的二进制码与sll $0,$0,0的二进制码一样,处理器如何译码?ssnop指令的二进制码与sll $0,$0,1的二进制码一样,处理器如何译码?
nop = sll $0,$0,0 ssnop = sll $0,$0,1
其实两者是等价的,sll指令向$0寄存器保存移位结果,实际不会有任何效果,因为无论向$0写任何数,其值始终为0,所以效果等同于什么都不做,这也正是空指令nop、ssnop的效果。所以nop、ssnop指令不用特意实现,完全可以当作特殊的逻辑左移指令sll。