Verilog流水線CPU設計(超詳細)

上篇:Verilog單週期CPU設計(超詳細)
本篇完整工程下載鏈接,已於19.12.17更新

一、設計目的與要求

1.1 實驗內容

  1. 本實例所設計CPU的指令格式的擬定。
  2. 基本功能部件的設計與實現。
  3. CPU各主要功能部件的設計與實現。
  4. CPU的封裝。
  5. 瞭解提高CPU性能的方法。
  6. 掌握流水線MIPS微處理器的工作原理。
  7. 理解數據冒險、控制冒險的概念以及流水線衝突的解決方法。
  8. 掌握流水線MIPS微處理器的測試方法。

1.2 實驗要求

  1. 至少支持add、sub、and、or、addi、andi、ori、lw、sw、beq、bne十一條指令。
  2. 採用5級流水線技術,具有數據前推機制。

1.3 實驗創新

  1. 寄存器堆的寫操作提前半個時鐘週期。
  2. 完成Lw指令的數據冒險的解決。
  3. 完成控制冒險的解決。

二、課程設計器材

2.1 硬件平臺

2.2 軟件平臺

  1. 操作系統:Win 10。
  2. 開發平臺:Vivado 2017.2。
  3. 編程語言:VerilogHDL硬件描述語言。

三、CPU邏輯設計總體方案

流水線是數字系統中一種提高系統穩定性和工作速度的方法,廣泛應用於高檔CPU的架構中。根據MIPS處理器的特點,將整體的處理過程分爲取指令(IF)、指令譯碼(ID)、執行(EX)、存儲器訪問(MEM)和寄存器會寫(WB)五級,對應多週期的五個處理階段。一個指令的執行需要5個時鐘週期,每個時鐘週期的上升沿來臨時,此指令所代表的一系列數據和控制信息將轉移到下一級處理。
在這裏插入圖片描述
圖3-1 流水線

3.1 數據通路

(1) IF級:取指令部分。
包括指令儲存器和PC寄存器及其更新模塊,負責根據PC寄存器的值從指令存儲器中取出指令編碼和對PC的值進行更新。
(2) ID級:指令譯碼部分。
根據獨處的指令編碼形成控制信號和讀寄存器堆輸出的寄存器的值。
流水線冒險檢測也在該級進行,冒險檢測電路需要上一條指令的MemRead,即在檢測到冒險條件成立時,冒險檢測電路產生stall信號清空ID/EX寄存器,插入一個流水線氣泡。
(3) EX級:執行部分。
根據指令的編碼進行算數或者邏輯運算或者計算條件分支指令的跳轉目標地址。
此外LW、SW指令所用的RAM訪問地址也是在本級上實現。控制信號有ALUCode、ALUSrcA、ALUScrB和RegDst,根據這些信號確定ALU操作、選擇兩個ALU操作數A、B,並確定目標寄存器。另外,數據轉發也在該級完成。數據轉發控制電路產生ForwardA和ForwardB兩組控制信號。
(4) MEM級:存儲器訪問部分。
只有在執行LW、SW指令時纔對存儲器進行讀寫,對其他指令只起到一個週期的作用。該級只需存儲器寫操作允許信號MemWrite。
(5) WB級:寄存器堆寫回部分。
該級把指令執行的結果回寫到寄存器文件中。
該級設置信號MemtoReg和寄存器寫操作允許信號RegWrite,其中MemtoReg決定寫入寄存器的數據來自於MEM級上的緩衝值或來自於MEM級上的存儲器。
在這裏插入圖片描述
在這裏插入圖片描述
圖3.1-1 數據通路

3.2 MIPS指令格式

MIPS指令系統結構有MIPS-32和MIPS-64兩種。本實驗的MIPS指令選用MIPS-32。以下所說的MIPS指令均指MIPS-32。
MIPS的指令格式爲32位。圖3-3給出了MIPS指令的3種格式。
在這裏插入圖片描述

圖3-2 MIPS指令格式

本實驗只選取了11條典型的MIPS指令來描述CPU邏輯電路的設計方法。表3-1列出了本實驗的所涉及到的11條MIPS指令。
在這裏插入圖片描述
R型指令的op均爲0,具體操作由func指定。rs和rt是源寄存器號,rd是目的寄存器號。移位指令中使用sa指定移位位數。
I型指令的低16位是立即數,計算時需擴展到32位,依指令的不同需進行零擴展和符號擴展。

3.3 流水線CPU結構設計圖

在這裏插入圖片描述
圖3-4 流水線CPU總體結構圖

圖3-4是一個簡單的基本上能夠在流水線上完成所要求設計的指令功能的數據通路和必要的控制線路圖。其中指令儲存在指令儲存器,數據儲存在數據存儲器。而且實現了數據冒險和控制冒險的解決方案。但是此結構僅解決了數據冒險,沒有解決控制冒險,若要想解決控制冒險,需要把EX級的移位器和計算分支目標地址的加法器前移到ID。在下文中會給出具體的解決方法。

3.4 流水線CPU邏輯流程圖

根據實驗原理中的流水線CPU總體結構圖,我們可以清楚的知道流水線CPU的設計應包括五級。其中爲了運行整個CPU還需要加入一個頂層模塊(MAIN)來調用這些模塊,所以自然地,這些模塊爲頂層模塊的子模塊。設計流程邏輯圖如下(左邊爲流水線寄存器組,右邊爲各級模塊)。
在這裏插入圖片描述

3.5 變量命名

由於在流水線中,數據和控制信息將在時鐘週期的上升沿轉移到下一級,所以規定流水線轉移變量命名爲:名稱_流水線級名稱。

四、冒險解決策略

4.1 數據冒險

在基本流水線中相鄰兩條指令的前一條指令還沒有更新目的寄存器時,後一條指令就已經先讀取了該寄存器的舊值,使得指令的計算結果出現錯誤。這樣相關的問題就稱爲數據冒險。下面給出本實驗所使用的解決方法。

4.1.1 寄存器堆的寫操作提前半個時鐘週期

在未提前半週期時CPU中,指令在時鐘週期結束時的上升沿將ID級寄存器的值鎖存進ID/EX,或將ALU的計算結果更新寄存器的值。但是寄存器的讀、寫的操作時間實際上只有時鐘週期的一半。因此可以把寄存器堆的寫操作提前到時鐘週期中間的下降沿完成。那麼後半個時鐘週期就可以將寫入之後的值讀出。這樣做後,同一級的數據冒險得到解決。

4.1.2內部前推

運算指令的結果在EX級結束時,就已經鎖存在EX/MEM的Rd中,然而寄存器的值在ID結束時就已經鎖存在ID/EX,但在EX級才真正使用這些值。
所以爲了支持內部前推,需在ALU的兩個輸入端之前,分別增加一個多路選擇器和相應的數據通路,並檢測處於EX級指令的兩個源操作寄存器號是否和處於MEM級或WB級指令的目的寄存器號相等。下面給出檢測條件。

  1. [條件a] E_Rs == EX/MEM.Rd,
    判斷EX級指令的rs字段是否和MEM級指令的目的寄存器號相同
  2. [條件b] E_Rs == MEM/WB.Rd,
    判斷EX級指令的rs字段是否和WB級指令的目的寄存器號相同
  3. [條件c] (E_Rt == EX/MEM.Rd) & ((E_Inst == I_add) | (E_Inst == I_sub) | (E_Inst == I_and) | (E_Inst == I_or) | (E_Inst == sw) | (E_Inst == beq) | (E_Inst == bne))
    判斷EX級指令的rt字段是否和MEM級指令的目的寄存器號相同
  4. [條件d] (E_Rt == MEM/WB.Rd) & ((E_Inst == I_add) | (E_Inst == I_sub) | (E_Inst == I_and) | (E_Inst == I_or) | (E_Inst == sw) | (E_Inst == beq) | (E_Inst == bne))
    判斷EX級指令的rt字段是否和WB級指令的目的寄存器號相同

