1. 程式人生 > 其它 >P4 流水線暫存器(logisim搭建)

P4 流水線暫存器(logisim搭建)

一、寫在前面

  • 首先,何為流水線CPU,流水線CPU和單週期CPU有什麼差別?

    單週期CPU上所有指令都在一個時鐘週期內完成,所以其時鐘週期一般較長(能夠完成最慢的指令),吞吐量不高。出於增大吞吐量的考慮,引入了流水線CPU,同一時刻有多條指令在其上執行,因此理論上五段流水CPU的吞吐量是單週期的五倍。——指導書

  • 那麼,如何實現多條指令同時執行?

    五級流水線CPU按照指令執行的五個階段對應地分成了五個頂層模組IF(instruction fetch)DEC(decode)EXE(execute)ME(memory)WB(write back)每個模組執行一條指令的一個階段

    為了實現階段的分割與傳遞性,需要在單週期CPU裡新增4個暫存器IF/DECDEC/EXEEXE/MEMMEM/WB,給予統一的時鐘與復位訊號。每次時鐘上升沿到來,暫存器便會將前一個階段的資訊傳遞給後一個階段,即將所有指令均向後傳遞一個階段,從而實現互不干擾,階段推進。

    寫回模組後面不需要流水線暫存器的原因是:其一,它執行的是指令執行的最後一個階段,它的執行資料並不需要給對應指令的下一階段使用;其二,寫回部分是暫存器堆,本身可以看作“第五個流水線暫存器”。——指導書。

    第五階段暫存器寫回暫存器不需要再等待一個時鐘上升沿,但讀出的rs與rt暫存器的值卻並不準確,這裡考慮內部轉發,後文介紹,這裡就不贅述了。

    注意:①每條指令都要走完5個週期,儘管可能在某些階段沒有操作;②所有對暫存器的寫回,都在WB階段進行,這樣才能保證按照指令順序寫回暫存器,不會出現順序錯亂的情況。

  • 實驗之前,需要注意什麼?

    因為我們是基於已搭建的單週期CPU,將其更改為流水線CPU,為了之後的操作更加順暢,這裡建議:①確保單週期CPU搭建的正確性;②將盡可能多的部分模組化;③合併多選器:將一位選擇指令合併為多位選擇指令。

二、單條指令的新增與測試

單週期CPU已經實現了單條指令單獨走完一個週期的正確性,那麼在流水線CPU裡,是不是不用額外更改就可實現單條指令的完成?當然不是的。

讀者可以注意以下幾點:

  • 各條指令資料是否通過暫存器傳遞

  • 各條指令的控制訊號是否在正確階段產生

    控制訊號由控制模組產生,但一個控制模組只服務於一個指令,而D、E、M、W四個階段分別執行著4條不同的指令(F階段無需生成控制訊號),很顯然一個control模組是不夠用的。這裡不妨採用分散式譯碼的方式(集中式譯碼讀者可自行探索),即放入4個control模組,對每個階段的指令進行譯碼操作。

    下圖為M階段的control模組:

