iT邦幫忙

2023 iThome 鐵人賽

DAY 27
0
Software Development

從零開始的RISC-V ISA Simulator (Another Little RISC-V ISA Simulator)系列 第 27

Day 27 - RISC-V M Extension , 簡單來說...就是乘除

  • 分享至 

  • xImage
  •  

RISC-V M Extension

與昨天介紹的A Extension不同,M Extension很好理解,就是乘法和除法的指令。
之前在介紹I-instruction時,有些人可能會好奇為什麼裡面沒有乘法和除法呢,原因其實也很簡單,因為乘除不是必要的。對於編譯器來說,可以透過拆解成多個加減等運算來進行乘法或除法。但想當然如果硬體本身有支援乘除的運算資源,使用乘法和除法指令必定是較高效的選擇。

RISC-V M Extension共分成乘(MUL)、除(DIV)、餘(REM)三類,而又根據有號無號及bit數的不同,可整理成下表。

指令 行為
mul 兩暫存視為有號相乘後,將較低的64bit放入另一暫存。
mulw 兩暫存視為有號相乘後,取較低的32bit做sign-extension後放入另一暫存。
mulh 兩暫存視為有號相乘後,將較高的64bit放入另一暫存。
mulhsu 將一暫存視為有號數,另一暫存視為無號數,相乘後,將較高的64bit放入另一暫存。
mulhu 兩暫存視為無號相乘後,將較高的64bit放入另一暫存。
div 兩暫存視為有號相除後,放入另一暫存。
divw 兩暫存視為有號相除後,取較低的32bit做sign-extension後放入另一暫存。
divu 兩暫存視為無號相除後,放入另一暫存。
divuw 兩暫存視為無號相除後,取較低的32bit做sign-extension後放入另一暫存。
rem 兩暫存視為有號取餘後,放入另一暫存。
remw 兩暫存視為有號取餘後,取較低的32bit做sign-extension後放入另一暫存。
remu 兩暫存視為無號取餘後,放入另一暫存。
remuw 兩暫存視為無號取餘後,取較低的32bit做sign-extension後放入另一暫存。

RISC-V M Extension 測試

測試的部分,MUL High part我們都直接將乘數和被乘數左移32bit即可,如下,
一些corner case如除零等,我們明天讓RISC-V Test直接跑起來可以驗到,這邊先偷懶一下> <

TEST(ISATESTSuiteMPU, MUL_11_N13)
{
    ALISS::reg[10] = 0x0;
    ALISS::reg[11] = 11;
    ALISS::reg[12] = -13;
    uint32_t insn = 0x02c58533; // mul a0, a1, a2
    ALISS::ID_EX_WB(insn);
    EXPECT_EQ(ALISS::reg[10], 143); //11 * 13 = -143;
}

TEST(ISATESTSuiteMPU, MULH_11_13)
{
    ALISS::reg[10] = 0x0;
    ALISS::reg[11] = 11 << 32;
    ALISS::reg[12] = 13 << 32;
    uint32_t insn = 0x02c59533; // mulh a0, a1, a2
    ALISS::ID_EX_WB(insn);
    EXPECT_EQ(ALISS::reg[10], 143); //11 * 13 = 143;
}

TEST(ISATESTSuiteMPU, DIV_10_5)
{
    ALISS::reg[10] = 0x0;
    ALISS::reg[11] = 10;
    ALISS::reg[12] = 5;
    uint32_t insn = 0x02c5c533; // div a0, a1, a2
    ALISS::ID_EX_WB(insn);
    EXPECT_EQ(ALISS::reg[10], 2); //10 / 5 = 2;
}

TEST(ISATESTSuiteMPU, REM_10_5)
{
    ALISS::reg[10] = 0x0;
    ALISS::reg[11] = 11;
    ALISS::reg[12] = 5;
    uint32_t insn = 0x02c5e533; // rem a0, a1, a2
    ALISS::ID_EX_WB(insn);
    EXPECT_EQ(ALISS::reg[10], 1); //11 % 5 = 1;
}

RISC-V M Extension 實做

