日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

自己动手写CPU(5)简单算术操作指令实现_1

發布時間:2023/12/19 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 自己动手写CPU(5)简单算术操作指令实现_1 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

自己動手寫CPU(5)簡單算數操作指令實現_1

指令介紹

MIPS32指令集架構定義的所有算術操作指令,共有21條 共有三類,分別是:

  • 簡單算術指令
  • 乘累加、乘累減指令
  • 除法指令

算術指令操作介紹

一共有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類,而且指令的第610bit都是0,根據指令的功能碼(05bit)來判斷是哪一條指令

  • add(功能碼6’b100000):加法運算,用法:add rd,rs,rt;作用:rd <- rs+rt,將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值進行加法運算,結果保存到地址為rd的通用寄存器中。如果加法運算溢出,那么會產生溢出異常,同時不保存結果。
  • addu(功能碼是6’b100001):加法運算,用法:addu rd,rs,rt; 作用:rd <-rs+rd,將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值進行加法運算,結果保存到rd的通用寄存器中。不進行溢出檢查,總是將結果保存到目的寄存器。
  • SUB(功能碼是6’b100010):減法運算,用法:sub rd,rs,rt; 作用:rd <- rs-rt,將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值進行減法運算,結果保存到地址為rd的通用寄存器中。如果減法運算溢出,那么產生溢出異常,同時不保存結果。
  • SUBU(功能碼是6’b100011):減法運算,用法:subu rd,rs,rt; 作用:rd <- rs-rt將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值進行減法運算,結果保存到地址為rd的通用寄存器中。不進行溢出檢查,總是將結果保存到目的寄存器。
  • SLT(功能碼是6’b101010):比較運算,用法:slt rd,rs,rt; 作用:rd <- (rs<rt)將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值按照有符號數進行比較,若前者小于后者,那么將1保存到地址為rd的通用寄存器,若前者大于后者,則將0保存到地址為rd的通用寄存器中。
  • SLTU(功能碼是6’b101011):比較運算,用法:sltu rd,rs,rt; 作用:rd <- (rs<rt)將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值按照無符號數進行比較,若前者小于后者,那么將1保存到地址為rd的通用寄存器,若前者大于后者,則將0保存到地址為rd的通用寄存器中。

2. addi、addiu、slti、sltiu指令

我們由指令格式可以看出,依據指令碼(26~31bit)判斷是哪一種指令

  • ADDI(指令碼是6’b001000):加法運算,用法:addi rt,rs,immediate; 作用:rt <- rs+(sign_extended)immediate,將指令中16位立即數進行符號擴展,與地址為rs的通用寄存器進行加法運算,結果保存到地址為rt的通用寄存器。如果加法運算溢出,則產生溢出異常,同時不保存結果。
  • ADDIU(指令碼是6’b001001):加法運算,用法:addiu rt,rs,immediate; 作用:rt <- rs+(sign_extended)immediate,將指令中16位立即數進行符號擴展,與地址為rs的通用寄存器進行加法運算,結果保存到地址為rt的通用寄存器。不進行溢出檢查,總是將結果保存到目的寄存器。
  • SLTI(功能碼是6’b001010):比較運算,用法:slti rt,rs,immediate; 作用:rt <- (rs<(sign_extended)immediate)將指令中的16位立即數進行符號擴展,與地址為rs的通用寄存器的值按照有符號數進行比較,若前者小于后者,那么將1保存到地址為rt的通用寄存器,若前者大于后者,則將0保存到地址為rt的通用寄存器中。
  • SLTIU(功能碼是6’b001011):比較運算,用法:sltiu rt,rs,immediate; 作用:rt <- (rs<(sign_extended)immediate)將指令中的16位立即數進行符號擴展,與地址為rs的通用寄存器的值按照無符號數進行比較,若前者小于后者,那么將1保存到地址為rt的通用寄存器,若前者大于后者,則將0保存到地址為rt的通用寄存器中。

3. clo、clz指令

由指令格式可以看出,這兩條指令的指令碼(2631bit)都是6’b011100,即是SPECIAL2類;而且第610bit都為0,根據指令中的功能碼(0~5bit)判斷是哪一條指令。

  • CLZ(功能碼是6’b100000):計數運算,用法:clz rd,rs; 作用:rd <- coun_leading_zeros rs,對地址為rs的通用寄存器的值,從最高位開始向最低位方向檢查,直到遇到值為“1”的位,將該為之前“0”的個數保存到地址為rd的通用寄存器中,如果地址為rs的通用寄存器的所有位都為0(即0x00000000),那么將32保存到地址為rd的通用寄存器中。
  • CLO(功能碼是6’b100001):計數運算,用法:clo,rd,rs; 作用:rd <- coun_leading_zeros rs對地址為rs的通用寄存器的值,從最高位開始向最低位方向檢查,直到遇到值為“0”的位,將該為之前“1”的個數保存到地址為rd的通用寄存器中,如果地址為rs的通用寄存器的所有位都為1(即0xFFFFFFFF),那么將32保存到地址為rd的通用寄存器中。

