日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【BUAA_CO_LAB】p5p6碎碎念

發(fā)布時間:2023/12/10 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【BUAA_CO_LAB】p5p6碎碎念 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 【BUAA_CO_LAB】p5&p6碎碎念
      • 寫在前面的話
      • 流水線知識
        • 流水級與命名
          • 流水級寄存器
            • R型計算指令
            • I型計算指令
            • 內存訪寫指令
            • Branch類指令
            • Jump指令
          • Control Unit
        • 轉發(fā)
        • 阻塞
      • p5の完結
      • 改裝p6
        • 乘除模塊
            • 模仿乘除法運算延遲
        • 按字節(jié)訪存
      • 課上指令之套路
        • 運算類
        • 條件跳轉類
        • 條件訪存類

【BUAA_CO_LAB】p5&p6碎碎念


寫在前面的話

首先前面必須要附上一段致歉。其實這篇文章應該早在兩三個星期之前就更出來了,但是由于期末月的到來,許多課程面臨結課大作業(yè)/考試等任務,博主前段時間忙于各種大作業(yè)與其他事務,也沒能很好地管理自己的時間(指周末睡大覺的屑),這個時候再發(fā)布,我估計大部分人其實應該都已經做完了這一部分了,所以這篇文章或許更多的意義在于“造福后人”罷。

不過看到了[p3&p4碎碎念]這篇博文下面的一些反饋,我個人還是很感動的,至少發(fā)現自己寫的東西有人在看,有幫助到一些像我當初一樣有困難的同學,個中情愫實在是無以言表。這也給了我繼續(xù)寫沒什么技術含量的技術博客的動力,以及繼續(xù)攻克計算機學習路上各種困難的決心!再次衷心感謝我的那幾位讀者!!(淚)

從p5開始,我們就從單周期CPU邁入了流水線CPU的旅程。這里的風景是獨特的,其中的轉發(fā)、阻塞等原理或許會讓初遇的同學感到不知所措。剛開始寫的時候我也迷茫了很久,但其實只要細心體會個中原理,就會豁然開朗。我的架構沒有借鑒gxp老師的PPT和黑書相關內容,所以如果你已經照著這些教程做出架構了,就沒必要完全按照此文來。

我不是那種思維跳躍的聰明同學,所以理解都是非常簡樸的,在那些聰明同學看來或許會顯得過于繁瑣冗雜而可笑,但我希望記錄下自己的思路,以為后來似我者之鑒(至少我在掙扎于這兩p的時候是很希望有這樣一種胎教級教程可以看看的)。同時,因為p6事實上是在p5的基礎上加裝乘除相關模塊,沒有什么需要特別分開說明的技術細節(jié),因此我采取了以p5為主的敘述模式,與p6有關的“加裝”部分我單獨放在**[改裝p6]**模塊里說。

現在手握p4代碼的你,就可以跟著本胎教教程著手改造自己的單周期CPU,進入流水線的新階段了。當然,如有錯誤,還望在評論區(qū)不吝指正。

流水線知識

這個時候大家應該已經在理論課學過流水線相關知識,我就不再講述流水線的必要性、效率、基礎架構之類的東西了。我們直接進入流水線跟單周期CPU最不一樣的三點:流水級轉發(fā)阻塞。

流水級與命名

理論課中已經學到,為了使指令能夠像PPT例子中的洗衣服那樣同時安排(后面將大量使用“洗衣服”的例子),我們要細分出幾個“洗衣步驟”或者說“流水線上的車間”,這就是我們的流水級。在書寫正式的流水線代碼之前,我們先基于p4代碼做出一個“Pipeline Without Hazard”,也就是完全不考慮轉發(fā)與阻塞,只是分出了流水級的CPU來。在課上,我們學到流水線CPU的基礎流水級架構是這樣的:

