iT邦幫忙

2023 iThome 鐵人賽

DAY 26
0
Software Development

從零開始的RISC-V ISA Simulator (Another Little RISC-V ISA Simulator)系列 第 26

Day 26 - RISC-V Atomic指令,確保數據一致的指令

  • 分享至 

  • xImage
  •  

RISC-V A-extension

RISC-V的A-extension指的是atomic instruction extension,而atomic instruction指的是要確保該指令是不會被中斷的,也就是說可能要確保在該址令執行前的指令皆完成,又或者是該指令執行完之後才能執行額外的指令。主要目的為在多核心的系統中能夠確保資料的一致性。

而A-extension內分成兩大類,一類為LR/SC指令,另一類為AMO指令。

LR/SC指令指的是Load-Reserved/Store-Conditional,透過LR去標記特定的位址(Reserved),而在SC時會檢查該位址是否有被Reserved,如果沒有則判斷為錯誤,而只要有SC指令發生就會將原先的所有Reserve清空,來確保一次只會有一個SC發生。 (簡單來說,如果同時有兩個SC指令在不同顆core上發生,則core0碰完該位址後core1會store失敗。)

AMO指令則是可以針對記憶體位置進行運算,而只需要一道指令。在有AMO指令之前,我們如果要對一個記憶體位址做運算,會有以下程式碼。

//讀一個存在a1的值,寫到a0,同時做ori 0x1111,最後再寫回a1
lui a2  0x1111
lw a0 0(a1)
or a0 a0 a2
sw a0 0(a1)

而由於這是三道指令組成,指令間可能有中斷發生,又或者是在寫回前有人想要去碰a1這個位置,導致會有行為與預期不符的狀況。

而在amo指令下,我們可以直接這樣寫。

lui a2  0x1111
amoor a0 a1 a2

在我們讀取mem[reg[a1]],做or,寫回mem[reg[a1]]的這段時間,這個位址的值是不會改變的。

LR/SC測試

我們測試LR/SC功能,SC分為success及fail

TEST(ISATESTSuiteLRSC, LR_W)
{
    ALISS::memory = (uint8_t *)std::malloc(4 * 1024 * 1024); //4MB size for test
    uint64_t* memory64  = (uint64_t*)ALISS::memory;
    memory64[0x40000 / 8] = 0xffffffff88888888;
    ALISS::reg[10] = 0x0;
    ALISS::reg[11] = 0x40000;

    ALISS::reservation = false;
    uint32_t insn = 0x1005a52f; // lr a0, a1
    ALISS::ID_EX_WB(insn);
    EXPECT_EQ(ALISS::reg[10], -2004318072 ); //load -2004318072;
    EXPECT_EQ(ALISS::reservation, true);  
    
    free(ALISS::memory);
}

TEST(ISATESTSuiteLRSC, SC_W_FAIL)
{
    ALISS::memory = (uint8_t *)std::malloc(4 * 1024 * 1024); //4MB size for test
    uint64_t* memory64  = (uint64_t*)ALISS::memory;
 
    ALISS::reg[10] = 0x0;
    ALISS::reg[11] = 0xaaaa;
    ALISS::reg[12] = 0x40000;

    ALISS::reservation = false;
    uint32_t insn = 0x18b6252f; // sc a0, a1, a2
    ALISS::ID_EX_WB(insn);

    EXPECT_EQ(memory64[0x40000 / 8], 0 ); //0 // failure
    EXPECT_EQ(ALISS::reg[10], 1 ); //1 //failure

    free(ALISS::memory);
}

TEST(ISATESTSuiteLRSC, SC_W_SUCCESS)
{
    ALISS::memory = (uint8_t *)std::malloc(4 * 1024 * 1024); //4MB size for test
    uint64_t* memory64  = (uint64_t*)ALISS::memory;
 
    ALISS::reg[10] = 0x0;
    ALISS::reg[11] = 0xaaaa;
    ALISS::reg[12] = 0x40000;

    ALISS::reservation = true;
    uint32_t insn = 0x18b6252f; // sc a0, a1, a2
    ALISS::ID_EX_WB(insn);

    EXPECT_EQ(memory64[0x40000 / 8], 0xaaaa ); 
    EXPECT_EQ(ALISS::reg[10], 0 ); //0 //success

    free(ALISS::memory);
}

LR/SC實作
LR/SC的實作可以參考LW/SW,差別只在於沒有IMM,以及要對reservation做判斷而已。

case 0x2: //LR.W
{
    reg[rd] = sext(get_mem_w(reg[rs1]),32);
    reservation = true;
    break;
}
case 0x3: //SC.W
{
    if(reservation)
    {
        set_mem_w(reg[rs1], (uint32_t)reg[rs2]);
        reg[rd] = 0;
    }
    else
    {

        reg[rd] = 1;
    }
    reservation = false;
    break;
}

AMO實作

AMO實作上就是結合Load/Store,並將要Store回去的值進行運算,實現結果如下。

case 0x1: //AMOSWP.D
 {
     reg[rd] = get_mem_d(reg[rs1]);
     set_mem_d(reg[rs1], reg[rs2]);
     break;
 }
 case 0x0: //AMOADD.D
 {
     reg[rd] = get_mem_d(reg[rs1]);
     set_mem_d(reg[rs1], reg[rs2] + reg[rd]);
     break;
 }
 case 0x4: //AMOXOR.D
 {
     reg[rd] = get_mem_d(reg[rs1]);
     set_mem_d(reg[rs1], reg[rs2] ^ reg[rd]);
     break;
     }
 case 0xc: //AMOAND.D
 {
     reg[rd] = get_mem_d(reg[rs1]);
     set_mem_d(reg[rs1], reg[rs2] & reg[rd]);
     break;
 }
 case 0x8: //AMOOR.D
 {
     reg[rd] = get_mem_d(reg[rs1]);
     set_mem_d(reg[rs1], reg[rs2] | reg[rd]);
     break;
 }
 case 0x10: //AMOMIN.D
 {
     reg[rd] = get_mem_d(reg[rs1]);
     set_mem_d(reg[rs1], (int64_t)reg[rs2] < (int64_t)reg[rd] ? reg[rs2] : reg[rd] );
     break;
 }
 case 0x14: //AMOMAX.D
 {
     reg[rd] = get_mem_d(reg[rs1]);
     set_mem_d(reg[rs1], (int64_t)reg[rs2] > (int64_t)reg[rd] ? reg[rs2] : reg[rd] );
     break;
 }
 case 0x18: //AMOMINU.D
 {
     reg[rd] = get_mem_d(reg[rs1]);
     set_mem_d(reg[rs1], reg[rs2] < reg[rd] ? reg[rs2] : reg[rd] );
     break;
 }
 case 0x1c: //AMOMAXU.D
 {
     reg[rd] = get_mem_d(reg[rs1]);
     set_mem_d(reg[rs1], reg[rs2] > reg[rd] ? reg[rs2] : reg[rd] );
     break;
 }

以上,A-extension就告一段落,指令數量雖然不少但實現上很接近,因此實作不難。


碎碎念 : 明天進到M-extension,A-extension一些概念之前其實也沒有很清楚,藉這個機會多理解不少。


上一篇
Day 25 - 64bit I-extension,ZiCSR測試及實作完成
下一篇
Day 27 - RISC-V M Extension , 簡單來說...就是乘除
系列文
從零開始的RISC-V ISA Simulator (Another Little RISC-V ISA Simulator)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言