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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

Verilog实现MIPS的5级流水线cpu设计(Modelsim仿真)[通俗易懂]

發布時間:2023/12/15 综合教程 31 生活家
生活随笔 收集整理的這篇文章主要介紹了 Verilog实现MIPS的5级流水线cpu设计(Modelsim仿真)[通俗易懂] 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

大家好,又見面了,我是你們的朋友風君子。如果您正在找激活碼,請點擊查看最新教程,關注關注公眾號 “全棧程序員社區” 獲取激活教程,可能之前舊版本教程已經失效.最新Idea2022.1教程親測有效,一鍵激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟無欺

Verilog實現IMPS的5級流水線cpu設計

本篇文章是在功能上實現cpu設計,而非結構上實現。結構上實現可以跳轉到(此為個人推薦):
Verilog流水線CPU設計(超詳細)
此外有與本文章配套的資源,文章中不懂的地方可以跳轉到(有工程源碼):
MIPS五級流水線cpu的制作

一、實驗內容

1.1:實驗目的
(1)CPU各主要功能部件的實現
(2)CPU的封裝
(3)了解提高CPU性能的方法
(4)掌握流水線MIPS微處理器的工作原理
(5)理解并掌握數據冒險、控制冒險的概念以及流水線沖突的解決方法
(6)掌握流水線MIPS微處理器的測試仿真方法
1.2:實驗要求
(1)至少實現MIPS中的三類指令,即R類,I內,J類指令
(2)采用5級流水線技術
(3)完成Lw指令的數據冒險的解決
(4)在ID段完成控制冒險的解決

二、實驗環境

2.1:硬件平臺
無,只進行仿真,未下載到FPGA

2.2:軟件平臺
(1)操作系統:WIN 10
(2)開發平臺:Modelsim SE-64 10.4
(3)編程語言:VerilogHDL硬件描述語言

三、實驗原理

3.1:IMPS流水線CPU原理
根據MIPS微處理器的特點,將整體的處理過程分為取指令(IF)、指令譯碼(ID)、指令執行(EX)、存儲器訪問(MEM)和寄存器寫回(WB)五個階段,對應著流水線的五級。這樣,我們就可以定義沒執行一條指令需要5個時鐘周期,每個時鐘周期的上升沿到來時,此指令的一系列數據和控制信息將轉移到下一級處理。

3.2:數據流動

  • IF級:取指令部分
    (1)根據PC值在指令存儲器中取指令,將取得指令放在流水寄存器中。
    (2)對PC寄存器的更新,更新有兩類,一是直接PC值 +4 ,取下一相鄰地址的指令;二是更新為跳轉地址,該地址來自于ID段算出的分支地址或者跳轉地址等。更新的選擇取決于來自ID段的一個判斷信號。將更新后的PC值放在流水線寄存器中。
  • ID級:指令譯碼部分
    (1)進行指令譯碼,按照對應寄存器號讀寄存器文件,并將讀出結果放入臨時寄存器A和B中。
    (2)數據冒險和控制冒險的檢測和處理都在此階段解決。
    (3)同時對位立即值保存在臨時寄存器中。指令的低16位進行符號位的擴展,并存放在流水寄存器中。
  • EX級:執行部分
    根據指令的編碼進行算數或者邏輯運算或者計算條件分支指令的跳轉目標地址。此外LW、SW指令所用的RAM訪問地址也是在本級上實現。
  • MEM級:訪存部分
    只有在執行LW、SW指令時才對存儲器進行讀寫,對其他指令只起到一個周期的作用。
  • WB級:寫回部分
    該級把指令執行的結果回寫到寄存器文件中。

3.3:冒險策略

  • 數據冒險
    (1)使用定向(旁路)解決數據冒險
    在ID段對寄存器進行讀數據時,要讀取的數據可能是上一個指令要寫入的結果,也就是當前結果在流水線中還沒有寫入寄存器,此時讀取寄存器的數據是未更新的,也就是錯誤的。這種情況一般發生在MEM級與WB級的寫數據與ID級的讀數據發生沖突(未考慮LW指令),這樣可以通過定向技術來解決數據冒險,因為在某條指令產生計算結果之前,其他指令并不是真正立即需要該計算結果,如果能夠將該計算結果從其產生的地方直接送到其他指令需要的地方,那么就可以避免沖突。
    (2)使用暫停機制解決Lw數據冒險
    定向技術有顯而易見的局限性,因為定向技術必須要求前一條指令在EX結束時更新,但是LW指令最早只能在WB級讀出寄存器的值。因此無法及時提供給下一條指令的EX級使用。分析流水線時序圖,可以發現lw指令的下一條指令,需要阻塞一個時鐘周期,才能確保該指令能獲得正確的操作數值,下面給出具體解決方法。在ID級需要進行數據冒險,ID級是進行譯碼的段,對操作碼進行比較,當發現當前的指令是一條LW指令時,ID級會發出一條請求流水線暫停的信號,部件ctrl會根據信號產生一個6位的stall信號,6位的信號分別控制pc部件,IF級,ID級,EX級,MEM級,WEB級的暫停,當然在這里當出現LW冒險時只需要關閉PC部件、IF級、ID級即可避免沖突。暫停就相當于在流水圖中添加一數列的氣泡,將后面的指令都往后推一個時鐘周期。
  • 控制冒險
    控制冒險是因為由控制相關引起的,也就是當出現分支指令等能使pc值發生變化的時候就會出現控制冒險。下面從兩個方面來說下如何解決控制冒險和降低分支延遲。
    (1)在正常的數據流水線中分支指令時候成功以及分支地址的傳送都是在MEM級完成的,這樣就會造成3個時鐘周期的延遲。現在我將這兩個操作都放在ID級完成,這樣分支延遲就會降低到一個時鐘周期。
    (2)再說下如何解決控制沖突。控制沖突都是分支指令,跳轉指令等引起的,但我們提前知道這條指令是分支(跳轉)指令時,我們就可以提前最好準備來解決沖突。這些也是在ID級來完成。ID是譯碼段,通過比較沒一條指令來發覺哪些是分支跳轉指令,但發現分子跳轉指令時,ID段就會計算出跳轉地址,并產生一個跳轉信號,在下個時鐘上沿到來后會將這兩個信號傳送到pc部件,同時阻止前面部件的輸出。在這里是采用了延遲槽的技術,但出現分支指令是就會認為延遲槽中的指令作廢,相當于一條空指令了。有點使用預測失敗的思想。

3.4:指令格式
(1)MIPS有三類指令,分別為R型指令,I型指令,J型指令