傳統(tǒng)CPU有四大功能:取指、譯碼、執(zhí)行計算、存取,這不就剛好可以分成四個“洗衣步驟”嗎?同時,由于后續(xù)轉發(fā)的特殊性,我們增加了一個寫回級(Write Back)。這樣就構成了五個基礎的流水級。接下來,我們就往五個流水級里塞更細的“洗衣步驟”,也就是各個模塊(當然,別忘了重要的控制信號生成單元Control Unit)。4個流水級寄存器,不過是我們用來傳遞“衣服”,或者說流水線上的“工件”的傳送帶而已,前一周期中的上一階段所傳來的數據,和后一周期中為下一階段提供的數據,都在且必須在這條傳送帶上流淌。無論是數據還是信號,都需要在寄存器中進行保存,直至不再需要。

增添模塊如下圖所示。

可以看到,除了綠色的[比較模塊CMP]以外其他都已經在p4里寫過了,基礎架構拿過來用就行。這里我們還要注意一點,每個“洗衣小車間”是獨立的(請務必,務必,務必記住這個概念),只通過流水線傳送帶——即流水級寄存器連接,也就是說在每一級產生的控制信號是具有其獨立性的。因此我們只需要寫出一個單獨的Control Unit,然后在頂端模塊中的不同流水級里對Control Unit分別進行不一樣的實例化調用。(閱讀課設教程,你會發(fā)現這種寫法叫做“分布式”)

在把它們組合起來之前,要先給它們改個名字,同時,輸入輸出各個模塊的信號名字都請命名成形如**[Pipeline Level] _ [Module name] _ [Signal name]**的形式(如E_ALU_Result),以辨析各個流水級,避免后續(xù)接線之類的錯誤。課程組教程的命名建議就非常好。我的命名是如圖中紅字所示。

  • 綠色的[比較模塊CMP]

    它是我們用來判斷Branch類跳轉是否發(fā)生的,它會通過對輸入的rs和rt值進行判斷,給出一個“branch_or_not”信號指導跳轉的發(fā)生與否。有的同學可能在p3、p4時期就已經開辟過類似模塊做過這件事了。下面給出我的代碼(p6版本),相信大家看了以后就明白它的作用了。這個模塊在上機的時候也會幫你大忙。

    對了,請務必不要一字不差地復制,咱的課設還沒結束呢orz

`timescale 1ns / 1ps`define b_beq 3'd0 `define b_bne 3'd1 `define b_blez 3'd2 `define b_bgtz 3'd3 `define b_bltz 3'd4 `define b_bgez 3'd5 `define nobranch 3'd6module D_CMP(input [31:0] RF_RD1, //Read From RD1 -> rsinput [31:0] RF_RD2, //Read From RD2 -> rtinput [2:0] branch_type, //Created by Control Unitoutput branch_or_not );wire equal = (RF_RD1 == RF_RD2);wire equal_0 = !(|RF_RD1);wire greater_0 = ((!equal_0) && (!RF_RD1[31])); //請盡量使用對[31]的判斷來辨別正負數,Verilog的有符號系統(tǒng)真的很坑!!!wire less_0 = ((!equal_0) && (RF_RD1[31]));assign branch_or_not = (equal && (branch_type == `b_beq)) ||(!equal && (branch_type == `b_bne)) ||((less_0 | equal_0) && (branch_type == `b_blez)) ||((greater_0) && (branch_type == `b_bgtz)) ||((less_0) && (branch_type == `b_bltz)) ||((greater_0 | equal_0) && (branch_type == `b_bgez));endmodule