​ 因為在進行該階段時,才需要判斷是否寫入儲存器,故我們只需要根據此階段的指令生成一個memw訊號。

  • 一些特殊指令:

    nop:什麼都不用做

    jjalbeq:根據指令集,我們需要獲取PC高4位與指令生成跳轉地址,因為jjal都在D階段判斷執行跳轉,故此二者都需從D階段取出(而不是F)。對於beq指令,應注意此時IFU模組裡地址已為PC+4而不是PC。特需注意的是:jal指令會將PC+4存入暫存器$31中,而新增延時槽後實際存入的值為PC+8

    為什麼是PC+8:因為延時槽的存在,在跳轉或分支指令之後,還會執行一條延時槽指令,該指令在來不及地址跳轉的時候就已經被執行。
    IFU模組I/O資料:

  • 關於除錯:

    關於mips中程式碼的匯出與匯入機器碼,讀者可移步:線上教程 → ROM、RAM的使用和MARS匯出機器碼的講解。

    這個階段的測試非常重要!因為還不考慮轉發和阻塞,所以造資料的時候應確保不要衝突,觀察暫存器堆的時候直接雙擊暫存器堆上的放大鏡即可,點選左側的模組是無法檢視實質內容的(哭,記得多加nop

三、轉發

首先,介紹一些概念:

  • tnew和tuse:

    對於指令的源暫存器s有時間tuse_D(/E/M),意思是在從D(/E/M)段開始,過幾個時鐘週期需要使用s裡的資料。對於指令的目的暫存器des有資料tnew_D(/E/M),意思是從D(/E/M)段開始,過幾個時鐘週期將要寫入des的資料產生。——指導書

    簡而言之,①tuse只在判斷阻塞的時候使用,指離源暫存器被使用的階段還有多少個時鐘週期。舉個例子:對於addu指令而言,$rs$rt暫存器的值在E階段被使用,故tuse_D_rs=1,因為D離E階段還有一個時鐘週期,而tuse_D_rs=0。②tnew判斷離該階段目的暫存器資料準備好還有多少個時鐘週期當tnew=0,說明資料已經準備完畢,可以轉發。舉個例子,對於lw指令而言,其在M階段進行取出儲存器裡資料的操作,故在W階段資料準備完畢,則對於lw指令,tnew_D=3,tnew_E=2,tnew_M=1,tnew_W=0。③對於每一個指令來說,其在每一個階段的tuse和tnew固定不變,那麼,如何獲取每一個階段指令的tuse與tnew?在控制模組用與或門的方式,新增控制訊號即可:

  • 內部轉發

    對於寫回模組到譯碼模組(W到D)的轉發,由於寫回階段寫入和譯碼階段讀出都是對於暫存器堆,我們可以採用內部轉發:通過暫存器堆的結構使得,在同一個時鐘上升沿,若有資料寫入暫存器r,也有從暫存器r讀出資料的請求,把將寫入資料直接讀出。這樣一來,寫回模組轉發到譯碼模組的資料可以不通過外部轉發,當然外部轉發也可。

    內部轉發邏輯:暫存器堆的RD1輸出:是0當且僅當A1輸入為0; 是將要寫入暫存器A3的資料當且僅當將要寫入的暫存器編號A3和讀取暫存器編號A1相等; 是暫存器A1的資料當且僅當將要寫入的暫存器編號A3和讀取暫存器編號A1不相等。 暫存器堆的RD2輸出:是0當且僅當A2輸入為0;是將要寫入暫存器A3的資料當且僅當將要寫入的暫存器編號A3和讀取暫存器編號A2相等; 是暫存器A2的資料當且僅當將要寫入的暫存器編號A3和讀取暫存器編號A2不相等。

    ——指導書

    什麼意思呢?我們需要在暫存器堆內部進行改造,看看圖就明白了:

那麼,何時轉發?

  • 轉發都是從流水線暫存器出發,傳遞給各個執行階段的

    為什麼會這樣?不如先來思考一下,如果想要轉發ALU運算結果,應該在什麼位置轉發?在E階段,資料還在進行運算,只有到E階段末尾,資料才被運算完成。但我們無法判斷E階段末尾,而E/M暫存器中傳遞的值就是已經運算完成的ALU結果,所以應當轉發E/M暫存器右側資料,此時M階段tnew為0,資料轉發是有效的。

    那麼,從這個思路出發,我們就會發現:

  • 一共有兩種轉發情況

    • E/M流水線暫存器轉發: 轉發給E和D階段
    • M/W流水線暫存器轉發:轉發給E,M,D階段。其中,轉發給D階段採用內部轉發方式。

    可以發現,被轉發的資料只有E階段的運算結果和W階段的寫回資料,為什麼沒有對M階段存入地址的判斷過程?因為儲存器訪問地址不會造成衝突!

下面來看看具體模組:

  • 內部:

    從此圖我們可以看見模組對轉發操作的實現原理,當源暫存器等於目的暫存器且目的暫存器的值已經準備好的時候(tnew=0),進行轉發操作當多個階段都滿足轉發條件,選擇資料最新的那個階段

  • 外部

​ 瞭解了內部邏輯,再來看模組外觀,就比較好理解了。

說明:des_E不是指E階段ALU運算結果所在的目的暫存器,而是指M階段當前指令的目的暫存器。其含義雖是前者,但由於實際在M階段轉發,所以採用des_M來直觀表示。

四、阻塞

何時阻塞?

將要使用的資料來不及轉發過來,如果不阻塞就會使用錯誤的資料進行計算。由於分支指令最早在譯碼階段就需要使用暫存器資料計算,暫停階段放在譯碼階段,即DEC模組。

結合開始介紹的概念,每條指令i在譯碼段時(DEC模組執行指令i),要和它前面執行的指令j(前面模組執行的指令j,一般是前面的所有模組)對照判斷,看是否需要暫停:當i的源暫存器和j的目的暫存器相同時(設為暫存器k),i和j存在資料關聯,這時如果i的tuse小於j的tnew,代表k中資料還沒被j準備好(甚至不能轉發過來),這時需要暫停D段指令i,否則i會使用k中舊資料裡錯誤執行。

——指導書

簡而言之:① 資料關聯;② 來不及轉發;③ 如何判斷:tuse < tnew

怎麼阻塞?

將當前指令和其後面的指令全部“凍結”,而其前面的指令正常執行,以等待資料準備好。D段指令凍結也就是F/D暫存器仍然保持當前狀態,而此時F段中的pc已經取出下一條指令地址,為防止丟失,pc也保持當前狀態。再者,為了D段需要暫停的指令無法向後進行,需要在下一上升沿在E段插入氣泡

故需要將D/E流水線暫存器同步復位(插入氣泡,使當前指令無法向下傳遞),且IF/DEC流水線暫存器和PC鎖定(維持當前狀態,即禁止使能),直到暫停的條件被打破。

——指導書

模組外觀如下,記得新增非門:

五、整體除錯

  • 自造刁鑽的資料,① 先全部執行看暫存器堆裡的值是否符合預期、儲存器內的值是否正確,② 再單步除錯看每一步每一階段狀態是否與mars一致(探針是個好東西。

  • 看測評結果,測評結果中含有WA測試點的的當前測試指令和相鄰測試指令,將其轉為二進位制,對照指令集可以看出是執行哪個指令的時候出了問題。不過,不一定是該指令本身出了問題,如果檢查後不是指令本身出錯,可能是以下兩種情況:

    • 寫入的地址不對:跳轉或分支指令出錯
    • 寫入的資料不對:涉及改變暫存器、儲存器資料的所有指令都有出錯可能。
  • 這裡給出一組測試資料,讀者可以試試

    ori $2,$1,0x1234
    lui $1,0x5678
    addu $1,$1,$2
    jal a
    nop
    beq $2,$1,a
    nop
    lw $2,0($0)
    a:
    sw $1,0($0)
    lw $2,0($0)
    sw $2,4($0)
    
    v2.0 raw
    34221234
    3c015678
    00220821
    0c000c08
    00000000
    10410002
    00000000
    8c020000
    ac010000
    8c020000
    ac020004
    
    output_reg:
    $1: 0x56781234
    $2: 0x56781234
    $31: 0x00003014
    
    output_mem:
    0x56781234 0x56781234
    

    (當然過了這組資料可能依然過不了...讀者可以根據自己的測評情況製造更刁鑽的資料()


感謝觀看!