RISC-V CTRL INSN
好低,今天來實作CTRL指令。
CTRL指令指的是在程式運行過程中控制程式流程的指令,例如說像分支(branch),就是在你寫程式的時候寫出if else的時候會翻成的指令,CPU碰到該指令時會決定下一道指令的PC位置。
CTRL指令可以分成前述的Branch以及Jump,兩者的差異在於說Branch是條件分支,不一定會有PC的跳轉(即下一道指令不是PC + 4的狀況),而Jump則是無論如何都要進行跳轉。
CTRL指令並沒有很多,分別有下列幾個。
指令 | 行為 |
---|---|
jal (jump and link) |
跳到PC加上立即值的位置,並將return address放到暫存內J-Type。 |
jalr |
跳到暫存內讀出值加上立即值的位置,並將return address放到暫存內,I-Type。 |
beq |
如果兩暫存相同,則跳到PC加上立即值的位置。 |
bne |
如果兩暫存不相同,則跳到PC加上立即值的位置。 |
blt |
如果暫存a小於暫存b(有號),則跳到PC加上立即值的位置。 |
bge |
如果暫存a大於等於暫存b(有號),則跳到PC加上立即值的位置。 |
bltu |
如果暫存a小於暫存b(無號),則跳到PC加上立即值的位置。 |
bgeu |
如果暫存a大於等於暫存b(無號),則跳到PC加上立即值的位置。 |
CTRL指令解碼
JAL及JALR的格式如下,
JALR是I-type,跟我們之前看過的addi等指令接近,只要將rs內的值讀出加上imm即可,而JAL的格式較為特別,
會將前面的20bit拼湊出一個imm進行跳轉。
而Branch等B-Type等雖然條件不同,但跳轉位置都是以[31:25]及[11:7]拼湊出imm進行跳轉。
JAL、JALR指令實作
由於CTRL指令都會改變next-pc的值,我們將next-pc = pc + 4 寫在decode function的頭,這樣後續遇到CTRL指令時可以直接改變next-pc位置。
測試程式如下,兩者的行為相同,這樣可以方便我們驗證。
TEST(ISATESTSuite, JALR_0x100_0x50)
{
ALISS::pc = 0x100;
ALISS::reg[10] = 0x0;
ALISS::reg[11] = 0x100;
uint32_t insn = 0xf8058567; // jalr a0, -128(a1)
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::reg[10], 0x104 ); //return address = 104;
EXPECT_EQ(ALISS::next_pc, 0x80 ); //pc = 0x80;
}
TEST(ISATESTSuite, JAL_0x100_0x50)
{
ALISS::pc = 0x100;
ALISS::reg[10] = 0x0;
uint32_t insn = 0xf81ff56f; // jal a0, -128
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::reg[10], 0x104 ); //return address = 104;
EXPECT_EQ(ALISS::next_pc, 0x80 ); //pc = 0x80;
}
而實作程式碼如下,除了JAL的imm值要去拚湊之外其他沒有什麼問題。
case 0x67: //JALR
{
uint64_t rd = ((insn >> 7) & 0x1f);
uint64_t rs1 = ((insn >> 15) & 0x1f);
uint64_t imm = ((insn >> 20) & 0xfff);
reg[rd] = next_pc;
next_pc = reg[rs1] + sext(imm,12);
break;
}
case 0x6f: //JAL
{
uint64_t rd = ((insn >> 7) & 0x1f);
uint64_t imm = (((insn & 0x80000000) >> 11) | // [20]
((insn & 0x7fe00000)>> 20) | // [10:1]
((insn & 0x00100000) >> 9 ) | // [11]
((insn & 0x000ff000))); //[19:12]
reg[rd] = next_pc;
next_pc = pc + sext((imm),21);
break;
}
Branch指令實作
Branch的[6:0]相同皆為,差別在於[14:12]的func3段。
我們一樣先設計測試程式,每個測試的跳轉位置相同,差別只在於條件不一樣。
測試
TEST(ISATESTSuite, BEQ_0x100_N128)
{
ALISS::pc = 0x100;
ALISS::reg[10] = 0x1234;
ALISS::reg[11] = 0x1234;
uint32_t insn = 0xf8b500e3; // beq a0, a1, -128
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::next_pc, 0x80 ); //pc = 0x80;
}
TEST(ISATESTSuite, BNE_0x100_N128)
{
ALISS::pc = 0x100;
ALISS::reg[10] = 0x1234;
ALISS::reg[11] = 0x5678;
uint32_t insn = 0xf8b510e3; // beq a0, a1, -128
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::next_pc, 0x80 ); //pc = 0x80;
}
TEST(ISATESTSuite, BLT_0x100_N128)
{
ALISS::pc = 0x100;
ALISS::reg[10] = 0x1234;
ALISS::reg[11] = 0x5678;
uint32_t insn = 0xf8b540e3; // beq a0, a1, -128
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::next_pc, 0x80 ); //pc = 0x80;
}
TEST(ISATESTSuite, BGE_0x100_N128)
{
ALISS::pc = 0x100;
ALISS::reg[10] = 0x1234;
ALISS::reg[11] = 0x1234;
uint32_t insn = 0xf8b550e3; // beq a0, a1, -128
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::next_pc, 0x80 ); //pc = 0x80;
}
TEST(ISATESTSuite, BLTU_0x100_N128)
{
ALISS::pc = 0x100;
ALISS::reg[10] = 0x5678;
ALISS::reg[11] = -1;
uint32_t insn = 0xf8b560e3; // beq a0, a1, -128
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::next_pc, 0x80 ); //pc = 0x80;
}
TEST(ISATESTSuite, BGEU_0x100_N128)
{
ALISS::pc = 0x100;
ALISS::reg[10] = -1;
ALISS::reg[11] = 0x1234;
uint32_t insn = 0xf8b570e3; // beq a0, a1, -128
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::next_pc, 0x80 ); //pc = 0x80;
}
實作上一樣要考慮如何拼湊出imm,除此之外只要注意有號無號及sext時要以13bit為單位進行即可。
case 0x63: //Branch
{
uint64_t rs1 = ((insn >> 15) & 0x1f);
uint64_t rs2 = ((insn >> 20) & 0x1f);
uint64_t imm = (((insn & 0xf00) >> 7) | //[4:1]
((insn & 0x7e000000) >> 20) | //[10:5]
((insn & 0x80) << 4) | //[11]
((insn & 0x80000000) >> 19));
switch ((insn >> 12) & 7)
{
case 0: //BEQ
{
if(reg[rs1] == reg[rs2])
next_pc = pc + sext(imm,13);
break;
}
case 1: //BNE
{
if(reg[rs1] != reg[rs2])
next_pc = pc + sext(imm,13);
break;
}
case 4: //BLT
{
if((int64_t)reg[rs1] < (int64_t)reg[rs2])
next_pc = pc + sext(imm,13);
break;
}
case 5: //BGE
{
if((int64_t)reg[rs1] >= (int64_t)reg[rs2])
next_pc = pc + sext(imm,13);
break;
}
case 6: //BLTU
{
if(reg[rs1] < reg[rs2])
next_pc = pc + sext(imm,13);
break;
}
case 7: //BGEU
{
if(reg[rs1] >= reg[rs2])
next_pc = pc + sext(imm,13);
break;
}
default:
{
printf("Illegal instruction");
printf("%x\n",insn);
break;
}
}
break;
}
通過測試,送出。
碎碎念:只要貼程式碼就會變超長現象,不過因為是實作環節所以好像也沒辦法,明天來處理Load/store指令