我給出了這個新的模塊的參考寫法,剩下的模塊中,除了五個流水級寄存器和Control Unit,都可以基本照搬p4了,只需要按照給出的指令集要求稍作修飾即可(比如指令類型、所需的控制信號類型、存儲器的大小等)。

  • 有一個值得注意的小地方是PC值運算模塊NextPC。它的寫法與p4略有不同。有些指令的跳轉地址選擇的 PC 指令是它自己這個小車間對應的相對獨立的“當前指令”,而不是電路中宏觀的當前指令,比如需要被 CMP 模塊判斷的分支跳轉指令和 J 型指令,它們的各類判斷發(fā)生在 D 級流水級,因此跳轉地址對應的“當前指令”也應該是 D 級流水級中的 PC 值 D_PC,而不是直接從F 級流水級的 IM 模塊發(fā)出的 F_PC。 其實這一點,只要理解了前面“小車間獨立性”的敘述,就非常容易理解了對不對?洗衣服の例子,yyds……

    `timescale 1ns / 1ps`define Branch 3'd0 `define J 3'd1 `define Jr 3'd2 `define Normal 3'd3module NextPC(input [31:0] D_PC,input [31:0] F_PC,input [25:0] Addr, //Addr or Imm16(Addr[15:0])input [31:0] reg_rs,input [2:0] NextType,input branch_or_not,output [31:0] NPC );assign NPC = (NextType == `Normal) ? (F_PC + 4) :(NextType == `J) ? {D_PC[31:28], Addr, 2'b00} :(NextType == `Jr) ? reg_rs :((NextType == `Branch) && (branch_or_not == 1'b1)) ? (D_PC + 4 + {{14{Addr[15]}}, Addr[15:0], 2'b00}) : (F_PC + 4);endmodule

下面介紹五個流水級寄存器和Control Unit的寫法。

流水級寄存器

首先我們應該明確模塊的輸入輸出,也就是說,這個寄存器究竟要存“哪些衣服”?剛過過水的?剛打過泡的?這個時候,只要分析一下指令在流水線中流淌的過程就可以明確,也即,我希望將什么樣的信號傳給下游的“車間”來處理(請務必記住每個流水級是相對獨立的五個小車間!它們之間唯一的聯(lián)系就是一條流水線傳送帶!)。下面將指令分為R型計算指令I型計算指令內存訪問指令、Branch類指令Jump類指令分析。

前文提到過,為保證“小車間獨立性”,每一個流水級里都需要單獨實例化Control Unit,而光它一個人就需要指令編碼Ins[31:0]了吧,因此下文不再重復提及其必要性。而我想,理論課上應該也已經講過流水線CPU需要將當前PC值不斷流水的特性,因此后面也不在每一級贅述了。它們是基礎。

時鐘信號clk、復位信號Reset和寫使能信號WE更是基礎中的基礎,不必再說了。

同時,由于流水級寄存器為所有指令所共用,因此我會直接列出該流水級寄存器需要存儲的所有信號,并在對應的指令分析部分對該指令需要的信號進行下劃線處理,望讀者辨析。

R型計算指令
  • IF_ID - Ins[31:0] + PC[31:0]
  • ID_EXE - Ins[31:0] + PC[31:0] + rs[31:0] + rt[31:0] + Ext[31:0]
    • 眾所周知,R型指令的R指的就是Register,從通用寄存器堆里取出來的數據,為了在E級對它們進行算術運算,GRF[rs]和GRF[rt]值是一定要傳遞到下一個小車間去的。
  • EX_MEM - Ins[31:0] + PC[31:0] + ALU[31:0] + rt[31:0] + Ext[31:0]
    • 接收ALU運算結果
  • MEM_WB - Ins[31:0] + PC[31:0] + ALU[31:0] + DM[31:0] + Ext[31:0]
    • 接收ALU運算結果,把ALU運算結果寫回通用寄存器堆
I型計算指令
  • IF_ID - Ins[31:0] + PC[31:0]
  • ID_EXE - Ins[31:0] + PC[31:0] + rs[31:0] + rt[31:0] + Ext[31:0]
    • I指立即數Immediate,我們需要從Ext模塊取出的處理好了的立即數,在E級和GRF[rs]進行算術運算
  • EX_MEM - Ins[31:0] + PC[31:0] + ALU[31:0] + rt[31:0] + Ext[31:0]
    • 接收ALU運算結果
  • MEM_WB - Ins[31:0] + PC[31:0] + ALU[31:0] + DM[31:0] + Ext[31:0]
    • 接收ALU運算結果,把ALU運算結果寫回通用寄存器堆
