昨天完成了R-Type的ALU,今天要來實作I-type。
I-type ALU
I-type跟R-type的區別在於說R-type一個是以兩個暫存器內的值做運算,而I-type則是以暫存器的值加上一個立即值來做運算,但整體來說是相當接近的,decode上在func3的欄位也相同,可以重複利用昨天的code。
不過要注意,由於[31:20]被immediate值使用,因此像sub等指令是沒有I-type的,
總之今天也來實作吧。
我們可以重複利用昨天的test,只要少掉一個暫存內的值即可,同樣以addi作為範例。
/*ISA Test*/
TEST(ISATESTSuite, ADDI0)
{
ALISS::reg[11] = 3;
//ALISS::reg[12] = 7;
uint32_t insn = 0x00758513; // addi a0 a1 7
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::reg[10], 0xa); //3 + 7 = 10;
}
TEST(ISATESTSuite, ADDI1)
{
ALISS::reg[10] = 0xffffffffffffffff;
//ALISS::reg[12] = 0x1;
uint32_t insn = 0x00150593; // addi a1 a0 1
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::reg[11], 0); //1 + -1 = 0;
}
而I-type的最後7bit是0x13,我們可以重複利用昨天的code。
比較要注意的是因為imm只有12bit,但有些指令的imm是有號的,因此要做sign extension,我們簡單實作sign extension程式如下,將輸入結果左移到64bit再右移回原本的位元數。
uint64_t sext(uint64_t input, int len) { return int64_t(input) << (64 - len) >> (64 - len); }
完成 sign extension後,我們實作ADDI如下,將R-type取rs2的地方改為取imm,
case 0x13:
{
uint64_t rd = ((insn >> 7) & 0x1f);
uint64_t rs1 = ((insn >> 15) & 0x1f);
uint64_t imm = ((insn >> 20) & 0xfff);
switch ((insn >> 12) & 7)
{
case 0x0: //ADDI
{
reg[rd] = reg[rs1] + sext(imm, 12);
break;
}
...
..
.
}
break;
}
...
..
.
另外還有shift相關指令如SLLI,SRAI,SRLI等,並不會將完整的[31:20]當成immediate (shift超過64bit是沒必要的),相反的是以[24:20]作為shift amount,因此要特別處理
uint64_t shamt = imm & 0x1f; //[24:20]
case 0x1: //SLLI
{
uint64_t shamt = imm & 0x1f; //[24:20]
reg[rd] = reg[rs1] << shamt;
break;
}
之後就可以直接仿照昨天實作的R-type的decode,將I-type的指令全部實現啦~
case 0x0: //ADDI
{
reg[rd] = reg[rs1] + sext(imm, 12);
break;
}
case 0x1: //SLLI
{
uint64_t shamt = imm & 0x1f; //[24:20]
reg[rd] = reg[rs1] << shamt;
break;
}
case 0x2: //SLTI
{
reg[rd] = ((int64_t)reg[rs1] < sext(imm, 12));
break;
}
case 0x3: //SLTUI
{
reg[rd] = (reg[rs1] < imm);
break;
}
case 0x4: //XORI
{
reg[rd] = (reg[rs1] ^ sext(imm, 12));
break;
}
case 0x5: //SRLI & SRAI
{
uint64_t shamt = imm & 0x1f; //[24:20]
switch ((insn >> 25) & 0x7f)
{
case 0x0 : //SRLI
{
reg[rd] = reg[rs1] >> shamt;
break;
}
case 0x20 : //SRAI
{
reg[rd] = (int64_t)reg[rs1] >> shamt;
break;
}
default:
{
printf("Illegal instruction");
printf("%x\n",insn);
break;
}
}
break;
}
case 0x6: //ORI
{
reg[rd] = (reg[rs1] | sext(imm, 12));
break;
}
case 0x7: //ANDI
{
reg[rd] = (reg[rs1] & sext(imm, 12));
break;
}
並同樣確認test都通過,送出。
碎碎念:以為有R-type的經驗I-type做起來會輕鬆很多,結果踩到sext的坑,明天來實作CTRL指令