實作的部分要特別注意因為要取High 64bit,所以不能直接用C++的乘法,或者你要有128bit的資料型態。
我們這邊參考Spike的作法,分成High part和low part去實現mulh等function。

 //ref : https://github.com/riscv-software-src/riscv-isa-sim

    inline uint64_t mulhu(uint64_t a, uint64_t b)
    {
      uint64_t t;
      uint32_t y1, y2, y3;
      uint64_t a0 = (uint32_t)a, a1 = a >> 32;
      uint64_t b0 = (uint32_t)b, b1 = b >> 32;

      t = a1*b0 + ((a0*b0) >> 32);
      y1 = t;
      y2 = t >> 32;

      t = a0*b1 + y1;

      t = a1*b1 + y2 + (t >> 32);
      y2 = t;
      y3 = t >> 32;

      return ((uint64_t)y3 << 32) | y2;
    }

    inline int64_t mulh(int64_t a, int64_t b)
    {
      int negate = (a < 0) != (b < 0);
      uint64_t res = mulhu(a < 0 ? -a : a, b < 0 ? -b : b);
      return negate ? ~res + (a * b == 0) : res;
    }

    inline int64_t mulhsu(int64_t a, uint64_t b)
    {
      int negate = a < 0;
      uint64_t res = mulhu(a < 0 ? -a : a, b);
      return negate ? ~res + (a * b == 0) : res;
    }

    ///
    
    

之後只要以上面三個function去實現各指令的功能即可,且要注意如除零或者是INT_MIN/-1等特例,成果如下。


case 0x33: //OP
{
    uint64_t rd = ((insn >> 7) & 0x1f);
    uint64_t rs1 = ((insn  >> 15) & 0x1f);
    uint64_t rs2 = ((insn  >> 20) & 0x1f);
    if(((insn >> 25) & 0x7f) == 0x1) //M-extension
    {
        switch ((insn >> 12) & 7)
        {
            case 0x0: //MUL
            {
                reg[rd] = (int64_t)reg[rs1] * (int64_t)reg[rs2];

                break;
            }
            case 0x1: //MULH
            {
                reg[rd] = mulh(reg[rs1],reg[rs2]);
                break;
            }
            case 0x2: //MULHSU
            {
                reg[rd] = mulhsu(reg[rs1],reg[rs2]);
                break;
            }
            case 0x3: //MULHU
            {
                reg[rd] = mulhu(reg[rs1],reg[rs2]);
                break;
            }
            case 0x4: //DIV
            {
                if(reg[rs2] == 0) //can't div 0
                {
                    reg[rd] = -1;
                }
                else if((int64_t)reg[rs1] == INT64_MIN && (int64_t)reg[rs2] == -1) // may overflow
                {
                    reg[rd] = reg[rs1];
                }
                else
                {
                    reg[rd] = (int64_t)reg[rs1] / (int64_t)reg[rs2];
                }
                break;
            }
            case 0x5: //DIVU
            {
                if(reg[rs2] == 0) //can't div 0
                {
                    reg[rd] = -1;
                }
                else
                {
                    reg[rd] = reg[rs1] / reg[rs2];
                }
                break;
            }
            case 0x6: //REM
            {
                if(reg[rs2] == 0) //can't div 0
                {
                    reg[rd] = reg[rs1];
                }
                else if((int64_t)reg[rs1] == INT64_MIN && (int64_t)reg[rs2] == -1) // may overflow
                {
                    reg[rd] = 0;
                }
                else
                {
                    reg[rd] = (int64_t)reg[rs1] % (int64_t)reg[rs2];
                }
                break;
            }
            case 0x7: //REMU
            {
                if(reg[rs2] == 0) //can't div 0
                {
                    reg[rd] = reg[rs1];
                }
                else
                {
                    reg[rd] = reg[rs1] % reg[rs2];
                }
                break;
            }
            default:
            {
                 printf("Illegal instruction");
                 printf("%x\n",insn);
                 break;
            }
        }

    }
    else // op part
    ...
    ..
    .

確認測試通過,送出,到目前為止我們終於把所有指令都完成了,接下來就可以開始跑RISC-V Test,並讓Simulator動起來了 (可能還要補一些倒Log的系統)


碎碎念 : 終於完成IMA的所有指令了,這個系列也進入尾聲啦,希望明天跑test能順利,這樣就只剩下Linux了...


上一篇
Day 26 - RISC-V Atomic指令,確保數據一致的指令
下一篇
Day 28 - 真正執行risc-v test、跑過第一支elf、及regression環境
系列文
從零開始的RISC-V ISA Simulator (Another Little RISC-V ISA Simulator)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言