內存訪寫指令
  • IF_ID - Ins[31:0] + PC[31:0]
  • ID_EXE - Ins[31:0] + PC[31:0] + rs[31:0] + rt[31:0] + Ext[31:0]
    • 訪問:需要從Ext模塊取出的處理好了的立即數,在E級和GRF[rs]進行算術運算,獲得訪問數據存儲器的地址,具體參見指令集的RTL描述細節(jié)
    • 寫入:需要將從通用寄存器堆里取出來的數據在E級進行算術運算,rs和rt都要
  • EX_MEM - Ins[31:0] + PC[31:0] + ALU[31:0] + rt[31:0] + Ext[31:0]
    • 訪問:接收ALU運算結果
    • 寫入:接收ALU運算結果還有rt值,用于計算寫入地址
  • MEM_WB - Ins[31:0] + PC[31:0] + ALU[31:0] + DM[31:0] + Ext[31:0]
    • 接收內存訪問結果,把結果寫回通用寄存器堆
Branch類指令

不用麻煩后面,一切在D級完結(唯一需要麻煩后面的是轉發(fā)問題,后面再說)

Jump指令

不用麻煩后面,一切在D級完結(唯一需要麻煩后面的是轉發(fā)問題,后面再說)

這時你會發(fā)現好像還有幾個地方沒被標過下劃線,沒關系,那是接下來即將要講的轉發(fā)部分需要的。先寫上吧!

Control Unit

對于這一部分,我覺得美工非常重要。整潔的排版可以有效地增加上機加指令的效率。總體上說,跟p4部分的架構其實差不多,只是需要進行一些排版。比如說,我們可以合并所有的R型指令為Cal_r等,這樣不僅有利于各種控制信號的生成,而且對后面要講的阻塞有大用處。下面我貼上我的“排版”,大家予以參考即可。

  • Part_1: 宏定義

  • Part_2: 信號定義分割

  • Part_3: 中間信號分類

  • Part_4: 控制信號生成(寫之前一定要先列好表噢)

    p.s. 在這里,我建議把所有需要用MUX選擇的部分(不管是選擇寫入地址也好,寫入數據也好),統(tǒng)統(tǒng)集成到這個地方來,而不是再去費勁心機地寫好多個不同位寬的MUX,吃力不討好,而且會顯得邏輯很亂。就如下圖的第一條assign一樣,把本該通過頂層模塊中MUX來選擇的寄存器堆寫入地址RegDst直接采用這種方式定義出來,就非常方便,要用的時候直接引出一個RegDst就好了,根本不用在外面判斷。

    類似的例子還有很多,比如MemToReg呀,ALU_Src_A和ALU_Src_B呀,都可以集成到CU這里來。

最后,我給出我對控制信號做出的安排(p5版本),以供大家參考。


至此,能吃p4老本的地方結束。

轉發(fā)

  • 通俗的講解

轉發(fā)是流水線 CPU 中至關重要的環(huán)節(jié)。數據冒險的嚴格定義請自行復習,我在這里只作我非常簡樸的講解。我認為轉發(fā)需要存在的根源在于:流水線是一級一級地往下流的,有的時候當前我們需要的GRF[rs]值或者GRF[rt]值還在后邊剛被算出來,還沒來得及被傳回到通用寄存器堆里去,它事實上已經產生了,只是流水線太拖拉,我們還夠不到它而已,因此我們需要一條捷徑,把已經產生了但走得太慢,還來不及回到通用寄存器堆的數據迅速地傳到我們手中。

這就好比我們的洗衣服流水線,ID_EXE級的老哥負責把洗好的衣服打包送給客戶?,F在客戶催著他要衣服,最后一級的老哥其實已經把衣服洗好了,但衣服還在流水線上流著呢,偷工減料的黑心流水線走得過于慢了,還有好一會兒衣服才能到ID_EXE老哥這里。因此現在ID_EXE級老哥叫了一個叫轉發(fā)的小工蹲在最后一級的老哥那里,他一洗好,就火速奪過衣服跑步送給ID_EXE級老哥。在我們的流水線里,E級、M級和W級都蹲著一個叫轉發(fā)的小工,隨時準備將處理好的衣服奪過來跑步送給寄存器,這就是轉發(fā)的意義所在。畢竟時間就是金錢,效率就是生命嘛。(怪不得叫暴力轉發(fā))