4. multu、mult、mul指令

由指令格式可以看出,mul指令的指令碼(2631bit)都是6’b011100,即是SPECIAL2類,mult和multu這兩條指令的指令碼(2631bit)都是6’b000000,即是SPECIAL類;有著不同的功能碼(0~5bit)

  • mul(指令碼是SPECIAL2,功能碼是6’b000010):乘法運算,用法:mul,rd,rs,st; 作用:rd <- rs * rt,將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值作為有符號數相乘,乘法結果的低32bit保存到地址為rd的通用寄存器中。
  • mult(指令碼是SPECIAL,功能碼是6’b011000):乘法運算,用法:mult,rs,st; 作用:{hi,lo} <- rs * rt,將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值作為有符號數相乘,乘法結果低32bit保存到LO寄存器中,高32bit保存到HI寄存器中。
  • multu(指令碼是SPECIAL,功能碼是6’b011001):乘法運算,用法:mult,rs,st; 作用:{hi,lo} <- rs * rt,將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值作為無符號數相乘,乘法結果低32bit保存到LO寄存器中,高32bit保存到HI寄存器中。

修改之處

譯碼階段

  • add、addu、sub、subu、slt、sltu:需要兩個寄存器的值分別作為兩個操作數,所以設置reg1_read_o和reg2_read_o都為1,運算完后結果需要寫入目的寄存器,所以設置wreg_o為WriteEnable,寫入目的寄存器地址wd_o是指令中16~20bit的值。
  • addi、addiu、subi、subiu:只需要讀取一個寄存器的值作為第一個操作數,即設置reg1_read_o為1,reg2_read_o為0,第二個操作數為立即數進行符號擴展后的值,運算完后結果需要寫入目的寄存器,所以設置wreg_o為WriteEnable,寫入目的寄存器地址wd_o是指令中16~20bit的值。
  • mult、multu:需要兩個寄存器的值分別作為兩個操作數,所以設置reg1_read_o和reg2_read_o都為1,運算完后結果需要不需要寫入通用寄存器,而是寫入HI、LO寄存器所以設置wreg_o為WriteDisable。
  • mul:需要兩個寄存器的值分別作為兩個操作數,所以設置reg1_read_o和reg2_read_o都為1,aluop_o為EXE_MUL_OP運算完后結果需要寫入目的寄存器,所以設置wreg_o為WriteEnable,寫入目的寄存器地址wd_o是指令中11~15bit的值。
  • clo、clz:只需要讀取一個寄存器的值作為第一個操作數,即設置reg1_read_o為1,reg2_read_o為0,運算完后結果需要寫入目的寄存器,所以設置wreg_o為WriteEnable,寫入目的寄存器地址wd_o是指令中11~15bit的值。

執行階段

根據譯碼階段的結果,來進行相關的執行操作

1.添加一些新的相關變量

reg[`RegBus] arithmeticres; //保存算術運算結果 wire ov_sum; //保存溢出情況 wire reg1_eq_reg2; //第一個操作數是否等于第二個操作數 wire reg1_lt_reg2; //第一個操作數是否小于第二個操作數 wire[`RegBus] reg2_i_mux; //保存輸入的第二個操作reg2_i的補碼 wire[`RegBus] reg1_i_not; //保存輸入的第一個操作數reg1_i取反后的值 wire[`RegBus] result_sum; //保存加法結果 wire[`RegBus] opdata1_mult; //乘法操作中的被乘數 wire[`RegBus] opdata2_mult; //乘法操作中的乘數 wire[`DoubleRegBus] hilo_temp; //臨時保存乘法結果,寬度為64位 reg[`DoubleRegBus] mulres; //保存乘法結果,寬度為64位

2.計算相關變量的值

2.1 reg2_i_mux

如果是減法或者有符號比較運算,那么reg2_i_mux等于第二個操作數reg2_i的補碼,否則reg2_i_mux等于第二個操作數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

  • 如果是加法運算,此時reg2_i_mux就是第二個操作數reg2_i,所以result_sum就是加法運算的結果
  • 如果是減法運算,此時reg2_i_mux是第二個操作數reg2_i的補碼,所以result_sum就是減法運算的結果
  • 如果是有符號比較運算,此時reg2_i_mux也是第二個操作數reg2_i的補碼,所以result_sum也是減法運算的結果,可以通過判斷減法結果是否小于零,進而判斷第一個操作數reg1_i是否小于第二個操作數reg2_i
assign result_sum = reg1_i + reg2_i_mux;

2.3 ov_sum

