自己动手写CPU(5)简单算术操作指令实现_1
自己動(dòng)手寫(xiě)CPU(5)簡(jiǎn)單算數(shù)操作指令實(shí)現(xiàn)_1
指令介紹
MIPS32指令集架構(gòu)定義的所有算術(shù)操作指令,共有21條 共有三類(lèi),分別是:
- 簡(jiǎn)單算術(shù)指令
- 乘累加、乘累減指令
- 除法指令
算術(shù)指令操作介紹
一共有15條指令分別是:add、addi、addiu、addu、sub、subu、clo、clz、slt、slti、sltiu、sltu、mul、mult、multu
1.add、addu、sub、subu、slt、sltu指令
由指令格式可以看出這六條指令指令碼都是6’b000000即SPECIAL類(lèi),而且指令的第610bit都是0,根據(jù)指令的功能碼(05bit)來(lái)判斷是哪一條指令
- add(功能碼6’b100000):加法運(yùn)算,用法:add rd,rs,rt;作用:rd <- rs+rt,將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值進(jìn)行加法運(yùn)算,結(jié)果保存到地址為rd的通用寄存器中。如果加法運(yùn)算溢出,那么會(huì)產(chǎn)生溢出異常,同時(shí)不保存結(jié)果。
- addu(功能碼是6’b100001):加法運(yùn)算,用法:addu rd,rs,rt; 作用:rd <-rs+rd,將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值進(jìn)行加法運(yùn)算,結(jié)果保存到rd的通用寄存器中。不進(jìn)行溢出檢查,總是將結(jié)果保存到目的寄存器。
- SUB(功能碼是6’b100010):減法運(yùn)算,用法:sub rd,rs,rt; 作用:rd <- rs-rt,將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值進(jìn)行減法運(yùn)算,結(jié)果保存到地址為rd的通用寄存器中。如果減法運(yùn)算溢出,那么產(chǎn)生溢出異常,同時(shí)不保存結(jié)果。
- SUBU(功能碼是6’b100011):減法運(yùn)算,用法:subu rd,rs,rt; 作用:rd <- rs-rt將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值進(jìn)行減法運(yùn)算,結(jié)果保存到地址為rd的通用寄存器中。不進(jìn)行溢出檢查,總是將結(jié)果保存到目的寄存器。
- SLT(功能碼是6’b101010):比較運(yùn)算,用法:slt rd,rs,rt; 作用:rd <- (rs<rt)將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值按照有符號(hào)數(shù)進(jìn)行比較,若前者小于后者,那么將1保存到地址為rd的通用寄存器,若前者大于后者,則將0保存到地址為rd的通用寄存器中。
- SLTU(功能碼是6’b101011):比較運(yùn)算,用法:sltu rd,rs,rt; 作用:rd <- (rs<rt)將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值按照無(wú)符號(hào)數(shù)進(jìn)行比較,若前者小于后者,那么將1保存到地址為rd的通用寄存器,若前者大于后者,則將0保存到地址為rd的通用寄存器中。
2. addi、addiu、slti、sltiu指令
我們由指令格式可以看出,依據(jù)指令碼(26~31bit)判斷是哪一種指令
- ADDI(指令碼是6’b001000):加法運(yùn)算,用法:addi rt,rs,immediate; 作用:rt <- rs+(sign_extended)immediate,將指令中16位立即數(shù)進(jìn)行符號(hào)擴(kuò)展,與地址為rs的通用寄存器進(jìn)行加法運(yùn)算,結(jié)果保存到地址為rt的通用寄存器。如果加法運(yùn)算溢出,則產(chǎn)生溢出異常,同時(shí)不保存結(jié)果。
- ADDIU(指令碼是6’b001001):加法運(yùn)算,用法:addiu rt,rs,immediate; 作用:rt <- rs+(sign_extended)immediate,將指令中16位立即數(shù)進(jìn)行符號(hào)擴(kuò)展,與地址為rs的通用寄存器進(jìn)行加法運(yùn)算,結(jié)果保存到地址為rt的通用寄存器。不進(jìn)行溢出檢查,總是將結(jié)果保存到目的寄存器。
- SLTI(功能碼是6’b001010):比較運(yùn)算,用法:slti rt,rs,immediate; 作用:rt <- (rs<(sign_extended)immediate)將指令中的16位立即數(shù)進(jìn)行符號(hào)擴(kuò)展,與地址為rs的通用寄存器的值按照有符號(hào)數(shù)進(jìn)行比較,若前者小于后者,那么將1保存到地址為rt的通用寄存器,若前者大于后者,則將0保存到地址為rt的通用寄存器中。
- SLTIU(功能碼是6’b001011):比較運(yùn)算,用法:sltiu rt,rs,immediate; 作用:rt <- (rs<(sign_extended)immediate)將指令中的16位立即數(shù)進(jìn)行符號(hào)擴(kuò)展,與地址為rs的通用寄存器的值按照無(wú)符號(hào)數(shù)進(jìn)行比較,若前者小于后者,那么將1保存到地址為rt的通用寄存器,若前者大于后者,則將0保存到地址為rt的通用寄存器中。
3. clo、clz指令
由指令格式可以看出,這兩條指令的指令碼(2631bit)都是6’b011100,即是SPECIAL2類(lèi);而且第610bit都為0,根據(jù)指令中的功能碼(0~5bit)判斷是哪一條指令。
- CLZ(功能碼是6’b100000):計(jì)數(shù)運(yùn)算,用法:clz rd,rs; 作用:rd <- coun_leading_zeros rs,對(duì)地址為rs的通用寄存器的值,從最高位開(kāi)始向最低位方向檢查,直到遇到值為“1”的位,將該為之前“0”的個(gè)數(shù)保存到地址為rd的通用寄存器中,如果地址為rs的通用寄存器的所有位都為0(即0x00000000),那么將32保存到地址為rd的通用寄存器中。
- CLO(功能碼是6’b100001):計(jì)數(shù)運(yùn)算,用法:clo,rd,rs; 作用:rd <- coun_leading_zeros rs對(duì)地址為rs的通用寄存器的值,從最高位開(kāi)始向最低位方向檢查,直到遇到值為“0”的位,將該為之前“1”的個(gè)數(shù)保存到地址為rd的通用寄存器中,如果地址為rs的通用寄存器的所有位都為1(即0xFFFFFFFF),那么將32保存到地址為rd的通用寄存器中。
4. multu、mult、mul指令
由指令格式可以看出,mul指令的指令碼(2631bit)都是6’b011100,即是SPECIAL2類(lèi),mult和multu這兩條指令的指令碼(2631bit)都是6’b000000,即是SPECIAL類(lèi);有著不同的功能碼(0~5bit)
- mul(指令碼是SPECIAL2,功能碼是6’b000010):乘法運(yùn)算,用法:mul,rd,rs,st; 作用:rd <- rs * rt,將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值作為有符號(hào)數(shù)相乘,乘法結(jié)果的低32bit保存到地址為rd的通用寄存器中。
- mult(指令碼是SPECIAL,功能碼是6’b011000):乘法運(yùn)算,用法:mult,rs,st; 作用:{hi,lo} <- rs * rt,將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值作為有符號(hào)數(shù)相乘,乘法結(jié)果低32bit保存到LO寄存器中,高32bit保存到HI寄存器中。
- multu(指令碼是SPECIAL,功能碼是6’b011001):乘法運(yùn)算,用法:mult,rs,st; 作用:{hi,lo} <- rs * rt,將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值作為無(wú)符號(hào)數(shù)相乘,乘法結(jié)果低32bit保存到LO寄存器中,高32bit保存到HI寄存器中。
修改之處
譯碼階段
- add、addu、sub、subu、slt、sltu:需要兩個(gè)寄存器的值分別作為兩個(gè)操作數(shù),所以設(shè)置reg1_read_o和reg2_read_o都為1,運(yùn)算完后結(jié)果需要寫(xiě)入目的寄存器,所以設(shè)置wreg_o為WriteEnable,寫(xiě)入目的寄存器地址wd_o是指令中16~20bit的值。
- addi、addiu、subi、subiu:只需要讀取一個(gè)寄存器的值作為第一個(gè)操作數(shù),即設(shè)置reg1_read_o為1,reg2_read_o為0,第二個(gè)操作數(shù)為立即數(shù)進(jìn)行符號(hào)擴(kuò)展后的值,運(yùn)算完后結(jié)果需要寫(xiě)入目的寄存器,所以設(shè)置wreg_o為WriteEnable,寫(xiě)入目的寄存器地址wd_o是指令中16~20bit的值。
- mult、multu:需要兩個(gè)寄存器的值分別作為兩個(gè)操作數(shù),所以設(shè)置reg1_read_o和reg2_read_o都為1,運(yùn)算完后結(jié)果需要不需要寫(xiě)入通用寄存器,而是寫(xiě)入HI、LO寄存器所以設(shè)置wreg_o為WriteDisable。
- mul:需要兩個(gè)寄存器的值分別作為兩個(gè)操作數(shù),所以設(shè)置reg1_read_o和reg2_read_o都為1,aluop_o為EXE_MUL_OP運(yùn)算完后結(jié)果需要寫(xiě)入目的寄存器,所以設(shè)置wreg_o為WriteEnable,寫(xiě)入目的寄存器地址wd_o是指令中11~15bit的值。
- clo、clz:只需要讀取一個(gè)寄存器的值作為第一個(gè)操作數(shù),即設(shè)置reg1_read_o為1,reg2_read_o為0,運(yùn)算完后結(jié)果需要寫(xiě)入目的寄存器,所以設(shè)置wreg_o為WriteEnable,寫(xiě)入目的寄存器地址wd_o是指令中11~15bit的值。
執(zhí)行階段
根據(jù)譯碼階段的結(jié)果,來(lái)進(jìn)行相關(guān)的執(zhí)行操作
1.添加一些新的相關(guān)變量
reg[`RegBus] arithmeticres; //保存算術(shù)運(yùn)算結(jié)果 wire ov_sum; //保存溢出情況 wire reg1_eq_reg2; //第一個(gè)操作數(shù)是否等于第二個(gè)操作數(shù) wire reg1_lt_reg2; //第一個(gè)操作數(shù)是否小于第二個(gè)操作數(shù) wire[`RegBus] reg2_i_mux; //保存輸入的第二個(gè)操作reg2_i的補(bǔ)碼 wire[`RegBus] reg1_i_not; //保存輸入的第一個(gè)操作數(shù)reg1_i取反后的值 wire[`RegBus] result_sum; //保存加法結(jié)果 wire[`RegBus] opdata1_mult; //乘法操作中的被乘數(shù) wire[`RegBus] opdata2_mult; //乘法操作中的乘數(shù) wire[`DoubleRegBus] hilo_temp; //臨時(shí)保存乘法結(jié)果,寬度為64位 reg[`DoubleRegBus] mulres; //保存乘法結(jié)果,寬度為64位2.計(jì)算相關(guān)變量的值
2.1 reg2_i_mux
如果是減法或者有符號(hào)比較運(yùn)算,那么reg2_i_mux等于第二個(gè)操作數(shù)reg2_i的補(bǔ)碼,否則reg2_i_mux等于第二個(gè)操作數(shù)reg2_i
assign reg2_i_mux = ((aluop_i == `EXE_SUB_OP) || (aluop_i == `EXE_SUBU_OP) ||(aluop_i == `EXE_SLT_OP)) ? (~reg2_i)+1 : reg2_i;2.2 result_sum
- 如果是加法運(yùn)算,此時(shí)reg2_i_mux就是第二個(gè)操作數(shù)reg2_i,所以result_sum就是加法運(yùn)算的結(jié)果
- 如果是減法運(yùn)算,此時(shí)reg2_i_mux是第二個(gè)操作數(shù)reg2_i的補(bǔ)碼,所以result_sum就是減法運(yùn)算的結(jié)果
- 如果是有符號(hào)比較運(yùn)算,此時(shí)reg2_i_mux也是第二個(gè)操作數(shù)reg2_i的補(bǔ)碼,所以result_sum也是減法運(yùn)算的結(jié)果,可以通過(guò)判斷減法結(jié)果是否小于零,進(jìn)而判斷第一個(gè)操作數(shù)reg1_i是否小于第二個(gè)操作數(shù)reg2_i
2.3 ov_sum
計(jì)算是否溢出,加法指令(add和addi)、減法指令(sub)執(zhí)行的時(shí)候,需要判斷是否溢出,滿足一下兩種情況時(shí),有溢出:
- reg1_i為正數(shù),reg2_i_mux為正數(shù),但是兩者之和為負(fù)數(shù)
- reg1_i為負(fù)數(shù),reg2_i_mux為負(fù)數(shù),但是兩者之和為正數(shù)
2.4 reg1_lt_reg2
計(jì)算操作數(shù)1是否小于操作數(shù)2,分兩種情況
-
aluop_i為EXE_SLT_OP表示有符號(hào)比較運(yùn)算:
reg1_i為負(fù)數(shù)、reg2_i為正數(shù),顯然reg1_i小于reg2_i
reg1_i為正數(shù)、reg2_i為正數(shù),并且reg1_i減去reg2_i的值小于0(即result_sum為負(fù)),此時(shí)也有reg1_i小于reg2_i
reg1_i為負(fù)數(shù)、reg2_i為負(fù)數(shù),并且并且reg1_i減去reg2_i的值小于0(即result_sum為負(fù)),此時(shí)也有reg1_i小于reg2_i
-
無(wú)符號(hào)數(shù)比較的時(shí)候u,直接使用比較運(yùn)算符比較reg1_i與reg2_i
2.5 reg1_i_not
對(duì)操作數(shù)1逐位取反,賦給reg1_i_not
assign reg1_i_not = ~reg1_i;仿真結(jié)果
1. 測(cè)試add、addi、addiu、addu、sub、subu指令
ori $1,$0,0x8000 # $1 = 0x8000sll $1,$1,16 # $1 = 0x80000000ori $1,$1,0x0010 # $1 = 0x80000010 給$1賦值ori $2,$0,0x8000 # $2 = 0x8000sll $2,$2,16 # $2 = 0x80000000ori $2,$2,0x0001 # $2 = 0x80000001 給$2賦值ori $3,$0,0x0000 # $3 = 0x00000000addu $3,$2,$1 # $3 = 0x00000011 $1加$2,無(wú)符號(hào)加法ori $3,$0,0x0000 # $3 = 0x00000000add $3,$2,$1 # $2 加 $1,有符號(hào)加法,結(jié)果溢出,$3保持不變sub $3,$1,$3 # $3 = 0x80000010 $1減去$3,有符號(hào)減法subu $3,$3,$2 # $3 = 0xF $3減去$2,無(wú)符號(hào)減法addi $3,$3,2 # $3 = 0x11 $3 加2,有符號(hào)加法ori $3,$0,0x0000 # $3 = 0x00000000addiu $3,$3,0x8000 # $3 = 0xffff8000 $3加0xffff8000 無(wú)符號(hào)加法2. 測(cè)試slt、sltu、slti、sltiu
or $1,$0,0xffff # $1 = 0xffffsll $1,$1,16 # $1 = 0xffff0000 給$1賦值slt $2,$1,$0 # $2 = 1 比較$1與0x0,有符號(hào)比較sltu $2,$1,$0 # $2 = 0 比較$1與0x0,無(wú)符號(hào)比較slti $2,$1,0x8000 # $2 = 1 比較$1與0xffff8000,有符號(hào)比較sltiu $2,$1,0x8000 # $2 = 1 比較$1與0xffff8000,無(wú)符號(hào)比較3. 測(cè)試clo和clz指令
lui $1,0x0000 # $1 = 0x00000000 給$1賦值clo $2,$1 # $2 = 0x00000000 統(tǒng)計(jì)$1中“1”之前“0”的個(gè)數(shù)clz $2,$1 # $2 = 0x00000020 統(tǒng)計(jì)$1中“0”之前“1”的個(gè)數(shù)lui $1,0xffff # $1 = 0xffff0000ori $1,$1,0xffff # $1 = 0xffffffff 給$1賦值clz $2,$1 # $2 = 0x00000000 統(tǒng)計(jì)$1中“1”之前“0”的個(gè)數(shù)clo $2,$1 # $2 = 0x00000020 統(tǒng)計(jì)$1中“0”之前“1”的個(gè)數(shù)lui $1,0xa100 # $1 = 0xa1000000 給$1賦值clz $2,$1 # $2 = 0x00000000 統(tǒng)計(jì)$1中“1”之前“0”的個(gè)數(shù)clo $2,$1 # $2 = 0x00000001 統(tǒng)計(jì)$1中“0”之前“1”的個(gè)數(shù)lui $1,0x1100 # $1 = 0x11000000 給$1賦值clz $2,$1 # $2 = 0x00000003 統(tǒng)計(jì)$1中“1”之前“0”的個(gè)數(shù)clo $2,$1 # $2 = 0x00000000 統(tǒng)計(jì)$1中“0”之前“1”的個(gè)數(shù)4. 測(cè)試mul、mult、multu指令
ori $1,$0,0xffff sll $1,$1,16ori $1,$1,0xfffb # $1 = -5 給$1賦值ori $2,$0,6 # $2 = 6 給$2賦值mul $3,$1,$2 # $3 = -30 = 0xffffffe2 $1 乘以$2,有符號(hào)乘法,結(jié)果低32位保存到$3mult $1,$2 # hi = 0xffffffff # lo = 0xffffffe2# $1 乘以$2,有符號(hào)乘法,結(jié)果低32位保存到HI LOmultu $1,$2 # hi = 0x5# lo = 0xffffffe2# $1 乘以$2,無(wú)符號(hào)乘法,結(jié)果低32位保存到HI LOnopnop實(shí)驗(yàn)心得
1、ov_sum是否溢出信號(hào)需在執(zhí)行階段最后選擇運(yùn)算結(jié)果時(shí)進(jìn)行判斷,如果發(fā)生溢出行為則將寫(xiě)寄存器堆的信號(hào)取消使能;
2、mult和multu指令均不對(duì)寄存器堆進(jìn)行回寫(xiě)操作,僅改變特殊寄存器HI和LO的值,所以譯碼階段這兩個(gè)指令的alusel_o信號(hào)均需設(shè)置成EXE_RES_NOP,不對(duì)寄存器堆進(jìn)行回寫(xiě)操作;
3、由仿真的波形圖發(fā)現(xiàn)譯碼階段的EXE_MUL指令的回寫(xiě)地址填錯(cuò)了,已修正;
項(xiàng)目鏈接
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的自己动手写CPU(5)简单算术操作指令实现_1的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 自己动手写CPU(4)移动操作指令的实现
- 下一篇: 自己动手写CPU(6)流水线暂停、乘累加