當然,在這里我們假設的是客戶來要衣服的時候,衣服必然已經洗好了。如果沒洗好,小工啥也沒拿到呢?這時就不得不讓客戶再等等了,這就是下一部分要講的“阻塞”問題。判斷是否洗好的部分交給阻塞模塊,在轉發(fā)模塊里,我們默認所有的衣服都已經洗好了,這就是教程里所描述的“全力”轉發(fā)。

為了實現這一點,我們首先要將“衣服”,也就是通用寄存器組的讀出值GPR[rs]和 GPR[rt]不斷地在各個流水級寄存器之間流水并且存儲,然后通過當前的指令生成的控制信號(一般是RegDst)來判斷是不是要發(fā)生轉發(fā)。如果我們現在需要的寫入地址值,也就是“客戶”,剛好跟某個流水級里的RegDst一樣,就說明該客戶的衣服在這里洗好了,我正要往客戶那里送呢,那么轉發(fā)小工,就拿上衣服走人吧。

  • 代碼細節(jié)

需要發(fā)生轉發(fā)的時間節(jié)點是:當前的“不夠”的時候,也就是當某一部件需要使用 GPR[rs]或GPR[rt]時,這個值還沒來得及寫入 GPR 的時候。這個時候,我們需要通過轉發(fā)將這個值從流水線寄存器中送到該部件的輸入處。這也就說明,**在每一個需要使用 GPR[rs]或者 GPR[rt]的端口節(jié)點前面,我們都可以對當前的 rs 和 rt 值做出一個判斷,判斷是可以使用當前的值,還是說當前的“不夠”,需要提前獲取后面的值。**教程中的原意應該是用 MUX 來實現,但是由于目標信號數量的不定性,我認為 MUX 模塊的設置并不太方便,同時我也不喜歡把轉發(fā)也集成成為一個模塊(線太多了),因此我選擇直接在頂層模塊連線的時候,在對應端口模塊實例化的前面先對輸入信號做出一個判斷,如下圖所示。

根據對數據通路的分析,這樣的位點其實很有限,只有 D 級 CMP 模塊的輸入、E 級 ALU 模塊的輸入和 M 級 DM 模塊的輸入(就是要用rs、rt值的地方嘛)。只要當前位點的讀取寄存器地址和某轉發(fā)輸入來源的寫入寄存器地址相等且不為 0,就選擇該轉發(fā)輸入來源;在有多個轉發(fā)輸入來源都滿足條件時,最新產生的數據優(yōu)先級最高,以實現全力轉發(fā)。

同時為了滿足“最新產生的數據”這一點,我們需要在 GRF 的內部實現內部轉發(fā),也就是將輸入的數據實時反饋到輸出端口,避免數據的滯塞。

需要注意的是對當前流水級來說,能夠選擇的轉發(fā)來源是“它之后與 GRF 之前”的流水級區(qū)間(小工總不可能把不洗衣服的車間里的衣服也搶了吧),因為如果數據都到了 GPR 就不存在所謂的轉發(fā)與否的問題了,衣服都已經流到我手上了,直接拿不香嗎……因此對 D 是 E、M(由于 W 的回寫行為與它直接對接,通過 GPR 的反饋不存在轉發(fā)的問題),對 E 是 M、W,對 M 是 W,而 W 級已經不存在需要轉發(fā)的數據了。也就是說,關鍵其實就是“我需要的數據可能滯留在哪里”。

  • 后邊加指令的時候也是一個道理,這一級要用,我就全力轉發(fā)(比如某些指令可能在M級也需要rs值,那你就需要多寫一個M_FWD_rs了),轉發(fā)的范圍是我之后到GRF之前,從新到舊,最后到我自己(本級的值)。就是這么簡單的思路。

阻塞

