iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0

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指令解碼

https://ithelp.ithome.com.tw/upload/images/20231004/2016243656jAeGNLb4.png

JAL及JALR的格式如下,
https://ithelp.ithome.com.tw/upload/images/20231004/20162436m3u1L8yhWa.png
JALR是I-type,跟我們之前看過的addi等指令接近,只要將rs內的值讀出加上imm即可,而JAL的格式較為特別,
會將前面的20bit拼湊出一個imm進行跳轉。

https://ithelp.ithome.com.tw/upload/images/20231004/20162436r7uUXoVeEj.png
而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;
            }

https://ithelp.ithome.com.tw/upload/images/20231004/20162436dZfysVu1tW.png
通過測試,送出。


碎碎念:只要貼程式碼就會變超長現象,不過因為是實作環節所以好像也沒辦法,明天來處理Load/store指令


上一篇
Day-18 RISC-V I-type ALU implement
下一篇
Day 20 - Load/Store實作,用來與記憶體互動的指令們
系列文
從零開始的RISC-V ISA Simulator (Another Little RISC-V ISA Simulator)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言