同時應該考慮以下幾種特殊情況。

  1. 某些指令可能不寫回寄存器,例如sw和beq指令,或者某些指令的寫信號被關閉。所以還需檢測處於MEM級或WB級指令的寄存器堆寫使能信號M_Wreg或W_Wreg是否有效。
  2. 寄存器$0始終爲0,不必考慮在$0上產生的數據冒險,即第三條指令分別與第一條、第二條指令存在數據冒險。按照執行邏輯,當第三條指令處於EX級時應選擇處於MEM級的第二條指令的前推。而不能選擇第一條前推。所以在判斷邏輯模塊的代碼實現時,應先判斷相鄰兩條指令是否存在數據冒險。

4.1.3 lw指令的數據冒險

內部前推有顯而易見的侷限性,因爲內部前推必須要求前一條指令在EX結束時更新,但是LW指令最早只能在WB級讀出寄存器的值。因此無法及時提供給下一條指令的EX級使用。
分析流水線時序圖,可以發現lw指令的下一條指令,需要阻塞一個時鐘週期,才能確保該指令能獲得正確的操作數值,下面給出具體解決方法。
檢測是否存在lw指令的數據冒險。
檢測單元仍放置於CONUNIT部件內,且Reg2reg信號可以唯一區分lw指令和其他指令。檢測條件可寫爲:
((Rs == E_Rd) | (Rt == E_Rd)) & (E_Reg2reg == 0) & (E_Rd != 0) & (E_Wreg == 1)

暫停流水線的實現:
我們可以通過插入氣泡來暫停流水線,也即是關閉PC寄存器和IF/ID流水線寄存器組的寫使能信號,而且將ID/EX流水線寄存器組的Clrn端口信號清零。

4.2 控制冒險

在處理beq和bne指令時,條件分支指令或者跳轉指令的後續指令有可能在目標地址形成之前或分支條件形成之前就已經進入流水線。這樣相關的問題就稱爲控制冒險。下面給出本實驗所使用的解決方法。

4.2.1 縮短分支的延遲

流水線中,儘早完成分支的決策,可以減少性能損失假設圖 中M_Z信號和分支目標地址的輸出階段從MEM級前移到EX級,那麼圖 中(下圖)的指令序列只需要被阻塞2個時鐘週期就能解決控制冒險因爲正確的目標地址在EX級形成,EX級結束時就更新了PC寄存器。
在這裏插入圖片描述
圖4.2-1 分支指令

之後通過如下四步解決控制冒險:

  • 在條件分支指令處於EX級時,判斷分支條件是否成立
  • 若成立,則控制部件CONUNIT的STALL端口(流水線阻塞)輸出高電平,IF/ID和ID/EX流水線寄存器組的Clrn端口輸入爲低電平
  • 在該時鐘週期結束時,IF/ID和ID/EX流水線寄存器組的內容清0,即變爲兩條空指令
  • 這樣在下一個時鐘週期開始時,正確的目標指令處於IF級,ID級和EX級都是編碼爲全0的空指令,條件分支指令進入到MEM級

五、 模塊詳細設計

5.1取指令部分(IF)

5.1.1 PCAdd4

  • 所處位置
    -在這裏插入圖片描述

  • 模塊功能
    作爲PC寄存器的更新信號。

  • 實現思路
    由於每條指令32位,所以增加一個32位加法器,固定與32位的立即數4進行相加,且得到的結果在當前時鐘信號的上升沿更新進PC寄存器。

  • 引腳及控制信號
    IF_Addr:當前指令地址,輸入端口(IF內循環)
    IF_PCadd4:下一條指令地址,輸出端口

  • 主要實現代碼
    1.PCAdd4

module PCadd4(PC_o,PCadd4);
input [31:0] PC_o;//偏移量
output [31:0] PCadd4;//新指令地址
CLA_32 cla32(PC_o,4,0, PCadd4, Cout);
endmodule

1.CLA_32

module CLA_32(X, Y, Cin, S, Cout);
input [31:0] X, Y; 
input Cin;   
output [31:0] S;
output Cout;
wire Cout0, Cout1, Cout2, Cout3, Cout4, Cout5, Cout6;    
CLA_4 add0 (X[3:0], Y[3:0], Cin, S[3:0], Cout0);
CLA_4 add1 (X[7:4], Y[7:4], Cout0, S[7:4], Cout1);
CLA_4 add2 (X[11:8], Y[11:8], Cout1, S[11:8], Cout2);
CLA_4 add3 (X[15:12], Y[15:12], Cout2, S[15:12], Cout3);
CLA_4 add4 (X[19:16], Y[19:16], Cout3, S[19:16], Cout4);
CLA_4 add5 (X[23:20], Y[23:20], Cout4, S[23:20], Cout5);
CLA_4 add6 (X[27:24], Y[27:24], Cout5, S[27:24], Cout6);
CLA_4 add7 (X[31:28], Y[31:28], Cout6, S[31:28], Cout);
Endmodule

1.CLA_4

module CLA_4(X, Y, Cin, S, Cout);
input [3:0] X;
input [3:0] Y;
input Cin;
output [3:0] S;
output Cout;
and get_0_0_0(tmp_0_0_0, X[0], Y[0]);
or get_0_0_1(tmp_0_0_1, X[0], Y[0]);
and get_0_1_0(tmp_0_1_0, X[1], Y[1]);
or get_0_1_1(tmp_0_1_1, X[1], Y[1]);

and get_0_2_0(tmp_0_2_0, X[2], Y[2]);
or get_0_2_1(tmp_0_2_1, X[2], Y[2]);
and get_0_3_0(tmp_0_3_0, X[3], Y[3]);
or get_0_3_1(tmp_0_3_1, X[3], Y[3]);
and get_1_0_0(tmp_1_0_0, ~tmp_0_0_0, tmp_0_0_1);
xor getS0(S0, tmp_1_0_0, Cin);
and get_1_1_0(tmp_1_1_0, ~tmp_0_1_0, tmp_0_1_1);
not get_1_1_1(tmp_1_1_1, tmp_0_0_0);
nand get_1_1_2(tmp_1_1_2, Cin, tmp_0_0_1);
nand get_2_0_0(tmp_2_0_0, tmp_1_1_1, tmp_1_1_2);
xor getS1(S1, tmp_1_1_0, tmp_2_0_0);
and get_1_2_0(tmp_1_2_0, ~tmp_0_2_0, tmp_0_2_1);
not get_1_2_1(tmp_1_2_1, tmp_0_1_0);
nand get_1_2_2(tmp_1_2_2, tmp_0_1_1, tmp_0_0_0);
nand get_1_2_3(tmp_1_2_3, tmp_0_1_1, tmp_0_0_1, Cin);
nand get_2_1_0(tmp_2_1_0, tmp_1_2_1, tmp_1_2_2, tmp_1_2_3);
xor getS2(S2, tmp_1_2_0, tmp_2_1_0);
and get_1_3_0(tmp_1_3_0, ~tmp_0_3_0, tmp_0_3_1);
not get_1_3_1(tmp_1_3_1, tmp_0_2_0);
nand get_1_3_2(tmp_1_3_2, tmp_0_2_1, tmp_0_1_0);
nand get_1_3_3(tmp_1_3_3, tmp_0_2_1, tmp_0_1_1, tmp_0_0_0);
nand get_1_3_4(tmp_1_3_4, tmp_0_2_1, tmp_0_1_1, tmp_0_0_1, Cin); 
nand get_2_2_0(tmp_2_2_0, tmp_1_3_1, tmp_1_3_2, tmp_1_3_3, tmp_1_3_4);
xor getS3(S3, tmp_1_3_0, tmp_2_2_0);
not get_1_4_0(tmp_1_4_0, tmp_0_3_0);
nand get_1_4_1(tmp_1_4_1, tmp_0_3_1, tmp_0_2_0);
nand get_1_4_2(tmp_1_4_2, tmp_0_3_1, tmp_0_2_1, tmp_0_1_0);
nand get_1_4_3(tmp_1_4_3, tmp_0_3_1, tmp_0_2_1, tmp_0_1_1, tmp_0_0_0);
nand get_1_4_4(tmp_1_4_4, tmp_0_3_1, tmp_0_2_1, tmp_0_1_1, tmp_0_0_1, Cin);
nand getCout(Cout, tmp_1_4_0, tmp_1_4_1, tmp_1_4_2, tmp_1_4_3,tmp_1_4_4);
assign S = {S3,S2,S1,S0};
endmodule