比起轉發(fā),阻塞的道理其實簡單得多,就是在轉發(fā)沒有辦法解決問題的時候,比如說新的數據根本還沒有產生的時候,向進程中插入“氣泡”暫緩流水,等信號產生了以后再開始流動,并實現數據的獲取(通過轉發(fā)等方式)。而 “新的數據根本還沒有產生”的條件是【Tuse < Tnew 且滿足轉發(fā)條件】,就是說需要用數據了,客戶來了,但我的衣服還踏馬沒洗好呢。

在CPU中的行為就是把指令暫停在 D 級,也即用一個 Stall 信號來參與控制該級流水寄存器的寫入數據行為,使得后面的值不會被更新,相當于插入了一個 nop,或者說是一個“氣泡”,讓中間的模塊安靜地執(zhí)行完現在的進程,等“氣泡”消除了之后,把新的值寫到流水寄存器中繼續(xù)開始流水。

插入“氣泡”的具體操作是:凍結PC值停止流水線運行(流水線再走衣服真的洗不完了哥,停一會兒罷),清空ID_EXE(后邊的慢慢洗罷,不再給你們派新活了,把手頭的洗完再說)??吹竭@個描述,你應該非常清楚前者通過WriteEnable寫使能信號完成,后者通過Reset復位信號完成。因此我們只需要新建一個能夠生成阻塞信號的阻塞模塊,然后將它產生的阻塞信號分別與部分寄存器的WE信號和部分寄存器的Reset信號與一下就可以了。

  • 與之類似的一個操作叫做【清空延遲槽】,它的動作是清空IF_ID,但是并不凍結PC值,更不會清空ID_EXE,請閱讀英文指令集有關內容自行思考。

Tuse:指令進入 D 級后,其后的某個功能部件再經過多少時鐘周期就必須要使用寄存器值。對于有兩個操作數的指令,其每個操作數的 Tuse 值可能不等(如 store 型指令 rs、rt 的 Tuse 分別為 1 和 2 )。用D級的信號判斷。

Tnew:位于 E 級及其后各級的指令,再經過多少周期就能夠產生要寫入寄存器的結果。在我們目前的 CPU 中,W 級的指令 Tnew 恒為 0;對于同一條指令,Tnew@M = max(Tnew@E - 1, 0)。 后面每一級都要具體判斷。

似乎有很多人喜歡在這里畫表,但我感覺不用吧,自己想一想就能明白了,如下圖所示。比如對于Tuse,branch類和J類的跳轉與否的判斷在D級就可以完成,自然是0(不用再找后面的了),需要E級的ALU運算結果的就再往后一個周期,就是1,后面的以此類推了。需要注意的是對于無關的東西我們要把Tuse設置為一個很大的值(比如我的3’d5),相當于一直塞著。

根據 Tuse 和 Tnew 所提供的信息,可以得出:當滿足轉發(fā)條件(詳見“轉發(fā)”部分),且 D 級指令的 Tuse 小于對應 E 級或 M 級指令的 Tnew時,我們就在 D 級暫停指令。在其他情況下,數據冒險都可以通過轉發(fā)解決。

從 Tuse 和 Tnew 的表達式,我們也能看出需要在每一個流水級都判斷一下當前的 Tuse 和 Tnew 值,以完成比對。這整個過程可以看成一個這樣的流程:

當然了,在“全力轉發(fā)”的思路下,我們的代碼里已經沒有GRF后面的“可與不可”的判斷了(管它現在有沒有呢,滿足轉發(fā)條件的直接轉發(fā)),這個圖只是對轉發(fā)必要性的一個更直觀的詮釋。

p5の完結

最后,在頂端模塊里把所有的模塊都實例化并連接起來吧——你的流水線CPU就這樣誕生了。如果不清楚接線細節(jié),可以參考PPT等資源,但我感覺只要把上面的邏輯捋清了,再結合p4的基礎,搭出美麗的p5、p6并不是問題。接下來就是測試等環(huán)節(jié),討論區(qū)的同學珠玉在前,我也不在此班門弄斧了,請大家好好利用身邊同學的資源吧!!

本學期課設結束后,我會把我的代碼上傳到Github上去,屆時會在本博文評論區(qū)里附上網址,以供后來人參考。


改裝p6