(2)本實驗用到的MIPS指令格式

四:模塊設計

  • 展示下宏文件defines.v :
`define RstEnable 1'b1 `define RstDisable 1'b0 `define ENABLE 1'b1 `define DISABLE 1'b0
`define WriteEnable 1'b1 `define WriteDisable 1'b0 `define ReadEnable 1'b1 `define ReadDisable 1'b0
`define InsEnable 1'b1 `define InsDisable 1'b0 // common constant define `define ZeroWord 32'h00000000 // bus width define `define InsAddrWidth 31:0 `define InsWidth 31:0 `define InsAddrBus 31:0 `define InsDataBus 31:0 `define RegAddrBus 4:0 `define RegDataBus 31:0 `define DRegDataBus 63:0 // number of objects `define InsMemUnitNum 262143 `define InsMemUnitNumLog2 18 `define RegFilesNum 32 `define RegFilesNumLog2 5 // instructions encoding `define SPECIAL 6'b000000 `define SPECIAL2 6'b011100
`define REGIMM 6'b000001 // 0. NOP `define NOP 6'b000000 // SPECIAL `define PREF 6'b110011 //SPECIAL `define SYNC 6'b001111
// 1. LOGIC Insts
`define AND 6'b100100 // SPECIAL `define OR 6'b100101 // SPECIAL `define XOR 6'b100110 // SPECIAL `define NOR 6'b100111 // SPECIAL
`define ANDI 6'b001100 `define ORI 6'b001101 `define XORI 6'b01110 `define LUI 6'b001111
// 2. SHIFT Insts
`define SLL 6'b000000 // SPECAIL `define SRL 6'b000010 // SPECIAL `define SRA 6'b000011 // SPECIAL `define SLLV 6'b000100 //SPECIAL
`define SRLV 6'b000110 //SPECIAL `define SRAV 6'b000111 //SPECIAL // 3. MOVE Insts `define MOVN 6'b001011 // SPECIAL `define MOVZ 6'b001010 // SPECIAL
`define MFHI 6'b010000 // SPECIAL `define MFLO 6'b010010 // SPECIAL `define MTHI 6'b010010 // SPECIAL `define MTLO 6'b010011 // SPECIAL
// 4. Arith Insts
`define ADD 6'b100000 // SPECIAL `define ADDU 6'b100001 // SPECIAL `define SUB 6'b100010 // SPECIAL `define SUBU 6'b100011 // SPECIAL
`define SLT 6'b101010 // SPECIAL `define SLTU 6'b101011 // SPECIAL `define ADDI 6'b001000 `define ADDIU 6'b001001
`define SLTI 6'b001010 `define SLTIU 6'b001011 `define CLZ 6'b100000 // SPECIAL2 `define CLO 6'b100001 // SPECIAL2
`define MUL 6'b000010 // SPECIAL2 `define MULT 6'b011000 // SPECIAL `define MULTU 6'b011001 //SPECIAL // 5. branch insts `define JR 6'b001000 //SPECIAL
//`define JALR 6'b001001 // SPECIAL `define J 6'b000010 //`define JAL 6'b000011 `define BEQ 6'b000100
`define BGTZ 6'b000111 `define BLEZ 6'b000110 `define BNE 6'b000101 `define BLTZ 5'b00000   // REGIMM
//`define BLTZAL 5'b10000 // REGIMM `define BGEZ 5'b00001 // REGIMM //`define BGEZAL 5'b10001 // REGIMM // LOAD and STORE //`define LB 6'b100000
//`define LBU 6'b100100 //`define LH   6'b100001 //`define LHU 6'b100101 `define LW 6'b100011
//`define LWL 6'b100010 //`define LWR  6'b100110 //`define SB 6'b101000 //`define SH 6'b101001
`define SW 6'b101011 //`define SWL  6'b101010 //`define SWR 6'b101110 // ALU OP `define alu_op_nop 8'b00000000
`define alu_op_or 8'b00100101 `define ALU_OP_NOP 8'b00000000 `define ALU_OP_AND 8'b00100100 `define ALU_OP_OR 8'b00100101
`define ALU_OP_XOR 8'b00100110 `define ALU_OP_NOR  8'b00100111 `define ALU_OP_ANDI 8'b01011001 `define ALU_OP_ORI 8'b01011010
`define ALU_OP_XORI 8'b01011011 `define ALU_OP_LUI  8'b01011100 `define ALU_OP_SLL 8'b01111100 `define ALU_OP_SLLV 8'b00000100
`define ALU_OP_SRL 8'b00000010 `define ALU_OP_SRLV  8'b00000110 `define ALU_OP_SRA 8'b00000011 `define ALU_OP_SRAV 8'b00000111
`define ALU_OP_MOVZ 8'b00001010 `define ALU_OP_MOVN 8'b00001011 `define ALU_OP_MFHI 8'b00010000 `define ALU_OP_MTHI 8'b00010001
`define ALU_OP_MFLO 8'b00010010 `define ALU_OP_MTLO 8'b00010011 `define ALU_OP_SLT 8'b00101010 `define ALU_OP_SLTU 8'b00101011
`define ALU_OP_SLTI 8'b01010111 `define ALU_OP_SLTIU  8'b01011000 `define ALU_OP_ADD 8'b00100000 `define ALU_OP_ADDU 8'b00100001
`define ALU_OP_SUB 8'b00100010 `define ALU_OP_SUBU  8'b00100011 `define ALU_OP_ADDI 8'b01010101 `define ALU_OP_ADDIU 8'b01010110
`define ALU_OP_CLZ 8'b10110000 `define ALU_OP_CLO  8'b10110001 `define ALU_OP_MULT 8'b00011000 `define ALU_OP_MULTU 8'b00011001
`define ALU_OP_MUL 8'b10101001 `define ALU_OP_J  8'b01001111 //`define ALU_OP_JAL 8'b01010000 //`define ALU_OP_JALR 8'b00001001
`define ALU_OP_JR 8'b00001000 `define ALU_OP_BEQ  8'b01010001 `define ALU_OP_BGEZ 8'b01000001 //`define ALU_OP_BGEZAL 8'b01001011
`define ALU_OP_BGTZ 8'b01010100 `define ALU_OP_BLEZ  8'b01010011 `define ALU_OP_BLTZ 8'b01000000 //`define ALU_OP_BLTZAL 8'b01001010
`define ALU_OP_BNE 8'b01010010 `define ALU_OP_LW  8'b11100011 `define ALU_OP_SW 8'b11101011 // ALU SEL `define ALU_NOP 3'b000
`define ALU_LOGIC 3'b001 `define ALU_SEL_LOGIC 3'b001 `define ALU_SEL_SHIFT 3'b010 `define ALU_SEL_NOP 3'b000
`define ALU_SEL_MOVE 3'b011 `define ALU_SEL_ARITH 3'b100 `define ALU_SEL_MUL 3'b101 `define ALU_SEL_BRANCH 3'b110
`define ALU_SEL_LOADSTORE 3'b111 //ALU `define ALUOpBus 7:0
`define ALUSelBus 2:0 // DataMem `define DMDataBus 31:0
`define DMAddrBus 31:0 `define DMUnitNum 131071
`define DMunitNumLog2 17 `define ByteWidth 7:0
  • pc部件