5.1.2 PC

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    用於給出指令在指令儲存器中的地址,且當發生數據冒險時,需要保持PC寄存器不變。

  • 實現思路
    爲實現穩定輸出,在時鐘信號的上升沿更新。在發生Lw指令的數據冒險和控制冒險時,PC寄存器應該消除錯誤指令,而且需要在此週期關閉PC寄存器。

  • 引腳及控制信號
    En:控制PC寄存器的開啓和關閉狀態,外部輸入信號。
    stall:判斷是否發生Lw指令數據冒險,輸入信號。
    Clk:時鐘週期,輸入信號
    Clrn:當存在Lw指令的數據冒險和控制冒險時爲零,輸入信號。
    IF_Result目標地址,輸入信號。
    IF_Addr:指令地址,輸出信號。

  • 主要實現代碼

module PC(IF_Result,Clk,En,Clrn,IF_Addr,stall);
input [31:0]IF_Result;
input Clk,En,Clrn,stall;
output [31:0] IF_Addr;
wire En_S;
assign En_S=En&~stall;
D_FFEC32 pc(IF_Result,Clk,En_S,Clrn,IF_Addr);
endmodule 

5.1.3 INSTMEM

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    依據當前pc,讀取指令寄存器中相對應地址Addr[6:2]的指令。

  • 實現思路
    將pc的輸入作爲敏感變量,當pc發生改變的時候,則進行指令的讀取,根據相關的地址,輸出指令寄存器中相對應的指令,且在設計指令的時候,要用到12條給出的指令且儘量合理。

  • 引腳及控制信號
    IF_Addr:指令地址,輸入信號
    IF_Inst:指令編碼,輸出信號

  • 主要實現代碼