p6跟p5的區(qū)別在于多了很多指令,以及乘除相關的{mult, div, multu, divu, mthi, mtlo, mfhi, mflo}。對于前面新增的指令,相信只要通過了p5課上的同學都能輕松地照葫蘆畫瓢給它加上去,在此就不贅述了。唯一麻煩的就是乘除相關模塊的加裝以及訪存數據存儲器時對不同位寬數據的處理。我們在p5的基礎上進行改裝,從而實現這兩個地方。

乘除模塊

乘除法是一種算術運算指令,因此我們把單獨開辟出來的乘除模塊放在E級,我給它取名叫E_HILO。在這里,我們需要完成乘除法的算術運算,并且模仿乘除法運算延遲的行為和從HI、LO寄存器中取值的行為。

模仿乘除法運算延遲

乘除法運算在電路中進行得很慢,所以我們需要進行一個模仿。這里我想到了之前學過的有限狀態(tài)機,我們可以設置一個Cycle狀態(tài)信號,執(zhí)行過乘除法后,它就充當一個計時器,等Cycle里的延遲時間走完了之后,我們再進行后面的操作。乘除法部件中內置了 HI 和 LO 兩個寄存器,這兩個寄存器,同時也是這個模塊與外界溝通的唯一窗口

我們假定乘 / 除部件的執(zhí)行乘法的時間為 5 個 cycle (包含寫入內部的 HILO 寄存器),執(zhí)行除法的時間為 10 個 cycle。你在乘 / 除部件內部必須模擬這個延遲,即通過 Busy 輸出標志來反映這個延遲。

想明白了“計時器”的實現后,代碼寫起來就沒什么技術含量,下面是我的,僅供參考。

always@(posedge clk)beginif (reset)begin//Reset all the Signalsendelse beginif (cycle == 0)beginif (HILO_mthi)//Operationelse if (HILO_mtlo)//Operationelse if (HILO_mult)beginbusy <= 1'b1;cycle <= 5;//Operation of MULTendelse if (HILO_multu)beginbusy <= 1'b1;cycle <= 5;//Operation of MULTUendelse if (HILO_div)beginbusy <= 1'b1;cycle <= 10;//Operation of DIV (Attention: Split the result into HI and LO!!!!)endelse if (HILO_divu)beginbusy <= 1'b1;cycle <= 10;//Operation of DIVU (Attention: Split the result into HI and LO!!!!) endendelse if (cycle == 1)beginbusy <= 1'b0;cycle <= 0;HI <= tmp_hi;LO <= tmp_lo;endelsecycle <= cycle - 1;endend

按字節(jié)訪存

由于我們這一學期的p6改為了【存儲器外置】的形式,據我所知跟之前有所不同,我也不確定以后是不是這樣,所以我就不寫存儲器外置的寫法了(課程組可能會有具體要求,實現也不難的,在頂端模塊里處理就行)。在這里貼上一個通用版本的支持按字節(jié)訪存的DM代碼。(好像p4已經貼過了?)

`timescale 1ns / 1ps`define BW_word 2'b00 `define BW_halfword 2'b01 `define BW_byte 2'b01`define word DataMemory[DM_A[15:2]] `define halfword `word[15 + 16 * DM_A[1] -:16] //-意為從此開始往下16位,同理,如果是+就是往上16位。這么寫的原因是因為DM_A并非是一個constant,在Verilog的語法定義里,不可以用變量的值表示向量位寬,除非采用這種寫法,不然必報錯 `define half_sign `word[15 + 16 * DM_A[1] -:1] `define byte `word[7 + 8 * DM_A[1:0] -:8] `define byte_sign `word[7 + 8 * DM_A[1:0] -:1]module M_DM(input [31:0] DM_A,input [31:0] DM_WD,input [1:0] BW,input [31:0] PC,input MemWrite,input Clk,input Reset,output [31:0] DM_out );reg [31:0] DataMemory [3071:0];assign DM_out = (BW == `BW_word) ? (`word) :(BW == `BW_halfword) ? {{16{`half_sign}}, `halfword} :(BW == `BW_byte) ? {{16{`byte_sign}}, `byte} : `word;integer i;initial beginfor (i = 0; i < 3072; i = i + 1)beginDataMemory[i] = 32'd0;endendalways@(posedge Clk)beginif (Reset)beginfor (i = 0; i < 3072; i = i + 1)beginDataMemory[i] = 32'd0;endendelse if (MemWrite)begin$display("%d@%h: *%h <= %h", $time, PC, DM_A, DM_WD); `word <= DM_WD;`halfword <= DM_WD[15:0];`byte <= DM_WD[7:0];endendendmodule

