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了...