module INSTMEM(Addr,Inst);//指令存儲器
input[31:0]Addr;
output[31:0]Inst;
wire[31:0]Rom[31:0];
assign Rom[5'h00]=32'h20010008;//addi $1,$0,8 $1=8 001000 00000 00001 0000000000001000
assign Rom[5'h01]=32'h3402000C;//ori $2,$0,12 $2=12
assign Rom[5'h02]=32'h00221820;//add $3,$1,$2 $3=20//數據冒險
assign Rom[5'h03]=32'h00412022;//sub $4,$2,$1 $4=4
assign Rom[5'h04]=32'h00222824;//and $5,$1,$2
assign Rom[5'h05]=32'h00223025;//or $6,$1,$2
assign Rom[5'h06]=32'h14220006;//bne $1,$2,6//00010100001000010000000000000110
assign Rom[5'h07]=32'h00221820;//add $3,$1,$2 $3=20
assign Rom[5'h08]=32'h00412022;//sub $4,$2,$1 $4=4
assign Rom[5'h09]=32'h10220002;// beq $1,$2,2
assign Rom[5'h0A]=32'h0800000D;// J 0D 
assign Rom[5'h0B]=32'hXXXXXXXX;
assign Rom[5'h0C]=32'hXXXXXXXX;
assign Rom[5'h0D]=32'hAD02000A;// sw $2 10($8) memory[$8+10]=12
assign Rom[5'h0E]=32'h8D04000A;//lw $4 10($8) $4=12
assign Rom[5'h0F]=32'h10440002;//beq $2,$4,2//lw數據冒險
assign Rom[5'h10]=32'h20210004;//addi $1,$1,4 //00100000001000010000000000000100
assign Rom[5'h11]=32'h00222824;//and $5,$1,$2
assign Rom[5'h12]=32'h14220006;//bne $1,$2,6
assign Rom[5'h13]=32'h30470009;//andi $2,9,$7//控制冒險
assign Rom[5'h14]=32'hXXXXXXXX;
assign Rom[5'h15]=32'hXXXXXXXX;
assign Rom[5'h16]=32'hXXXXXXXX;
assign Rom[5'h17]=32'hXXXXXXXX;
assign Rom[5'h18]=32'hXXXXXXXX;
assign Rom[5'h19]=32'hXXXXXXXX;
assign Rom[5'h1A]=32'hXXXXXXXX;
assign Rom[5'h1B]=32'hXXXXXXXX;
assign Rom[5'h1C]=32'hXXXXXXXX;
assign Rom[5'h1D]=32'hXXXXXXXX;
assign Rom[5'h1E]=32'hXXXXXXXX;
assign Rom[5'h1F]=32'hXXXXXXXX;
assign Inst=Rom[Addr[6:2]];
Endmodule

5.1.4 MUX4X32

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    實現目標地址的選擇

  • 實現思路
    目標地址可能是PC+4,也可能是beq和bne的跳轉地址或是J型跳轉地址,所以採用一個32位四選一多路選擇器

  • 引腳及控制信號
    IF_PCadd4:PC+4的地址,輸入信號。
    0:空位,輸入信號。
    mux4x32_2:beq和bne指令的跳轉地址,輸入信號。
    0:空位,輸入信號。(InstL2:J指令的跳轉地址,輸入信號)
    Pcsrc:對地址進行選擇的控制信號,輸入信號
    IF_Result:目標地址,輸出信號

  • 主要實現代碼

module MUX4X32 (A0, A1, A2, A3, S, Y);
input [31:0] A0, A1, A2, A3;
input [1:0] S;
output [31:0] Y;
function [31:0] select;
input [31:0] A0, A1, A2, A3;
input [1:0] S;
case(S)
2'b00: select = A0;
2'b01: select = A1;
2'b10: select = A2;
2'b11: select = A3;
endcase
endfunction
assign Y = select (A0, A1, A2, A3, S);
endmodule

5.1.5 REG_ifid

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    寄存IF級的輸出指令,分割IF級和ID級的指令或控制信號,防止相互干擾,在IF級執行結束時將指令的控制信號傳遞至下一級。

  • 引腳及控制信號
    IF_PCadd4:下一條指令地址,輸入信號。
    IF_Inst:本條指令編碼,輸入信號。
    En:寫使能信號,輸入信號。
    Clk:時鐘信號,輸入信號。
    Clrn:清零信號,輸入信號。
    ID_PCadd4:下條指令地址,輸出信號。
    ID_Inst:本條指令編碼,輸出信號。
    Stall:記錄是否存在Lw數據冒險,輸出信號。
    Stall:記錄是否存在控制冒險,輸出信號。

  • 主要實現代碼

module REG_ifid(D0,D1,En,Clk,Clrn,Q0,Q1,stall,condep);
input [31:0] D0,D1;
input En,Clk,Clrn;
input stall,condep;
output [31:0] Q0,Q1;
wire En_S,Clrn_C;
assign En_S=En&~stall;
assign Clrn_C=Clrn&~condep;
D_FFEC32 q0(D0,Clk,En_S,Clrn_C,Q0);
D_FFEC32 q1(D1,Clk,En_S,Clrn_C,Q1);
Endmodule

5.2指令譯碼部分(ID)

5.2.1 CONUNIT

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    控制器是作爲CPU控制信號產生的器件,通過通過解析op得到該指令的各種控制信號,使其他器件有效或無效。

  • 實現思路
    在單週期CONUNIT的基礎上,需要增加一些功能,如判斷處於ID級的指令和處於EX級或MEM級的指令是否存在數據冒險,是否存在Lw數據冒險,是否存在控制冒險。

  • 引腳及控制信號
    M_Op;MEM級指令的OP字段,輸入信號。
    M_Z;MEM級指令的Z值。stall
    ID_Inst[31:26]:Op,輸入信號。
    ID_Inst[5:0]:Func,輸入信號。
    Z:零標誌信號,對Pcsrc有影響,輸入信號。
    Regrt:控制輸入寄存器的Wr端口,輸出信號。
    Se:控制擴展模塊,輸出信號。
    Wreg:控制寄存器端的寫使能信號,輸出信號。
    Aluqb:控制ALU的Y端口的輸入值,輸出信號。
    Aluc:控制ALU的計算種類,輸出信號。
    Wmem:控制數據存儲器的寫使能信號,輸出信號。
    Pcsrc:控制目標指令地址,輸出信號。
    Reg2reg:控制REHFILE更新值的來源,輸出信號。
    ID_Inst[25:21]:ID指令的rs字段,輸入信號。
    ID_Inst[20:16]:ID指令的rt字段,輸入信號。
    E_Rd[4:0]:EX級輸出的目的寄存器號,輸入信號。
    M_Rd[4:0]:MEM級輸出的目的寄存器號,輸入信號。
    E_Wreg:EX級的寫使能信號,輸入信號。
    M_Wreg:MEM級寫使能信號,輸入信號。
    FwdA[1:0]:判斷是否在rs寄存器發生數據冒險,輸出信號。
    FwdB[1:0]:判斷是否在rt寄存器發生數據冒險,輸出信號。
    E:寫使能信號,外部輸入信號

  • 主要實現代碼

module CONUNIT(E_Op,Op,Func,Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg,Rs,Rt,E_Rd,M_Rd,E_Wreg,M_Wreg,FwdA,FwdB,E_Reg2reg,stall,condep);
input [5:0]Op,Func,E_Op;
input Z;
input E_Wreg,M_Wreg,E_Reg2reg;
input [4:0]E_Rd,M_Rd,Rs,Rt;
output Regrt,Se,Wreg,Aluqb,Wmem,Reg2reg,stall,condep;
output [1:0]Pcsrc,Aluc;
output reg [1:0]FwdA,FwdB;
wire R_type=~|Op;
wire I_add=R_type&Func[5]&~Func[4]&~Func[3]&~Func[2]&~Func[1]&~Func[0];
wire I_sub=R_type&Func[5]&~Func[4]&~Func[3]&~Func[2]&Func[1]&~Func[0];
wire I_and=R_type&Func[5]&~Func[4]&~Func[3]&Func[2]&~Func[1]&~Func[0];
wire I_or=R_type&Func[5]&~Func[4]&~Func[3]&Func[2]&~Func[1]&Func[0];
wire I_addi=~Op[5]&~Op[4]&Op[3]&~Op[2]&~Op[1]&~Op[0];
wire I_andi=~Op[5]&~Op[4]&Op[3]&Op[2]&~Op[1]&~Op[0];
wire I_ori=~Op[5]&~Op[4]&Op[3]&Op[2]&~Op[1]&Op[0];
wire I_lw=Op[5]&~Op[4]&~Op[3]&~Op[2]&Op[1]&Op[0];
wire I_sw=Op[5]&~Op[4]&Op[3]&~Op[2]&Op[1]&Op[0];
wire I_beq=~Op[5]&~Op[4]&~Op[3]&Op[2]&~Op[1]&~Op[0];
wire I_bne=~Op[5]&~Op[4]&~Op[3]&Op[2]&~Op[1]&Op[0];
wire E_beq=~E_Op[5]&~E_Op[4]&~E_Op[3]&E_Op[2]&~E_Op[1]&~E_Op[0];
wire E_bne=~E_Op[5]&~E_Op[4]&~E_Op[3]&E_Op[2]&~E_Op[1]&E_Op[0];
wire I_J=~Op[5]&~Op[4]&~Op[3]&~Op[2]&Op[1]&~Op[0];
wire E_Inst = I_add|I_sub|I_and|I_or|I_sw|I_beq|I_bne;
assign Regrt = I_addi|I_andi|I_ori|I_lw|I_sw|I_beq|I_bne|I_J;
assign Se = I_addi|I_lw|I_sw|I_beq|I_bne;
assign Wreg = I_add|I_sub|I_and|I_or|I_addi|I_andi|I_ori|I_lw;
assign Aluqb = I_add|I_sub|I_and|I_or|I_beq|I_bne|I_J;
assign Aluc[1] = I_and|I_or|I_andi|I_ori;
assign Aluc[0] = I_sub|I_or|I_ori|I_beq|I_bne;
assign Wmem = I_sw;
assign Pcsrc[1] = (E_beq&Z)|(E_bne&~Z)|I_J;
assign Pcsrc[0] = I_J;
assign Reg2reg = I_add|I_sub|I_and|I_or|I_addi|I_andi|I_ori|I_sw|I_beq|I_bne|I_J;
always@(E_Rd,M_Rd,E_Wreg,M_Wreg,Rs,Rt)begin
   FwdA=2'b00;
   if((Rs==E_Rd)&(E_Rd!=0)&(E_Wreg==1))begin
       FwdA=2'b10;
   end else begin
       if((Rs==M_Rd)&(M_Rd!=0)&(M_Wreg==1))begin
           FwdA=2'b01;
       end
   end
end
always@(E_Rd,M_Rd,E_Wreg,M_Wreg,Rs,Rt)begin
   FwdB=2'b00;
   if((Rt==E_Rd)&(E_Rd!=0)&(E_Wreg==1))begin
              FwdB=2'b10;
   end else begin
       if((Rt==M_Rd)&(M_Rd!=0)&(M_Wreg==1))begin
           FwdB=2'b01;
       end
   end
end
assign stall=((Rs==E_Rd)|(Rt==E_Rd))&(E_Reg2reg==0)&(E_Rd!=0)&(E_Wreg==1);
assign condep=(E_beq&Z)|(E_bne&~Z);
endmodule

5.2.2 MUX2X5

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    R型指令和I行指令的Wr信號不同,所以需要一個5位二選一選擇器進行
    選擇。

  • 實現思路
    R型指令Wr選擇rd信號,I型指令Wr選擇rt信號。

  • 引腳及控制信號
    ID_Inst[15:11],:R型指令的rd信號,輸入信號
    ID_Inst[20:16]:I型指令的rt信號,輸入信號
    Regrt:選擇指令的控制信號,輸入信號
    ID_Wr:Wr信號,輸出信號

  • 主要實現代碼

module MUX2X5(A0,A1,S,Y);
input [4:0] A0,A1;
input S;
output [4:0] Y;
function [4:0] select;
input [4:0] A0,A1;
input S;
case(S)
0:select=A0;
1:select=A1;
endcase
endfunction
assign Y=select(A0,A1,S);
endmodule

5.2.3 EXT16T32

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    I指令的addi需要對立即數進行符號拓展,andi和ori需要對立即數進行零擴展,所以需要一個擴展模塊。

  • 實現思路
    採用一個16位擴展成32位的擴展模塊EXT16T32,實現零擴展和符號擴展。

  • 引腳及控制信號
    ID_Inst[15:0]:I型指令的立即數字段,輸入信號。
    Se:選擇零擴展或是符號擴展的控制模塊,輸入信號。
    ID_EXTIMM:擴展後的立即數,輸出信號。

  • 主要實現代碼

module EXT16T32 (X, Se, Y);
input [15:0] X;
input Se;
output [31:0] Y;
wire [31:0] E0, E1;
wire [15:0] e = {16{X[15]}};
parameter z = 16'b0;
assign E0 = {z, X};
assign E1 = {e, X};
MUX2X32 i(E0, E1, Se, Y);
endmodule   

5.2.4 REGFILE

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    給出要讀取的兩個寄存器編號和要寫入的寄存器編號,然後由Qa和Qb端口更新Ra和Rb端口的輸入編號分別輸入其值。

  • 實現思路
    由32個寄存器組成,增加兩個端口用於接收要讀取的兩個寄存器編號,另一個端口用於接收要寫入的寄存器的編號。且在時鐘上升沿將D寫入。

  • 引腳及控制信號
    ID_Inst[25:21]:讀取寄存器編號1,輸入信號。
    ID_Inst[20:16]:讀取寄存器編號2或立即數,輸入信號。
    D:寄存器更新值,輸入信號。
    W_Wr:寫入寄存器編號3,輸入信號。
    W_Wreg:寫使能信號,爲0的時候不能寫入,D值不更新,爲1的時候能寫入,D值更新,輸入信號。
    Clk:時鐘週期,輸入信號。
    Clrn:清零信號,輸入信號。
    ID_Qa:輸出寄存器1的值,輸入信號。
    ID_Qb:輸出寄存器2的值,輸入信號。

  • 主要實現代碼

module REGFILE(Ra,Rb,D,Wr,We,Clk,Clrn,Qa,Qb);
input [4:0]Ra,Rb,Wr;
input [31:0]D;
input We,Clk,Clrn;
output [31:0]Qa,Qb;
wire [31:0]Y_mux,Q31_reg32,Q30_reg32,Q29_reg32,Q28_reg32,Q27_reg32,Q26_reg32,Q25_reg32,Q24_reg32,Q23_reg32,Q22_reg32,Q21_reg32,Q20_reg32,Q19_reg32,Q18_reg32,Q17_reg32,Q16_reg32,Q15_reg32,Q14_reg32,Q13_reg32,Q12_reg32,Q11_reg32,Q10_reg32,Q9_reg32,Q8_reg32,Q7_reg32,Q6_reg32,Q5_reg32,Q4_reg32,Q3_reg32,Q2_reg32,Q1_reg32,Q0_reg32;

DEC5T32E dec(Wr,We,Y_mux);

REG32  A(D,Y_mux,Clk,Clrn,Q31_reg32,Q30_reg32,Q29_reg32,Q28_reg32,Q27_reg32,Q26_reg32,Q25_reg32,Q24_reg32,Q23_reg32,Q22_reg32,Q21_reg32,Q20_reg32,Q19_reg32,Q18_reg32,Q17_reg32,Q16_reg32,Q15_reg32,Q14_reg32,Q13_reg32,Q12_reg32,Q11_reg32,Q10_reg32,Q9_reg32,Q8_reg32,Q7_reg32,Q6_reg32,Q5_reg32,Q4_reg32,Q3_reg32,Q2_reg32,Q1_reg32,Q0_reg32);

MUX32X32 select1(Q0_reg32,Q1_reg32,Q2_reg32,Q3_reg32,Q4_reg32,Q5_reg32,Q6_reg32,Q7_reg32,Q8_reg32,Q9_reg32,Q10_reg32,Q11_reg32,Q12_reg32,Q13_reg32,Q14_reg32,Q15_reg32,Q16_reg32,Q17_reg32,Q18_reg32,Q19_reg32,Q20_reg32,Q21_reg32,Q22_reg32,Q23_reg32,Q24_reg32,Q25_reg32,Q26_reg32,Q27_reg32,Q28_reg32,Q29_reg32,Q30_reg32,Q31_reg32,Ra,Qa);
MUX32X32 select2(Q0_reg32,Q1_reg32,Q2_reg32,Q3_reg32,Q4_reg32,Q5_reg32,Q6_reg32,Q7_reg32,Q8_reg32,Q9_reg32,Q10_reg32,Q11_reg32,Q12_reg32,Q13_reg32,Q14_reg32,Q15_reg32,Q16_reg32,Q17_reg32,Q18_reg32,Q19_reg32,Q20_reg32,Q21_reg32,Q22_reg32,Q23_reg32,Q24_reg32,Q25_reg32,Q26_reg32,Q27_reg32,Q28_reg32,Q29_reg32,Q30_reg32,Q31_reg32,Rb,Qb);

Endmodule

5.2.5 REG_idex

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    寄存ID級的輸出指令,分割ID級和EX級的指令或控制信號,防止相互干擾,在ID級執行結束時將指令的控制信號傳遞至下一級。

  • 引腳及控制信號
    Wreg,Reg2reg,Wmem,ID_Inst[31:26],Aluc,Aluqb,ID_PCadd4,ID_Qa,ID_Qb,ID_EXTIMM,ID_Wr,En,Clk,Clrn,E_Wreg,E_Reg2reg,E_Wmem,E_Op,E_Aluc,E_Aluqb,E_PC,E_R1,E_R2,E_I,E_Rd,FwdA,FwdB,E_FwdA,E_FwdB,stall

  • 主要實現代碼

module REGidex(D13,D0,D1,D2,D3,D4,D5,D7,D8,D9,D10,En,Clk,Clrn,Q0,Q1,Q2,Q3,Q4,Q5,Q7,Q8,Q9,Q10,D11,D12,Q11,Q12,stall,Q13,condep);
input [31:0] D7,D8,D9,D13;
input [5:0]D3;
input [4:0]D10;
input [1:0]D4,D11,D12;
input D0,D1,D2,D5;
input En,Clk,Clrn,stall,condep;
wire Clrn_SC;
assign Clrn_SC=Clrn&~stall&~condep;
output [31:0] Q7,Q8,Q9,Q13;
output [5:0] Q3;
output [4:0]Q10;
output [1:0]Q4,Q11,Q12;
output Q0,Q1,Q2,Q5;

D_FFEC q0(D0,Clk,En,Clrn_SC,Q0);
D_FFEC q1(D1,Clk,En,Clrn_SC,Q1);
D_FFEC q2(D2,Clk,En,Clrn_SC,Q2);
D_FFEC6 q3(D3,Clk,En,Clrn_SC,Q3);
D_FFEC2 q4(D4,Clk,En,Clrn_SC,Q4);
D_FFEC q5(D5,Clk,En,Clrn_SC,Q5);
// D_FFEC32 q6(D6,Clk,En,Clrn_SC,Q6);
D_FFEC32 q7(D7,Clk,En,Clrn_SC,Q7);
D_FFEC32 q8(D8,Clk,En,Clrn_SC,Q8);
D_FFEC32 q9(D9,Clk,En,Clrn_SC,Q9);
D_FFEC5 q10(D10,Clk,En,Clrn_SC,Q10);
D_FFEC2 q11(D11,Clk,En,Clrn_SC,Q11);
D_FFEC2 q12(D12,Clk,En,Clrn_SC,Q12);
D_FFEC32 q13(D13,Clk,En,Clrn_SC,Q13);
Endmodule

5.3 執行部分(EX)

5.3.1 SHIFTER32_L2

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    使用32位移位器SHIFTER32,固定左移兩位即可

  • 實現思路
    一個固定左移兩位的移位器

  • 引腳及控制信號
    E_I:指令中的偏移量,輸入信號
    E_I_L2:偏移量左移後的結果,輸出信號

  • 主要實現代碼

module SHIFTER32_L2(X,Sh);
input [31:0] X;
output [31:0] Sh;
parameter z=2'b00;
assign Sh={X[29:0],z};
endmodule

5.3.2 MUX4X32

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    在內部前推的工作中,ALU的兩個輸入端都需要選擇來自
    ID/EX寄存器或者EX/MEM寄存器或者MEM/WB寄存器所鎖存的值

  • 實現思路
    通過控制模塊的控制信號FwdA和FwdB來選擇。輸出信號輸入同級的ALU進行計算。

  • 引腳及控制信號
    1)
    E_R1;來自Qa端口的信號,輸入信號。
    D;來自WB級的信號,輸入信號
    M_R;來自MEM級的信號,輸入信號。
    0;空信號。
    E_FwdA;選擇信號。
    Alu_X:作爲ALU中X端的信號,輸出信號。
    2)
    E_R2:來自Qb端口的信號,輸入信號。
    D:;來自WB級的信號,輸入信號。
    M_R: 來自MEM級的信號,輸入信號。
    0:空信號
    E_FwdB:選擇信號
    E_NUM:,選擇的寄存器鎖存的值和立即數做選擇,後輸作爲ALU中Y端的信號,輸出信號。
    在這裏插入圖片描述

  • 主要實現代碼

module MUX4X32 (A0, A1, A2, A3, S, Y);
input [31:0] A0, A1, A2, A3;
input [1:0] S;
output [31:0] Y;
function [31:0] select;
input [31:0] A0, A1, A2, A3;
input [1:0] S;
case(S)
2'b00: select = A0;
2'b01: select = A1;
2'b10: select = A2;
2'b11: select = A3;
endcase
endfunction
assign Y = select (A0, A1, A2, A3, S);
endmodule

5.3.3 MUX2X32

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    ALU的Y端輸入信號種類根據指令的不同而不同

  • 實現思路
    在執行R型指令時,ALU的Y端輸入信號可能來自Qb,在進行內部前推後變成FwdA端信號。在執行I型指令的addi,andi和ori指令時時,ALU的Y端輸入信號來自EXT16T32,所以需要一個二選一選擇器。

  • 引腳及控制信號
    E_I: 來自EXT16T32的信號,輸入信號。
    E_NUM: 來自FwdB端口的信號,輸入信號。
    E_Aluqb: 控制信號。
    Y: 輸入ALU進行後續計算的信號,輸出信號。

  • 主要實現代碼

module MUX2X32(A0,A1,S,Y);
input [31:0] A0,A1;
input S;
output [31:0] Y;
function [31:0] select;
input [31:0] A0,A1;
input S;
case(S)
0:select=A0;
1:select=A1;
endcase
endfunction
assign Y=select(A0,A1,S);
endmodule

5.3.4 ALU

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    算數邏輯部件,需要實現加,減,按位與,按位或。

  • 實現思路
    需要2位控制信號控制運算類型,核心部件是32位加法器ADDSUB_32。

  • 引腳及控制信號
    Alu_X:寄存器1的值。來自REG_idex,輸入信號。
    Y:寄存器2的值或立即數,輸入信號。
    E_Aluc:控制信號。
    E_R:輸入寄存器端口D的計算結果,輸出信號。
    E_Z:當值爲1時代表兩個輸入信號值相等,當值爲0時代表兩個輸入信號不等,輸出信號。

  • 主要實現代碼

module ALU(X,Y,Aluc,R,Z);//ALU代碼
input [31:0]X,Y;
input [1:0]Aluc;
output [31:0]R;
output Z;
wire[31:0]d_as,d_and,d_or,d_and_or;
ADDSUB_32 as(X,Y,Aluc[0],d_as);
assign d_and=X&Y;
assign d_or=X|Y;
MUX2X32 select1(d_and,d_or,Aluc[0],d_and_or);
MUX2X32 seleted(d_as,d_and_or,Aluc[1],R);
assign Z=~|R;
endmodule

5.3.5 CLA_32

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    在beq和bne指令時如果發生地址的跳轉,需要使用32位加法器,跳轉地址是pc+4的地址和立即數之和的結果。

  • 實現思路
    輸入信號是pc+4,和左移後的立即數,結果作爲beq和bne的跳轉地址。

  • 引腳及控制信號
    E_PC:REG_idex寄存的PCadd4的值,輸入信號。
    E_I_L2:左移後的立即數,輸入信號。
    0:進位信號,初始爲0,輸入信號。
    EX_PC:作爲beq和bne的跳轉地址,輸出信號。
    Cout: 進位信息,輸出信號。

  • 主要實現代碼

module CLA_32(X, Y, Cin, S, Cout);
   input [31:0] X, Y; 
   input Cin;   
   output [31:0] S;
   output Cout;
   wire Cout0, Cout1, Cout2, Cout3, Cout4, Cout5, Cout6;    
   CLA_4 add0 (X[3:0], Y[3:0], Cin, S[3:0], Cout0);
   CLA_4 add1 (X[7:4], Y[7:4], Cout0, S[7:4], Cout1);
   CLA_4 add2 (X[11:8], Y[11:8], Cout1, S[11:8], Cout2);
   CLA_4 add3 (X[15:12], Y[15:12], Cout2, S[15:12], Cout3);
   CLA_4 add4 (X[19:16], Y[19:16], Cout3, S[19:16], Cout4);
   CLA_4 add5 (X[23:20], Y[23:20], Cout4, S[23:20], Cout5);
   CLA_4 add6 (X[27:24], Y[27:24], Cout5, S[27:24], Cout6);
   CLA_4 add7 (X[31:28], Y[31:28], Cout6, S[31:28], Cout);
endmodule

5.3.6 REG_exmem

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    寄存EX級的輸出指令,分割EX級和MEM級的指令或控制信號,防止相互干擾,在EX級執行結束時將指令的控制信號傳遞至下一級。

  • 引腳及控制信號
    E_Wreg,E_Reg2reg,E_Wmem,E_Op,EX_PC,E_Z,E_R,E_R2,E_Rd,En,Clk,Clrn,M_Wreg,M_Reg2reg,M_Wmem,M_Op,M_PC,M_Z,M_R,M_S,M_Rd

  • 主要實現代碼

module REGexmem(D0,D1,D2,D3,D4,D5,D6,D7,D8,En,Clk,Clrn,Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8);
input [31:0] D4,D6,D7;
input [5:0]D3;
input [4:0]D8;
input D0,D1,D2,D5;

input En,Clk,Clrn;
output [31:0] Q4,Q6,Q7;
output [5:0] Q3;
output [4:0]Q8; 
output Q0,Q1,Q2,Q5;

D_FFEC q0(D0,Clk,En,Clrn,Q0);
D_FFEC q1(D1,Clk,En,Clrn,Q1);
D_FFEC q2(D2,Clk,En,Clrn,Q2);
D_FFEC6 q3(D3,Clk,En,Clrn,Q3);
D_FFEC32 q4(D4,Clk,En,Clrn,Q4);
D_FFEC q5(D5,Clk,En,Clrn,Q5);
D_FFEC32 q6(D6,Clk,En,Clrn,Q6);
D_FFEC32 q7(D7,Clk,En,Clrn,Q7);
D_FFEC5 q8(D8,Clk,En,Clrn,Q8);

endmodule

5.4存儲器訪問部分(MEM)

5.4.1 DATAMEMß

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    數據存儲器,通過控制信號,對數據寄存器進行讀或者寫操作,並且此處模塊額外合併了輸出DB的數據選擇器,此模塊同時輸出寫回寄存器組的數據DB。

  • 實現思路
    由於需要支持取數/存數指令,所以要在指令儲存器的基礎上增加寫入數據的數據寫入端口,寫使能信號。又因爲寫操作在時鐘信號的上升沿,所以要增加時鐘信號。

  • 引腳及控制信號
    在這裏插入圖片描述
    當We爲1時,進行sw指令操作,此時Din端口輸入信號實際爲rt,Addr端口輸入信號爲rs和偏移量相加的地址,在時鐘週期上升沿將rt的值寫入改地址的儲存單元。
    當We爲0時,進行lw指令操作,此時Addr端口輸入信號爲rs和偏移量相加的地址,Dout爲讀取該地址儲存器的內容。

  • 主要實現代碼

module DATAMEM(Addr,Din,Clk,We,Dout);
input[31:0]Addr,Din;
input Clk,We;
output[31:0]Dout;
reg[31:0]Ram[31:0];
assign Dout=Ram[Addr[6:2]];
always@(posedge Clk)begin
if(We)Ram[Addr[6:2]]<=Din;
end
integer i;
initial begin
for(i=0;i<32;i=i+1)
Ram[i]=0;
end
endmodule

5.4.2 REG_memwb

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    數據存儲器,通過控制信號,對數據寄存器進行讀或者寫操作,並且此處模塊額外合併了輸出DB的數據選擇器,此模塊同時輸出寫回寄存器組的數據DB。

  • 引腳及控制信號
    M_Wreg,M_Reg2reg,M_R,Dout,M_Rd,En,Clk,Clrn,W_Wreg,W_Reg2reg,W_D,W_C,W_Wr

  • 主要實現代碼

module REGmemwb(D0,D1,D2,D3,D4,En,Clk,Clrn,Q0,Q1,Q2,Q3,Q4);
input D0,D1;
input [31:0] D2,D3;
input [4:0] D4;
input En,Clk,Clrn;
output Q0,Q1;
output [31:0] Q2,Q3;
output [4:0] Q4;
D_FFEC q0(D0,Clk,En,Clrn,Q0);
D_FFEC q1(D1,Clk,En,Clrn,Q1);
D_FFEC32 q2(D2,Clk,En,Clrn,Q2);
D_FFEC32 q3(D3,Clk,En,Clrn,Q3);
D_FFEC5 q4(D4,Clk,En,Clrn,Q4);
Endmodule

5.5寄存器堆寫回部分(WB)

5.5.1 MUX2X32

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    對寫入寄存器的數據進行選擇

  • 實現思路
    在lw指令中,需要將DATAMEM中選中儲存器的值保存到REGFILE的寄存器中,而其他會更新REGFILE的指令的更新信號來自於ALU的R輸出端。所以需要一個二選一選擇器進行選擇。

  • 引腳及控制信號
    W_C:DATAMEM的輸出值,輸入信號
    W_D:ALU的輸出值,輸入信號
    W_Reg2reg:控制信號
    D:寫入R寄存器堆D端的信號,輸出信號

  • 主要實現代碼

module MUX2X32(A0,A1,S,Y);
input [31:0] A0,A1;
input S;
output [31:0] Y;
function [31:0] select;
input [31:0] A0,A1;
input S;
case(S)
0:select=A0;
1:select=A1;
endcase
endfunction
assign Y=select(A0,A1,S);
endmodule

5.6 頂層模塊

  • 所處位置
    在這裏插入圖片描述

  • 模塊功能
    實現CPU的封裝,設計輸出信號使得在方正時便於觀察其波形圖

  • 實現思路
    調用各個下層模塊並將他們的輸入和輸出連接到一起。

  • 引腳及控制信號
    CLk:時鐘週期,外部輸入信號。
    Clrn:清零信號,外部輸入信號。
    En:寫使能信號,外部輸入信號。

  • 主要實現代碼

module MAIN(Clk,En,Clrn,IF_ADDR,EX_X,EX_Y,EX_R);

input Clk,En,Clrn;
output[31:0] IF_ADDR,EX_R,EX_X,EX_Y;

wire [31:0] IF_Result,IF_Addr,IF_PCadd4,IF_Inst,D,ID_Qa,ID_Qb,ID_PCadd4,ID_Inst;
wire [31:0] E_R1,E_R2,E_I,E_I_L2,Y,E_R,EX_PC,M_PC,M_R,M_S,Dout,W_D,W_C,ID_EXTIMM,Alu_X,E_NUM,ID_EXTIMM_L2,ID_PC;
wire[5:0] E_Op;
wire [4:0] ID_Wr,W_Wr,E_Rd,M_Rd;
wire [1:0]Aluc,Pcsrc,E_Aluc,FwdA,FwdB,E_FwdA,E_FwdB;
wire Regrt,Se,Wreg,Aluqb,Reg2reg,Wmem,Z;
wire E_Wreg,E_Reg2reg,E_Wmem,E_Aluqb,Cout,M_Wreg,M_Reg2reg,M_Wmem,W_Wreg,W_Reg2reg,stall,condep;

//IF

MUX4X32 mux4x32(IF_PCadd4,0,EX_PC,0,Pcsrc,IF_Result);
PC pc(IF_Result,Clk,En,Clrn,IF_Addr,stall);
PCadd4 pcadd4(IF_Addr,IF_PCadd4);
INSTMEM instmem(IF_Addr,IF_Inst);

REG_ifid ifid(IF_PCadd4,IF_Inst,En,Clk,Clrn,ID_PCadd4,ID_Inst,stall,condep);

//ID
CONUNIT conunit(E_Op,ID_Inst[31:26],ID_Inst[5:0],Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg,ID_Inst[25:21],ID_Inst[20:16],E_Rd,M_Rd,E_Wreg,M_Wreg,FwdA,FwdB,E_Reg2reg,stall,condep);
MUX2X5 mux2x5(ID_Inst[15:11],ID_Inst[20:16],Regrt,ID_Wr);
EXT16T32 ext16t32(ID_Inst[15:0],Se,ID_EXTIMM);//ID_EXTIMM對應E_I
REGFILE regfile(ID_Inst[25:21],ID_Inst[20:16],D,W_Wr,W_Wreg,Clk,Clrn,ID_Qa,ID_Qb);
SHIFTER32_L2 shifter2(ID_EXTIMM,ID_EXTIMM_L2);//控制冒險
CLA_32 cla_32(ID_PCadd4,ID_EXTIMM_L2,0,ID_PC,Cout);//ID_PCadd4對應E_PC
REGidex idex(ID_PC,Wreg,Reg2reg,Wmem,ID_Inst[31:26],Aluc,Aluqb,ID_Qa,ID_Qb,ID_EXTIMM,ID_Wr,En,Clk,Clrn,E_Wreg,E_Reg2reg,E_Wmem,E_Op,E_Aluc,E_Aluqb,E_R1,E_R2,E_I,E_Rd,FwdA,FwdB,E_FwdA,E_FwdB,stall,EX_PC,condep);

//EX
//SHIFTER32_L2 shifter2(E_I,E_I_L2);
MUX4X32 mux4x32_ex_1(E_R1,D,M_R,0,E_FwdA,Alu_X);
MUX4X32 mux4x32_ex_2(E_R2,D,M_R,0,E_FwdB,E_NUM);
MUX2X32 mux2x321(E_I,E_NUM,E_Aluqb,Y);
ALU alu(Alu_X,Y,E_Aluc,E_R,Z);
//CLA_32 cla_32(E_PC,E_I_L2,0,EX_PC,Cout);

REGexmem exmem(E_Wreg,E_Reg2reg,E_Wmem,EX_PC,E_R,E_R2,E_Rd,En,Clk,Clrn,M_Wreg,M_Reg2reg,M_Wmem,M_PC,M_R,M_S,M_Rd);

//MEM
DATAMEM datamem(M_R,M_S,Clk,M_Wmem,Dout);

REGmemwb memwb(M_Wreg,M_Reg2reg,M_R,Dout,M_Rd,En,Clk,Clrn,W_Wreg,W_Reg2reg,W_D,W_C,W_Wr);

//WB
MUX2X32 mux2x322(W_C,W_D,W_Reg2reg,D);

assign IF_ADDR=IF_Addr;
assign EX_R=E_R;
assign EX_X=Alu_X;
assign EX_Y=Y;
endmodule

5.7 仿真模塊

  • 主要實現代碼
module TEST;
reg Clk;
reg En;
reg Clrn;

wire [31:0] IF_ADDR;
wire [31:0] EX_R;
wire [31:0] EX_X;
wire [31:0] EX_Y;
MAIN uut(
.Clk(Clk),
.En(En),
.Clrn(Clrn),
.IF_ADDR(IF_ADDR),
.EX_R(EX_R),
.EX_X(EX_X),
.EX_Y(EX_Y)
);
initial begin
Clk=0;Clrn=0;En=1;
#10;
Clk=1;Clrn=0;
#10;
Clrn=1;
Clk=0;
forever #20 Clk=~Clk;
end
endmodule

六、 仿真模擬分析

6.1 仿真波形圖

在這裏插入圖片描述

6.2 指令代碼分析

1)assign Rom[5’h00]=32’h20010008;
二進制源碼:001000 00000 00001 00000 00000 001000
指令含義:addi $1,$0,8
結果:$1=$0+8=8

2)assign Rom[5’h01]=32’h3402000C;//ori $2,$0,12 $2=12
二進制源碼:001101 00000 00010 00000 00000 001100
指令含義:ori $2,$0,12
結果:$2=12

3)assign Rom[5’h02]=32’h00221820;
指令含義:add $3,$1,$2
結果:$3=20,輸出值正確,代表一般的數據冒險得到解決。

4)assign Rom[5’h03]=32’h00412022;
指令含義:addi $1,$0,8
結果:$1=$0+8=8

5)assign Rom[5’h04]=32’h00222824;
指令含義:addi $1,$0,8
結果:$1=$0+8=8

6)assign Rom[5’h05]=32’h00223025;
指令含義:addi $1,$0,8
結果:$1=$0+8=8

7)assignRom[5’h06]=32’h14220006;
二進制源碼:00010100001000010000000000000110
指令含義:bne $1,$2,6
結果:跳轉到地址加6的位置

8)assign Rom[5’h07]=32’h00221820;
指令含義:add $3,$1,$2 $3=20
結果:未執行

9)assign Rom[5’h08]=32’h00412022;
指令含義:sub $4,$2,$1 $4=4
結果:未執行

10)assign Rom[5’h09]=32’h10220002;
指令含義:beq $1,$2,2
結果:未執行

11)assign Rom[5’h0A]=32’h0800000D;
指令含義:J 0D
結果:未執行

12)assign Rom[5’h0B]=32’hXXXXXXXX;
13)assign Rom[5’h0C]=32’hXXXXXXXX;
14)assign Rom[5’h0D]=32’hAD02000A;
二進制源碼:001000 00000 00001 00000 00000 001000
指令含義:sw $2 10($8)
結果:memory[$8+10]=12

15)assign Rom[5’h0E]=32’h8D04000A;//
二進制源碼:001000 00000 00001 00000 00000 001000
指令含義:lw $4 10($8)
結果:$4=12

16)assign Rom[5’h0F]=32’h10440002;
二進制源碼:001000 00000 00001 00000 00000 001000
指令含義:beq $2,$4,2
結果:寄存器值相等,發生跳轉,跳轉到地址加2的地方。說明lw數據冒險得到解決。

17)assign Rom[5’h10]=32’h20210004;
二進制源碼:00100000001000010000000000000100
指令含義:addi $1,$1,4
結果:未執行

18)assign Rom[5’h11]=32’h00222824;
指令含義:and $5,$1,$2
結果:未執行

19)assign Rom[5’h12]=32’h14220006;//
二進制源碼:001000 00000 00001 00000 00000 001000
指令含義:bne $1,$2,6
結果:發生了跳轉,說明18這條指令未執行,控制冒險得到解決。

20)assign Rom[5’h13]=32’h30470009;
指令含義:andi $2,9,$7
結果:未執行

21)assign Rom[5’h14]=32’hXXXXXXXX;
22)assign Rom[5’h15]=32’hXXXXXXXX;
23)assign Rom[5’h16]=32’hXXXXXXXX;
24)assign Rom[5’h17]=32’hXXXXXXXX;
25)assign Rom[5’h18]=32’hXXXXXXXX;
26)assign Rom[5’h19]=32’hXXXXXXXX;
27)assign Rom[5’h1A]=32’hXXXXXXXX;
28)assign Rom[5’h1B]=32’hXXXXXXXX;
29)assign Rom[5’h1C]=32’hXXXXXXXX;
30)assign Rom[5’h1D]=32’hXXXXXXXX;
31)assign Rom[5’h1E]=32’hXXXXXXXX;
32)assign Rom[5’h1F]=32’hXXXXXXXX;