計算是否溢出,加法指令(add和addi)、減法指令(sub)執行的時候,需要判斷是否溢出,滿足一下兩種情況時,有溢出:

  • reg1_i為正數,reg2_i_mux為正數,但是兩者之和為負數
  • reg1_i為負數,reg2_i_mux為負數,但是兩者之和為正數
assign ov_sum = ((!reg1_i[31] && !reg2_i_mux[31]) && result_sum[31]) ||((reg1_i[31] && reg2_i_mux[31]) && (!result_sum[31]));

2.4 reg1_lt_reg2

計算操作數1是否小于操作數2,分兩種情況

  • aluop_i為EXE_SLT_OP表示有符號比較運算:

    reg1_i為負數、reg2_i為正數,顯然reg1_i小于reg2_i

    reg1_i為正數、reg2_i為正數,并且reg1_i減去reg2_i的值小于0(即result_sum為負),此時也有reg1_i小于reg2_i

    reg1_i為負數、reg2_i為負數,并且并且reg1_i減去reg2_i的值小于0(即result_sum為負),此時也有reg1_i小于reg2_i

  • 無符號數比較的時候u,直接使用比較運算符比較reg1_i與reg2_i

assign reg1_lt_reg2 = ((aluop_i == `EXE_SLT_OP)) ? ((reg1_i[31] && !reg2_i[31]) || (!reg1_i[31] && !reg2_i[31] && result_sum[31]) || (reg1_i[31] && reg2_i[31] && result_sum[31])) : (reg1_i < reg2_i);

2.5 reg1_i_not

對操作數1逐位取反,賦給reg1_i_not

assign reg1_i_not = ~reg1_i;

仿真結果

1. 測試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,無符號加法ori $3,$0,0x0000 # $3 = 0x00000000add $3,$2,$1 # $2 加 $1,有符號加法,結果溢出,$3保持不變sub $3,$1,$3 # $3 = 0x80000010 $1減去$3,有符號減法subu $3,$3,$2 # $3 = 0xF $3減去$2,無符號減法addi $3,$3,2 # $3 = 0x11 $3 加2,有符號加法ori $3,$0,0x0000 # $3 = 0x00000000addiu $3,$3,0x8000 # $3 = 0xffff8000 $3加0xffff8000 無符號加法

2. 測試slt、sltu、slti、sltiu

or $1,$0,0xffff # $1 = 0xffffsll $1,$1,16 # $1 = 0xffff0000 給$1賦值slt $2,$1,$0 # $2 = 1 比較$1與0x0,有符號比較sltu $2,$1,$0 # $2 = 0 比較$1與0x0,無符號比較slti $2,$1,0x8000 # $2 = 1 比較$1與0xffff8000,有符號比較sltiu $2,$1,0x8000 # $2 = 1 比較$1與0xffff8000,無符號比較

3. 測試clo和clz指令

lui $1,0x0000 # $1 = 0x00000000 給$1賦值clo $2,$1 # $2 = 0x00000000 統計$1中“1”之前“0”的個數clz $2,$1 # $2 = 0x00000020 統計$1中“0”之前“1”的個數lui $1,0xffff # $1 = 0xffff0000ori $1,$1,0xffff # $1 = 0xffffffff 給$1賦值clz $2,$1 # $2 = 0x00000000 統計$1中“1”之前“0”的個數clo $2,$1 # $2 = 0x00000020 統計$1中“0”之前“1”的個數lui $1,0xa100 # $1 = 0xa1000000 給$1賦值clz $2,$1 # $2 = 0x00000000 統計$1中“1”之前“0”的個數clo $2,$1 # $2 = 0x00000001 統計$1中“0”之前“1”的個數lui $1,0x1100 # $1 = 0x11000000 給$1賦值clz $2,$1 # $2 = 0x00000003 統計$1中“1”之前“0”的個數clo $2,$1 # $2 = 0x00000000 統計$1中“0”之前“1”的個數

4. 測試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,有符號乘法,結果低32位保存到$3mult $1,$2 # hi = 0xffffffff # lo = 0xffffffe2# $1 乘以$2,有符號乘法,結果低32位保存到HI LOmultu $1,$2 # hi = 0x5# lo = 0xffffffe2# $1 乘以$2,無符號乘法,結果低32位保存到HI LOnopnop

實驗心得

1、ov_sum是否溢出信號需在執行階段最后選擇運算結果時進行判斷,如果發生溢出行為則將寫寄存器堆的信號取消使能;

2、mult和multu指令均不對寄存器堆進行回寫操作,僅改變特殊寄存器HI和LO的值,所以譯碼階段這兩個指令的alusel_o信號均需設置成EXE_RES_NOP,不對寄存器堆進行回寫操作;

3、由仿真的波形圖發現譯碼階段的EXE_MUL指令的回寫地址填錯了,已修正;

項目鏈接

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的自己动手写CPU(5)简单算术操作指令实现_1的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。