(1)模塊結構:

(2)模塊功能:
作為pc寄存器的更新信號,即對pc值進行更新

(3)實現思路:
pc值的更新有兩類,一是沒有遇到分支指令或者能改變pc值的指令時,程序順序執行,pc值加4即可;二是當出現分支指令或者跳轉指令時,程序不按順序執行,此時pc值不在加4,而是等于ID段傳來的跳轉(分支)地址。而pc值的選擇根據ID段傳來的是否跳轉的信號決定。出現冒險時Lw指令冒險的時候會被暫停信號的輸出,即保持pc值不變。

(4)引腳及控制信號
rst:對部件進行復位操作,一位的輸入端口
clk:時鐘信號,一位的輸入端口
branchEN:是否跳轉信號,一位的輸入端口
branchAddr:跳轉地址,32位的輸入端口
stall:暫停流水線控制信號,6位的輸入端口
pc:pc值,32位的輸出端口
insEn:使能信號,判斷是否關閉指令存儲器,一位的輸出端口
(5)主要代碼

`include "defines.v" module pc( // ---- input ----- input wire rst, input wire clk, input wire branchEN, input wire[`RegDataBus] branchAddr,	
input wire[5:0] stall,
// ---- output ----
output reg[`InsAddrWidth] pc, output reg insEn ); always@(posedge clk) begin if(rst == `RstEnable) 
begin
insEn <= `InsDisable; pc <= `ZeroWord;
end
else
begin
insEn <= `InsEnable; end end always@(posedge clk) begin if(insEn == `DISABLE)
pc <= `ZeroWord; else begin if(stall[0] == `DISABLE)
begin
if(branchEN == `ENABLE)
pc <= branchAddr;
else
pc <= pc + 4;
end
end
end
endmodule
  • insMem部件

(1)模塊結構:

(2)模塊功能:
從pc部件獲取pc值,然后在insMe中取得指令。

(3)實現思路:
為了避免不必要的訪存沖突(結構沖突),將指令存儲器、數據存儲器、存儲器設為三個獨立的部件。在發生冒險時會被pc部件關閉。

(4)引腳及控制信號:
insEN:控制insMem的開啟與關閉,一位的輸入端口
insAddr:輸入pc值,32位的輸入端口
inst:取出的指令,32位的輸出端口

(5)主要代碼

`include "defines.v" module insMem(insEn, insAddr, inst); // ----- input ------ input wire insEn; input wire[`InsAddrWidth] insAddr;	
// ----- output -----
output reg[`InsWidth] inst; reg[`InsWidth] instM[0:`InsMemUnitNum]; initial Begin+ $readmemh("instructions.data",instM); end always@(*) begin if(insEn == `InsDisable) begin
inst <= `ZeroWord; end else begin inst <= instM[insAddr[`InsMemUnitNumLog2+1:2]];
end
end
endmodule
  • IF_ID部件
    (1)模塊結構:

    (2)模塊功能:
    IF_ID模塊是IF級與ID級之間的流水寄存器,用來隔離相鄰兩階段的處理工作。

(3)實現思路:
儲存IF級處理完工作后的數據并在時鐘上升沿到來的時候將存儲的數據傳給ID級。在發生Lw互鎖的時候會暫停數據的流出。

(4)引腳及控制信號:
rst:對部件進行復位操作,一位的輸入端口
clk:時鐘信號,一位的輸入端口
if_pc:來自insMem部件的pc信號,32位的輸入端口
if_ins:來自insMen部件的指令代碼信號,32位的輸入端口
stall:暫停流水線控制信號,6位的輸入端口
id_pc:傳給ID段的pc信號,32位的輸出端口
id_ins:傳給ID段的指令代碼,32位的輸出端口

(5)主要代碼