6.3阻塞分析

在這裏插入圖片描述
第一個紅框是流水線CPU因爲bne結構冒險而暫停的兩個時鐘週期,
第二個紅框是流水線CPU因爲lw數據冒險而暫停的一個時鐘週期,
第三個紅框是流水線PCU因爲beq數據冒險而暫停的兩個時鐘週期
第四個紅框是流水線PCU因爲bne數據冒險而暫停的時鐘週期。

七、 結論和體會

7.1體會感悟

通過此次的CPU設計實驗,讓我們對CPU內部組成以及指令在CPU部件上如何運作有了一個更深的理解。在實驗過程中,我們遇到了各種問題,一開始老師佈置下來的CPU任務的時候,完全是懵的,因爲CPU器件和指令運算只在課本上學習,從來沒有真正實踐過,現在需要自己設計CPU的各個部件,而且要將指令在器件上運行,感覺很複雜。但在接下來的日子,我們沒有因爲不會而放棄,而是努力專心去設計好每個部件,對每個部件的功能進行模擬仿真,確保這一部件模塊不出錯,在設計過程中,感覺慢慢可以理清思路,也明白了下一步需要設計的東西通過此次實驗,讓我們對CPU有了更深的理解,而不只是紙上談兵。其中控制冒險和Lw的數據冒險讓我們頭疼了很久,我們始終不能準確判斷conque和stall信號的輸出位置。經過整整兩週的摸索,終於找到了正確的解決方案,並且順利的設計出合理的指令,達到了預期的目的。

7.2對本實驗過程及方法、手段的改進建議

1.過程中,應該每個部件都分別進行調試之後再組裝在一起。
2.各部件儘量再拆分爲更小的部件組合而成。
3.對控制信號的檢測應該更加多元化,這樣更有利於分析CPU流水線運作過程。

PS

PS: 好啦,至此位置,單週期和流水線CPU的簡單實現的流程就全部交代清楚來,接下來我會將重心轉向人工智能領域,短期規劃是現實常見算法的pygame可視化(比如DFS,BFS,markov,Astar ,SIR,etc)一起加油吧!😃

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章