到這里,其實p6就完成了,沒有想象中那么難罷。

但是注意:p6的指令很多,加的時候一定要看清楚機器碼等細節(jié),尤其是在處理控制信號的時候。

課上指令之套路

課上指令可以總結為三類:R型/I型運算類(運算),B+alr類(條件跳轉),lw/sw + [condition]類(條件訪存)。它們都有自己對應的套路。

運算類

如果你在Control Unit里做好了中間信號分類,那么運算類就是小菜一碟。在Control Unit里先宏定義好新指令信號,將其合并入cal_r或者cal_i中,這樣你就不用再理會任何與阻塞/轉發(fā)有關的東西了。然后,為它分配一個新的ALUOp碼,去ALU模塊里定義好它的具體運算操作,基本上就完成了。一般來說加這個運算類指令可以在5分鐘內解決戰(zhàn)斗——只要你課下沒錯。所以,做好測試!!!

運算類也可能會出現“條件寫”的要求,比如運算結果滿足某條件才寫回,這里只需要在E級把你運算得到的結果轉化成條件真值,然后傳到E級Control Unit里去指導RegDst就可以了。

條件跳轉類

一般是B+alr類。首先,在Control Unit里將它合并到branch類中,此后阻塞不必再理。然后為它分配合適的BranchType等控制信號,到對應的模塊中判斷跳轉是否發(fā)生,并且生成好NextPC的值(根據題干具體定)。這時,我們可以得到來自CMP模塊的判斷信號branch_or_not(根據判斷條件的不同,可以酌情向CMP模塊中加入信號)。用這個branch_or_not信號,我們可以在D級中定義是否發(fā)生跳轉的信號為一個check信號:wire check =branch_or_not,然后將這個信號流水下去。如果新的指令需要你在條件跳轉的同時寫寄存器(比如寫$31),那么就在每個流水級都將這個check信號傳入到控制信號生成器中去,和指令信號一起指導RegDst等信號的生成。

條件訪存類

在這里,第一步依然是合并指令到load類或者store類中,進行一些常規(guī)的控制信號處理操作。不過這時我們不能再對阻塞模塊和轉發(fā)置之不理。因為根據題目的特定要求,它有的時候在M級需要rs值(只是舉個例子),這個時候我們就需要在M級加上對rs的轉發(fā);
在阻塞模塊里,我們也要做出修改,因為我們在M級才能得到條件真值,這個時候如果D級有需要rs、rt的動作(有客戶來取衣服了),E級的情形就是不確定的,因此我們需要在E級進行一個保守的條件約束如果E級也是該條指令,并且此時D級要用該條指令要寫入的寄存器,我們就stall。形如:

當然根據不同指令的要求不同,這里給E級強加的暫停條件也不同,請大家根據指令的RTL描述自行變通。但我認為條件暫停的根本是只約束E級,因為事實上只有這里可以產生RegDst等控制信號的不定值(也有人認為M級也要約束,但對于課上測試結果應該是一樣的)。


本文到此就結束了,如果文中有錯誤,或者有我還說得不清楚的地方,歡迎大家在評論區(qū)留言告知。雖然也沒幾個人看,但是已有的讀者的反饋真的讓我感到非常感動,今后我也會努力學習并分享各種知識(寒假我會選擇分享一些獨立游戲制作的內容,比如unity3d和GMS2),力求能夠幫助到有需要的人!謝謝!!(鞠躬

總結

以上是生活随笔為你收集整理的【BUAA_CO_LAB】p5p6碎碎念的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。