`include "defines.v" module IF_ID( // INPUT input wire rst, input wire clk, input wire[`InsAddrWidth] if_pc,
input wire[`InsWidth] if_ins, input wire[5:0] stall, //OUTPUT output reg[`InsAddrWidth] id_pc,
output reg[`InsWidth] id_ins ); always@(posedge clk) begin if(rst == `RstEnable)
begin
id_pc <= `ZeroWord; id_ins <= `ZeroWord;
end
else
begin
if(stall[1] == `ENABLE && stall[2] == `DISABLE)
begin
id_pc <= `ZeroWord; id_ins <= `ZeroWord;
end
else if(stall[1] == `DISABLE)
begin
id_pc <= if_pc;
id_ins <= if_ins;
end
end
end
endmodule
  • RegFiles部件

(1)模塊結構:

(2)模塊功能:
RegFiles模塊是通用寄存器組,用來存儲數據用。在譯碼階段,需要對寄存器進行數據讀取,在這個模塊中只實現數據的讀取。

(3)實現思路:
通用寄存器組用在ID段的數據讀取和WB段的數據寫回。根據指令的不同rs與rt的讀操作是不同的,有時候rt不做讀操作,這個時候就需要兩個使能信號來控制rs與rt的讀操作當然在讀數據時是需要提供數據地址的,也就是被讀寄存器的地址。在寫回操作中,需要寫回地址以及寫回數據,因為不同的指令是有可能沒有寫回操作的,所以寫回動作也需要一個使能信號。輸出就是讀取的數據進行輸出,有的指令在譯碼階段只有一個數據輸出。

(4)引腳及控制信號:
rst:對部件進行復位操作,一位的輸入端口
clk:時鐘信號,一位的輸入端口
wrDataAddr:WB段要寫回的數據,32位輸入端口
wrData:WB段需要寫回的數據,32位的輸入端口
reData1Addr:譯碼時需要讀取的rs寄存器地址,5位的輸入端口
reData2Addr:譯碼時需要讀取的rt寄存器地址,5位的輸入端口
ren1:控制讀rs寄存器的使能信號,一位的輸入端口
ren2:控制讀rt寄存器的使能信號,一位的輸入端口
wrn:控制寫回的使能信號,一位的輸入端口
reData1:讀取的rs寄存器的數據,32位的輸入端口
reData2:讀取的rt寄存器的數據,32位的輸入端口

(5)主要代碼


`include "defines.v" module RegFiles( // INPUT input wire rst, input wire clk, input wire wrn, input wire[`RegAddrBus] wrDataAddr,
input wire[`RegDataBus] wrData, input wire ren1, input wire[`RegAddrBus] reData1Addr,
input wire ren2,
input wire[`RegAddrBus] reData2Addr, // OUTPUT output reg[`RegDataBus] reData1,
output reg[`RegDataBus] reData2 ); // define all registers reg[`RegDataBus] regFiles[0:`RegFilesNum-1]; integer i; // init all registers to be zero initial begin for(i = 0; i < `RegFilesNum; i = i + 1)
regFiles[i] = `ZeroWord; end // write operation always@(posedge clk) begin if(rst == `RstDisable && wrn == `WriteEnable && wrDataAddr != `RegFilesNumLog2'b0)
begin
regFiles[wrDataAddr] <= wrData;
end	
end
// read operation1
always@(*)
begin
if(rst == `ENABLE) reData1 <= `ZeroWord;
else if(wrn == `ENABLE && wrDataAddr == reData1Addr && ren1 == `ENABLE)
reData1 <= wrData;
else if(ren1 == `ENABLE) reData1 <= regFiles[reData1Addr]; else reData1 <= `ZeroWord;
end
// read operation2
always@(*)
begin
if(rst == `ENABLE) reData2 <= `ZeroWord;
else if(wrn == `ENABLE && wrDataAddr == reData2Addr && ren2 == `ENABLE)
reData2 <= wrData;
else if(ren2 == `ENABLE) reData2 <= regFiles[reData2Addr]; else reData2 <= `ZeroWord;
end
endmodule
  • ID部件

(1)模塊結構:

(2)模塊功能:
對指令進行譯碼,相當于ID段的全部工作。

(3)實現思路:
ID段除去流水寄存器,一共就兩個部件,一個是通用寄存器組RegFiles一個就是這個ID部件。簡單來說這兩個部件仿佛從屬關系,ID部件給RegFiles提供工作的信號,RegFiles輸出的結果返還給ID。在ID部件中,他需要解決數據冒險和控制冒險,關于這兩個冒險的解決方式在前面已經講述,在這不在講述。除去冒險的解決外,ID部件還需要對輸入的指令進行譯碼,然后進行6位操作碼的比較,也就是對32位指令進行分割等。其實在這個段中最主要的就是冒險的解決,其他的和正常的流水線沒時候區別。
(4)引腳及控制信號:
rst:復位信號,一位的輸入端口
Inst:上一級傳來的指令,32位的輸入端口
Pc:上一級傳來的pc值,32位的輸入端口
reData1_in:RegFiles部件傳來讀取的寄存器數據,32位的輸入端口
reData2_in:RegFiles部件傳來讀取的寄存器數據,32位的輸入端口
ex_wrAddr:EX段數據要寫回的地址,5位的輸入端口
ex_wrData:EX段要寫回的數據,32位的輸入端口
mem_wrAddr:MEM段數據要寫回的地址,5位的輸入端口
mem_wrData:MEM段要寫回的數據,32位的輸入端口
Last_alu_op:指令的子類型,8位的輸入端口
ex_wrn:EX段數據的寫信號,一位的輸入端口
mem_wrn:MEM段數據的寫信號,一位的輸入端口
delaylotEn:指出當前的指令是否位于延遲槽,一位的輸入端口
ren1:送給RegFiles的rs讀信號,一位的輸出端口
Ren2:送給RegFiles的rt讀信號,一位的輸出端口
ReData1Addr:送給RegFiles的rs寄存器地址,5位的輸出端口
reData2Addr:送給RegFiles的rt寄存器地址,5位的輸出端口
wrDataAddr:往下一級傳的寫回地址,有ID段產生,5位的輸出端口
regData1_out:rs寄存器數據的輸出胡,32位的輸出端口
regData2_out:rt寄存器數據的輸出胡,32位的輸出端口
Alu_op:譯碼階段的指令要進行運算的子類型,8位的輸出端口
Alu_sel:譯碼階段的指令要進行運算的類型,3位的輸出端口
branchAddr:分支轉移地址,32位的輸出端口
Inst_o:往下一級傳的指令,32位的輸出端口
branchEN:分支跳轉信號,一位的輸出端口
next_delayslotEn:標示下一條進入譯碼階段的指令是否處于延遲槽,一位的輸出端口
stall_rep:請求流水線暫停信號,一位的輸出端口
wrn:寫回地址的寫信號,一位的輸出端口

  • ID_EX部件

(1)模塊結構:

(2)模塊功能:
ID段與EX段之間的流水線寄存器。

(3)實現思路:
保存上一級ID段產生的數據,包括讀取寄存器的數據,分割指令得到的操作碼數據,指令數據,寫回地址和寫回信號等數據。在這個模塊中有兩點值得一提。一是輸入信號stall,這個是當出現流水線暫停時(LW指令數據沖突)關閉該寄存器,延遲一個時鐘周期在視情況繼續執行;二是id_next_delayloyEn信號,這個是用來實現控制冒險檢測的,他在輸出端有個ew_next_delaylotEn信號,這個實在下一時鐘上升沿到來時將信號返回給ID段以達到解決控制沖突。

(4)引腳及控制信號:
rst:復位信號,一位的輸入端口
Inst:上一級傳來的指令,32位的輸入端口
Clk:時鐘信號,一位的輸入端口
id_reg1Data:讀取的rs寄存器數據,32位的輸入端口
id_reg2Data:讀取的rt寄存器數據,32位的輸入端口
id_alu_op:操作碼,8位的輸入端口
id_alu_sel:進行運算的類型,3位的輸入端口
id_wrAddr:寫回地址,5位的輸入端口
Stall:暫停流水線控制信號,6位的輸入端口
id_wrn:寫回信號,一位的輸入端口
id_next_delaylotEn:當前的指令是否處在延遲槽,一位的輸入端口
ex_reg1Data:讀取的rs寄存器數據,32位的輸出端口
ex_reg2Data:讀取的rs寄存器數據,32位的輸出端口
ex_alu_op:操作碼,8位的輸出端口
ex_alu_sel:進行運算的類型,3位的輸出端口
ex_wrAddr:回地址,5位的輸出端口
ex_inst:上一級傳來的指令,32位的輸出端口
ew_wrn:寫回信號,一位的輸出端口
ex_next_delaylotEn:當前的指令是否處在延遲槽,一位的輸出端口

(5)主要代碼

`include "defines.v" module ID_EX( //input input wire clk, input wire rst, input wire[`InsDataBus] id_inst,
input wire[`RegDataBus] id_reg1Data, input wire[`RegDataBus] id_reg2Data,
input wire[`ALUOpBus] id_alu_op, input wire[`ALUSelBus] id_alu_sel,
input wire[`RegAddrBus] id_wrAddr, input wire id_wrn, input wire id_next_delayslotEn, input wire[5:0] stall, //output output reg[`RegDataBus] ex_reg1Data,
output reg[`RegDataBus] ex_reg2Data, output reg[`ALUOpBus] ex_alu_op,
output reg[`ALUSelBus] ex_alu_sel, output reg[`RegAddrBus] ex_wrAddr,
output reg ex_wrn,
output reg ex_next_delayslotEn,
output reg[`InsDataBus] ex_inst ); always@(posedge clk) begin if(rst == `ENABLE)
begin
ex_reg1Data <= `ZeroWord; ex_reg2Data <= `ZeroWord;
ex_alu_sel <= `ALU_NOP; ex_alu_op <= `alu_op_nop;
ex_wrn <= `DISABLE; ex_wrAddr <= 5'b00000; ex_next_delayslotEn <= `DISABLE;
ex_inst <= `ZeroWord; end else begin if(stall[2] == `ENABLE && stall[3] == `DISABLE) begin ex_reg1Data <= `ZeroWord;
ex_reg2Data <= `ZeroWord; ex_alu_sel <= `ALU_NOP;
ex_alu_op <= `alu_op_nop; ex_wrn <= `DISABLE;
ex_wrAddr <= 5'b00000;
ex_next_delayslotEn <= `DISABLE; ex_inst <= `ZeroWord;
end
else if(stall[2] == `DISABLE)
begin
ex_reg1Data <= id_reg1Data;
ex_reg2Data <= id_reg2Data;
ex_alu_op <= id_alu_op;
ex_alu_sel <= id_alu_sel;
ex_wrn <= id_wrn;
ex_wrAddr <= id_wrAddr;
ex_next_delayslotEn <= id_next_delayslotEn;
ex_inst <= id_inst;
end
end
end
endmodule
  • EX部件

(1)模塊結構

(2)模塊功能
實現不同指令的執行。

(3)實現思路
接收來自上一級的寄存器數據和操作碼類型,然后根據操作碼類型判斷做何種運算,將運算結果傳給下一級。

(4)引腳及控制信號
rst:復位信號,一位的輸入端口
Inst:上一級傳來的指令,32位的輸入端口
wrn_i:寫回使能信號,一位的輸入端口
reg1Data:來自上一級的第一個操作數,32位的輸入端口
Reg2Data:來自上一級的第一個操作數,32位的輸入端口
wrAddr_i:寫回地址,5位的輸入端口
alu_op:運算的子類型,8位的輸入端口
alu_sel:運算類型,3位的輸入端口
wrAddr_o:寫回地址,5位的輸出端口
wrn_o:寫回使能信號,一位的輸出端口
result:運算結果,32位的輸出端口
alu_op_o:運算的子類型,8位的輸出端口
mem_addr_o:運算結果要寫入MEM的地址,32位的輸出端口
mem_data_o:運算結果要寫入MEM的數據,32位的輸出端口

  • EX_MEM部件

(1)模塊結構:

(2)模塊功能:
該模塊是EX段與MEM段之間的流水寄存器,用來隔開兩段之間的處理工作。

(3)實現思路:
接收上一級EX段產生的數據結果。接收的數據有兩類,一類是ALU行的計算結果,一類是訪存數據。訪存數據要包括要訪問的地址以及要存儲的數據,當然這個需要操作碼來判斷做何種操作。

(4)引腳及控制信號:
rst:復位信號,一位的輸入端口
clk:時鐘信號,一位的輸入端口
wrn_i:寫回使能信號,一位的輸入端口
wrAddr_i:要寫回的寄存器地址,5位的輸入端口
result_i:EX段計算結果,32位輸入端口
stall:暫停流水線信號,6位的輸入端口
alu_op_i:運算的子類型字段,8位的輸入端口
mem_addr_i:訪存指令要訪問的地址,32位輸入端口
mem_data_i:要存儲在存儲器中的數據,32位的輸入端口
wrn_o:寫回使能信號,一位的輸出端口
wrAddr_o:要寫回的寄存器地址,5位的輸出端口
result_o:EX段計算結果,32位輸出端口
alu_op_o:運算的子類型字段,8位的輸出端口
mem_addr_o:訪存指令要訪問的地址,32位輸出端口
mem_data_o:要存儲在存儲器中的數據,32位的輸出端口

(5)主要代碼


`include "defines.v" module EX_MEM( // input input wire clk, input wire rst, input wire wrn_i, input wire[`RegAddrBus] wrAddr_i,
input wire[`RegDataBus] result_i, input wire wrn_HILO_i, input wire[`RegDataBus] wrData_HI_i,
input wire[`RegDataBus] wrData_LO_i, input wire[5:0] stall, // mem input wire[`ALUOpBus] alu_op_i,
input wire[`RegDataBus] mem_addr_i, input wire[`RegDataBus] mem_data_i,
//output
output reg wrn_o,
output reg[`RegAddrBus] wrAddr_o, output reg[`RegDataBus] result_o,
output reg wrn_HILO_o,
output reg[`RegDataBus] wrData_HI_o, output reg[`RegDataBus] wrData_LO_o,
//mem
output reg[`ALUOpBus] alu_op_o, output reg[`RegDataBus] mem_addr_o,
output reg[`RegDataBus] mem_data_o ); always@(posedge clk) begin if(rst == `ENABLE)
begin
wrn_o <= `DISABLE; wrAddr_o <= 5'b00000; result_o <= `ZeroWord;
wrn_HILO_o <= `DISABLE; wrData_HI_o <= `ZeroWord;
wrData_LO_o <= `ZeroWord; alu_op_o <= `ALU_OP_NOP;
mem_addr_o <= `ZeroWord; mem_data_o <= `ZeroWord;
end
else
begin
if(stall[3] == `ENABLE && stall[4] == `DISABLE)
begin
wrn_o <= `DISABLE; wrAddr_o <= 5'b00000; result_o <= `ZeroWord;
wrn_HILO_o <= `DISABLE; wrData_HI_o <= `ZeroWord;
wrData_LO_o <= `ZeroWord; alu_op_o <= `ALU_OP_NOP;
mem_addr_o <= `ZeroWord; mem_data_o <= `ZeroWord;
end
else if(stall[3] == `DISABLE)
begin
wrn_o <= wrn_i;
wrAddr_o <= wrAddr_i;
result_o <= result_i;
wrn_HILO_o <= wrn_HILO_i;
wrData_HI_o <= wrData_HI_i;
wrData_LO_o <= wrData_LO_i;
alu_op_o <= alu_op_i;
mem_addr_o <= mem_addr_i;
mem_data_o <= mem_data_i;
end
end
end
endmodule
  • DATAMEM部件

(1)模塊結構:

(2)模塊功能:
該模塊是作為數據存儲器來使用的。

(3)實現思路:
MEM段有兩個模塊,一個是dataMem模塊,一個是MEM模塊。dataMem模塊收到來自MEM模塊的訪存地址信號和需要的存儲數據,除此之外還需要一個信號來控制是存儲數據還是讀取數據。

(4)引腳及控制信號:
clk:時鐘信號,一位的輸入端口
ce:使能信號,判斷存儲還是讀取,一位的輸入端口
wrn:使能信號,存儲信號有效為1,一位的輸入端口
mem_addr:要訪存的地址,32位的輸入端口
mem_datai:要存儲的數據,32位的輸入端口
mem_data_o:要讀取的數據,32位的輸出胡端口

(5)主要代碼

`include "defines.v" module dataMem( input wire clk, input wire ce, input wire wrn, // write en input wire[`DMAddrBus] mem_addr,
input wire[`DMDataBus] mem_data_i, // output output reg[`DMDataBus] mem_data_o
);
reg[`ByteWidth] DataMEM[0:`DMUnitNum];
integer i;
initial
begin
for(i = 0; i < `DMUnitNum; i=i+1) begin DataMEM[i] = 8'b0; end end always@(clk) begin if(ce == `ENABLE)
begin
if(wrn == `ENABLE) begin { 
DataMEM[mem_addr],DataMEM[mem_addr+1],DataMEM[mem_addr+2],DataMEM[mem_addr+3]} <= mem_data_i; end end end always@(*) begin if(ce == `DISABLE)
begin
mem_data_o <= `ZeroWord;
end
else
begin
mem_data_o <= { 
DataMEM[mem_addr],DataMEM[mem_addr+1],DataMEM[mem_addr+2],DataMEM[mem_addr+3]};
end
end
endmodule
  • MEM部件

(1)模塊結構:

(2)模塊功能:
對數據存儲器進行訪問,包括控制存儲數據和讀取數據。

(3)實現思路:
收到上一級傳來的數據信號,包括ALU計算的結果,要訪問存儲器的地址,以及要存儲在數據存儲器中的數據。除此之外還需要接收操作碼信號,因為要用來判斷是lw指令還是sw指令。在輸入信號端有一個存儲器讀取的數據,是DATAMEM模塊的返回數據。該模塊的輸出端則提供DATAMEM模塊的輸入信號。

(4)引腳及控制信號:
rst:復位信號,一位的輸入端口
wrn_i:寫回的使能信號,一位的輸入端口
result_i:上一級的ALU計算結果,32位的輸入端口
wrAddr_i:寫回的寄存器地址,5位的輸入端口
alu_op_i:操作碼字段,8位的輸入端口
mem_wrdata_i:存儲器寫數據,32位的輸入端口
mem_addr_i:要訪問的存儲器地址,32位的輸入端口
mem_redata_i:讀取的存儲器數據,32位的輸入端口
wrn_o:寫回使能信號,一位的輸出端口
result_o:上一級的ALU計算結果,32位的輸出端口
wrAddr_o:寫回的寄存器地址,5位的輸出端口
mem_wrn:使能信號,存儲信號有效為1,一位的輸出端口
mem_ce:使能信號,判斷存儲還是讀取,一位的輸出端口
mem_wraddr:要訪存的地址,32位的輸出端口
mem_wrdata:要存儲的數據,32位的輸出端口

(5)主要代碼


`include "defines.v" module MEM( //input input wire rst, input wire[`RegDataBus] result_i,
input wire wrn_i,
input wire[`RegAddrBus] wrAddr_i, input wire wrn_HILO_i, input wire[`RegDataBus] wrData_HI_i,
input wire[`RegDataBus] wrData_LO_i, input wire[`ALUOpBus] alu_op_i,
input wire[`RegDataBus] mem_wrdata_i, input wire[`RegDataBus] mem_addr_i,
// input from dataMem
input wire[`RegDataBus] mem_redata_i, //output output reg wrn_o, output reg[`RegDataBus] result_o,
output reg[`RegAddrBus] wrAddr_o, output reg wrn_HILO_o, output reg[`RegDataBus] wrData_HI_o,
output reg[`RegDataBus] wrData_LO_o, //output to dataMem output reg mem_wrn, output reg mem_ce, output reg[`RegDataBus] mem_wraddr,
output reg[`RegDataBus] mem_wrdata ); always@(*) begin if(rst == `ENABLE)
begin
wrn_o <= `DISABLE; wrAddr_o <= 5'b00000; result_o <= `ZeroWord;
wrn_HILO_o <= `DISABLE; wrData_HI_o <= `ZeroWord;
wrData_LO_o <= `ZeroWord; // about mem mem_wrn <= `DISABLE;
mem_ce <= `DISABLE; mem_wraddr <= `ZeroWord;
mem_wrdata <= `ZeroWord; end else begin wrn_o <= wrn_i; wrAddr_o <= wrAddr_i; result_o <= result_i; wrn_HILO_o <= wrn_HILO_i; wrData_HI_o <= wrData_HI_i; wrData_LO_o <= wrData_LO_i; // about mem mem_wrn <= `DISABLE;
mem_ce <= `DISABLE; mem_wraddr <= `ZeroWord;
mem_wrdata <= `ZeroWord; case(alu_op_i) `ALU_OP_SW:
begin
mem_wrn <= `ENABLE; mem_ce <= `ENABLE;
mem_wraddr <= mem_addr_i;
mem_wrdata <= mem_wrdata_i;
end
`ALU_OP_LW: begin mem_wrn <= `DISABLE;
mem_ce <= `ENABLE;
mem_wraddr <= mem_addr_i;
result_o <= mem_redata_i;
end
default:
begin
end
endcase
end
end
endmodule
  • MEM_WB部件

(1)模塊結構:

(2)模塊功能:
該模塊是MEM段與WB段之間的流水線寄存器。

(3)實現思路:
為WB段的寫回做儲備,所以接收的是MEM段的寫回數據,寫回地址以及寫回使能信號。

(4)引腳及控制信號:
clk:時鐘信號,一位的輸入端口
rst:復位信號,一位的輸入端口
wrn_i:寫回的使能信號,一位的輸入端口
wrAddr_i:寫回的使能信號,一位的輸入端口
wrData_i:寫回的數據,32位的輸入端口
stall:暫停流水線的信號,6位的輸入端口
wrn_o:寫回的使能信號,一位的輸出端口
wrAddr_o:寫回的使能信號,一位的輸出端口
wrData_o:寫回的數據,32位的輸出端口

(5)主要代碼


`include "defines.v" module MEM_WB( //input input wire clk, input wire rst, input wire wrn_i, input wire[`RegAddrBus] wrAddr_i,
input wire[`RegDataBus] wrData_i, input wire wrn_HILO_i, input wire[`RegDataBus] wrData_HI_i,
input wire[`RegDataBus] wrData_LO_i, input wire[5:0] stall, //output output reg wrn_o, output reg[`RegAddrBus] wrAddr_o,
output reg[`RegDataBus] wrData_o, output reg wrn_HILO_o, output reg[`RegDataBus] wrData_HI_o,
output reg[`RegDataBus] wrData_LO_o ); always@(posedge clk) begin if(rst == `ENABLE)
begin
wrn_o <= `DISABLE; wrAddr_o <= 5'b00000; wrData_o <= `ZeroWord;
wrn_HILO_o <= `DISABLE; wrData_HI_o <= `ZeroWord;
wrData_LO_o <= `ZeroWord; end else begin if(stall[4] == `ENABLE && stall[5] == `DISABLE) begin wrn_o <= `DISABLE;
wrAddr_o <= 5'b00000;
wrData_o <= `ZeroWord; wrn_HILO_o <= `DISABLE;
wrData_HI_o <= `ZeroWord; wrData_LO_o <= `ZeroWord;
end
else if(stall[4] == `DISABLE)
begin
wrn_o <= wrn_i;
wrAddr_o <= wrAddr_i;
wrData_o <= wrData_i;
wrn_HILO_o <= wrn_HILO_i;
wrData_HI_o <= wrData_HI_i;
wrData_LO_o <= wrData_LO_i;
end
end
end
endmodule
  • ctrl部件

(1)模塊結構:

(2)模塊功能:
提供6位的暫停信號,作用于pc模塊以及五段流水。

(3)實現思路:
當在ID段出現LW指令的數據冒險時,ID段會發出一個請求暫停信號,ctrl模塊接收這個信號后就會產生一個6位的暫停信號。stall初始值是000000,在接收到請求暫停信號id_stall后就會變為111000,作用于前面各個模塊。

(4)引腳及控制信號:
rst:復位信號,一位的輸出端口
id_stall:請求暫停信號,一位的輸出端口
stall:暫停信號,6位的輸出端口

(5)主要代碼

`include "defines.v" module ctrl( input wire rst, input wire id_stall, //input wire ex_stall, output reg[5:0] stall ); always@(*) begin if(rst == `ENABLE)
begin
stall <= 6'b000000; end else begin if(id_stall == `ENABLE) begin stall <= 6'b000111;
end
else
begin
stall <= 6'b000000;
end
end
end
endmodule
  • MIPS_32CPU部件

(1)模塊結構:

(2)模塊功能:
這個文件是封裝了除指令存儲器INSMEN模塊和數據存儲區DATAMEM模塊之外的所有模塊。

(3)實現思路:
將cpu分成三個部件,一個是MIPS_32CPU,一個是DATAMEM,一個是INSMEM,這三個模塊之間的信號是互連的。

(4)引腳及控制信號:
clk:時鐘信號,一位的輸入端口
rst:復位信號,一位的輸入端口
inst:指令代碼,32位IDE輸入信號
datamem_mem_redata:
insAddr_o:pc值,32位的輸出信號
insEn:控制INSMEM模塊的使能信號,一位的輸出端口
mem_datamem_wrn:數據存儲器寫數據,32位的輸出端口
mem_datamem_ce:使能信號,判斷數據存儲器是存儲還是讀取,一位的輸出端口
mem_datamem_addr:數據存儲器的訪存地址,32位的輸出端口
mem_datamem_wrdata:寫入數據存儲器的數據,32位的輸出端口

  • MIN_SOPC部件

(1)模塊結構:

(2)模塊功能:
對MIPS_32CPU模塊、INSMEM模塊和DATAMEM模塊的封裝。

(3)實現思路:
簡單的封裝操作。

(4)引腳及控制信號:
clk:時鐘信號,一位的輸出端口
rst:復位信號,一位的輸出端口

(5)主要代碼


`include "defines.v" module min_sopc( // input input wire clk, input wire rst ); wire insEn; wire[`InsDataBus] inst;
wire[`InsAddrBus] instAddr; wire[`RegDataBus] datamem_mem_redata;
wire mem_datamem_wrn;
wire mem_datamem_ce;
wire[`RegDataBus] mem_datamem_addr; wire[`RegDataBus] mem_datamem_wrdata;
//OpenMips OpenMips_0
MIPS_32CPU MIPS32
(
// input
.clk(clk),
.rst(rst),
.inst(inst),
// output
.insAddr_o(instAddr),
.insEn(insEn),
.datamem_mem_redata(datamem_mem_redata),
.mem_datamem_wrn(mem_datamem_wrn),
.mem_datamem_ce(mem_datamem_ce),
.mem_datamem_addr(mem_datamem_addr),
.mem_datamem_wrdata(mem_datamem_wrdata)
);
insMem insMem_0
(
//input
.insAddr(instAddr),
.insEn(insEn),
// output
.inst(inst)
);
// dataMemory
dataMem DATAMEM
(
.clk(clk),.ce(mem_datamem_ce), .wrn(mem_datamem_wrn),
.mem_addr(mem_datamem_addr), .mem_data_i(mem_datamem_wrdata),
.mem_data_o(datamem_mem_redata)
);
endmodule
  • 仿真代碼
`include "defines.v" `timescale 1ns/1ns
module testBench();
reg CLOCK;
reg rst;
initial
begin
CLOCK = 1'b0;
forever #12 CLOCK = ~CLOCK;
end
initial
begin
rst = `ENABLE; #20 rst = `DISABLE;
#1000 $stop;
end
min_sopc min_sopc_0
(	
.clk(CLOCK),
.rst(rst)
);
endmodule

六、測試程序

七、結果分析

7.1:數據結果分析
以下跑的是第一個測試程序:
(1)pc值的變化
選擇觀察信號:時鐘信號clk 、pc部件的輸出端口pc信號
結果:


分析:根據實驗數據可以發現,pc值都是隨著clk正常更新的。比如在第二張圖中。第一個紅色箭頭指的地方是測試程序中的第5條指令,該指令是當條件滿足時發生跳轉,從程序中可以看出條件是滿足的要發生跳轉。第五條指令的pc是00000010,當發生跳轉時,下一條指令00000014是不執行的,但是在程序中單判斷出現跳轉時,第六條指令以及取出來了,pc值已經加4,但是他是不執行的狀態。隨意pc值在下個時鐘周期上升沿應該更新加8,也就是跳轉到0000001C處開始執行,從圖中可以看出確實是這樣的。

(2)指令的流動
為了更好的證實上面pc變化的正確性,現在來觀察下指令的流動。
選擇觀察信號:時鐘信號clk 、insMem部件的輸出端inst信號
結果:


分析:可以看出指令是正常流動的,在第15個周期后既沒有指令流出了,因為我沒有設置指令的循環。在看第二張圖紅色箭頭標注的地方,這是對上面pc值變動的驗證。第5條指令的指令碼是10640002,可以看到正好對應第一個箭頭所指的地方。然后發生跳轉,第6條指令雖然被取出但是沒有被執行,第7條指令直接沒有被取出來,調到了第8條指令00223022那執行,這個和圖中的信息都是對應的。

(3)計算的結果
觀察下每條指令的執行結果。
選擇觀察信號:時鐘信號clk 、EX部件的輸出端result信號 、DATAMEM 部件的輸入端mem_data_i信號以及輸出端mem_data_o 信號。
結果:
圖一:

圖二:

圖三:

分析:先看圖一,在圖一中有兩個箭頭,一個指向時鐘信號,一個指向EX段的result端口的輸出值。可以看到,result端口在是在第三個時鐘周期輸出數據的,這正好是第一條指令EX段散發出數據的時間,在測試程序中第一條指令就是給寄存器$1賦值為10,仿真數據是對的,對比后面的數據也都是正確的。在看圖二,在圖二中可以看到EX段的result端口是有一段沒有數據輸出的,這是因為在測試程序中這段是在執行訪存指令,即LW和SW指令,在指令執行完后就出現了最后一條指令的計算結果,因此可以看出EX段的功能是正確的。然后看圖三,圖三中主要是針對訪存指令的分析,看藍色箭頭指向,這是說明在沒有執行訪存指令時,數據存儲器DATAMEM是不在工作的,在測試程序中先出現了SW寫入存儲器指令,寫入值為3,在仿真圖中可以看到DATAMEM的mem_data_i輸入端有信號值為3,接著是LW取值信號,取數據為3,在圖中可以看到mem_data_o輸出端的信號值是3,再過一個周期后,值被傳遞走就回復為0狀態,這些都說明了MEM段功能的正確性。

7.2:冒險處理分析
以下跑的是第一個測試程序:
(1)定向技術結果分析
觀察WB段與ID段的數據冒險。
選擇觀察信號:時鐘信號clk 、ID部件的輸入端inst信號、EX部件的輸出 端result信號
結果:

分析:在測試程序中,第四條指令(指令碼為00222020)與第二條指令(指令碼為20020003)發生數據沖突,也就是在上面的左邊的箭頭所指的位置。第二個沖突就是在第四條指令與第五條指令(指令碼為10640002)發生數據沖突,也就是上面的右邊箭頭所指的位置。當初想數據沖突時,如果沒有旁路的話,他會出現錯誤的結果,但是對比EX部件的輸出端result信號的值會發現值是正確的,也不存在延遲產生的情況,所以說旁路的設計很好的解決了數據沖突問題。
但是旁路解決數據沖突的功能是有限的,當出現LW數據沖突時,旁路是解決不了問題的,因為在ID段就需要數據,但是LW取出的數據在MEM段快結束時才會有,旁路并不能將數據指向ID段,這個時候就需要暫停機制來解決這個問題。

(2)LW數據冒險分析
上面已經說明,當出現LW數據冒險時,會產生一個stall信號來暫停相應的部件。
選擇觀察信號:時鐘信號clk 、IF_ID部件的輸出端id_ins信號、DATAMEM 部件的輸出端mem_data_o信號
結果:

分析:在測試程序中的第11條指令是LW取數據指令,然后在第12條指令要用到LW取出的數據,這樣就出現了旁路所不能解決的數據沖突。看圖中,當出現LW指令時(指令碼為8D04000A),后面的一條指令因為要用到lw取出的數據而被暫停一個周期,從圖中就可以看出來,10440001的指令碼占用了兩個字段。

(3)控制冒險結果分析
選擇觀察數據:時鐘信號clk 、ID部件輸入端delayslotEn信號以及輸出 端next_delayslotEn信號、ID_EX部件輸出端 ex_next_delayslotEn信號,insMem部件的輸出端inst信 號。
結果:
圖一:

圖二:

分析:在ID段檢測到跳轉指令后會發出一個信號,該信號會進入ID_EX流水寄存器,但是在下一時鐘周期上升沿到來后又會返回到ID部件,作用是通知當前在延遲槽的指令無用,使其變為空操作不輸出。在仿真圖中就可以看出當出現成功跳轉時,next_delayslotEn信號會變成高電平(測試程序中一共是三條跳轉指令,其中只有兩條發生了跳轉,有一條判斷條件不成立所以未進行跳轉)。當出現跳轉時已經進入到延遲槽中的指令就會被作為空指令來執行,然后pc值會被更改為跳轉地址,在仿真圖中也有體現。

7.3:J跳轉分析
以下跑的是第一個測試程序:
選擇觀察數據:時鐘信號clk 、insMem部件的輸入端insAddr信號以及輸 出端inst信號。
結果:

分析:跳轉指令J是無條件跳轉,在第5跳轉指令執行完后(該指令為有條件跳轉)會去執行第8條指令,也就是J型跳轉指令,指令會直接跳轉到第三條指令處執行,之后會進入這個循環的執行環境中,仿真結果與理論是一樣的。

八:參考資料

參考課本有《自己動手寫CPU》

看完覺得有幫助就順手點個贊唄 ^_^ !!!

總結

以上是生活随笔為你收集整理的Verilog实现MIPS的5级流水线cpu设计(Modelsim仿真)[通俗易懂]的全部內容,希望文章能夠幫你解決所遇到的問題。

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