今天要來實作R-type ALU,首先我們從RISC-V test裡面挑幾個來當成測資,由於步驟較為繁瑣,這邊簡單的以ADD為範例。
ADD Test
/*ISA Test*/
TEST(ISATESTSuite, ADD0)
{
ALISS::reg[11] = 3;
ALISS::reg[12] = 7;
uint32_t insn = 0x00c58533; // add a0 a1 a2
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::reg[10], 0xa); //3 + 7 = 10;
}
TEST(ISATESTSuite, ADD1)
{
ALISS::reg[10] = 0xffffffffffffffff;
ALISS::reg[12] = 0x1;
uint32_t insn = 0x00c505b3; // add a1 a0 a2
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::reg[11], 0); //;
}
這裡取一個基本的3 + 7 = 10,以及-1 + 1等比較特別的case來進行測試。
Decode實作
RISC-V指令會根據最後7bit判斷是什麼指令,如附圖。
ALU等指令會先判斷出是屬於OP (R-Type)還是OP-imm (I-Type),OP的話最後7bit為0x33 (0110011),判斷出為OP後,要再判斷是哪一種指令,此時會參考14:12 bit (funct3)以及31:25 (funct7)的結果來定位屬於什麼指令,例如說ADD的funct3為000,而funct7也為0000000 (通常會先看funct3,原因是同樣的指令在不同format的funct3會相同,如ADD/ADDI,而I format沒有funct7)。
結合上面內容可以寫出下列程式碼。
uint8_t decode = insn & 0x7f; //last 7 bit
switch(decode)
{
case 0x33:
{
uint64_t rd = ((insn >> 7) & 0x1f);
uint64_t rs1 = ((insn >> 15) & 0x1f);
uint64_t rs2 = ((insn >> 20) & 0x1f);
switch ((insn >> 12) & 7)
{
case 0x0: //ADD or SUB
{
switch ((insn >> 25) & 0x7f)
{
case 0x0 : //ADD
{
reg[rd] = reg[rs1] + reg[rs2];
break;
}
default:
{
printf("no insn or not implement");
break;
}
}
break;
}
default:
{
printf("no insn or not implement");
break;
}
}
break;
}
default:
{
printf("no insn or not implement");
break;
}
}
確認以上程式碼可以通過測試,ADD實現完成,而其他ALU指令也是依樣畫葫蘆,最終的結果如下。
R-Type OP Decode Implement
case 0x33:
{
uint64_t rd = ((insn >> 7) & 0x1f);
uint64_t rs1 = ((insn >> 15) & 0x1f);
uint64_t rs2 = ((insn >> 20) & 0x1f);
switch ((insn >> 12) & 7)
{
case 0x0: //ADD or SUB
{
switch ((insn >> 25) & 0x7f)
{
case 0x0 :
{
reg[rd] = reg[rs1] + reg[rs2];
break;
}
case 0x20 :
{
reg[rd] = reg[rs1] - reg[rs2];
break;
}
default:
{
printf("no insn or not implement :");
printf("%d\n",(insn >> 25) & 0x7f);
break;
}
}
break;
}
case 0x1: //SLL
{
reg[rd] = reg[rs1] << reg[rs2];
break;
}
case 0x2: //SLT
{
reg[rd] = ((int64_t)reg[rs1] < (int64_t)reg[rs2]);
break;
}
case 0x3: //SLTU
{
reg[rd] = (reg[rs1] < reg[rs2]);
break;
}
case 0x4: //XOR
{
reg[rd] = reg[rs1] ^ reg[rs2];
break;
}
case 0x5: //SRL or SRA
{
switch ((insn >> 25) & 0x7f)
{
case 0x0 : //SRL
{
reg[rd] = reg[rs1] >> reg[rs2];
break;
}
case 0x20 : //SRA
{
reg[rd] = (int64_t)reg[rs1] >> reg[rs2];
break;
}
default:
{
printf("no insn or not implement :");
printf("%d\n",(insn >> 25) & 0x7f);
break;
}
}
break;
}
case 0x6: //OR
{
reg[rd] = reg[rs1] | reg[rs2];
break;
}
case 0x7: //AND
{
reg[rd] = reg[rs1] & reg[rs2];
break;
}
default:
{
printf("no insn or not implement");
break;
}
}
break;
}
default:
{
printf("no insn or not implement");
break;
}
並且每一個ALU的OP Code都有先完成對應的Test,並在實現後全數通過,送出。
碎碎念:今天主要是Coding,看著一個一個指令實現並通過測試還蠻有成就感的~