下篇:Verilog流水線CPU設計(超詳細)
本篇完整工程下載鏈接
實驗 單週期CPU
一、設計目的與目標
實驗內容
- 本實例所設計CPU的指令格式的擬定;
- 基本功能部件的設計與實現;
- CPU各主要功能部件的設計與實現;
- CPU的封裝;
- 對各個單元組合而成的CPU進行指令測試,配合使用模擬仿真,瞭解指令和數據在各個單元中的傳輸過程及方向。
實驗要求
至少支持add、sub、and、or、addi、andi、ori、lw、sw、beq、bne和j十二條指令。
二、課程設計器材
硬件平臺
無
軟件平臺
- 操作系統:Win 10。
- 開發平臺:Vivado 2017.2。
- 編程語言:VerilogHDL硬件描述語言。
三、 CPU邏輯設計總體方案
單週期CPU可以看成由數據通路和和控制部件兩大部分組成。數據通路是指在指令執行過程中,數據所經過的路徑和路徑上所涉及的功能部件。而控制部件則根據每條指令的不同功能,生成對不同數據通路的不同控制信號,正確地控制指令的執行流程。因此,要設計處理器,首先需要確定處理器的指令集和指令編碼,然後確定每條指令的數據通路,最後確定數據通路的控制信號。
指令模塊
單週期(Single Cycle)CPU是指CPU從取出1條指令到執行完該指令只需1個時鐘週期。
一條指令的執行過程包括:取指令→分析指令→執行指令→保存結果(如果有的話)。對於單週期CPU來說,這些執行步驟均在一個時鐘週期內完成。
MIPS指令格式
MIPS指令系統結構有MIPS-32和MIPS-64兩種。本實驗的MIPS指令選用MIPS-32。以下所說的MIPS指令均指MIPS-32。MIPS的指令格式爲32位。下圖給出了MIPS指令的3種格式。
本實驗只選取了12條典型的MIPS指令來描述CPU邏輯電路的設計方法。下表列出了本實驗的所涉及到的12條MIPS指令。
R型指令的op均爲0,具體操作由func指定。rs和rt是源寄存器號,rd是目的寄存器號。移位指令中使用sa指定移位位數。
I型指令的低16位是立即數,計算時需擴展到32位,依指令的不同需進行零擴展和符號擴展。
J型指令的低26位是地址,是用於產生跳轉的目標地址。
指令處理流程
一般來說,CPU在處理指令時需要經過以下幾個過程:
(1) 取指令(IF):根據程序計數器PC中的指令地址,從指令存儲器中取出一條指令,同時PC根據指令字長度自動遞增產生下一條指令所需要的指令地址,但遇到“地址轉移”指令時,則控制器把“轉移地址”送入PC,當然得到的“地址”需要做些變換才送入PC。
(2) 指令譯碼(ID):對取指令操作中得到的指令進行分析並譯碼,確定這條指令需要完成的操作,由指令的[15-12]位產生相應的操作控制信號,用於驅動執行狀態中的各種操作。
(3) 指令執行(EXE):根據指令譯碼得到的操作控制信號,具體地執行指令動作,然後轉移到結果寫回狀態。
(4) 存儲器訪問(MEM):所有需要訪問存儲器的操作都將在這個步驟中執行,該步驟給出存儲器的數據地址,把數據寫入到存儲器中數據地址所指定的存儲單元或者從存儲器中得到數據地址單元中的數據。
(5) 結果寫回(WB):指令執行的結果或者訪問存儲器中得到的數據寫回相應的目的寄存器中。
數據通路
CPU的電路包括數據路徑(Data path)和控制部件(Control Unit)兩大部分。下面先給出單週期CPU的總體設計圖,再分別介紹每個路徑和控制部件的設計。
總體結構圖
上圖是一個簡單的基本上能夠在單週期上完成所要求設計的指令功能的數據通路和必要的控制線路圖。其中指令儲存在指令儲存器,數據儲存在數據存儲器。訪問存儲器時,先給出地址,然後由讀/寫信號控制。對於寄存器組,讀操作時,先給出地址,輸出端直接輸出相應數據;而在寫操作時,在 We使能信號爲1時,在時鐘邊沿觸發寫入。
設計流程邏輯圖
根據實驗原理中的單週期CPU總體結構圖,我們可以清楚的知道單週期CPU的設計應包括PC,PCAdd4,INSTMEM,CONUNIT,REGFILE,ALU,DATAMEM, EXT16T32這幾個核心模塊,其中PCAdd4模塊需要用到32位加法器CLA_32。此外還需要左移處理模塊SHIFTER_COMBINATION,一個固定左移兩位的移位器SHIFT32_L2,一個四選一多路選擇器MUX4X32,兩個32位二選一多路選擇器MUX2X32,一個5位二選一多路選擇器MUX2X5,一個數據擴展器EXT16T32。其中爲了運行整個CPU還需要加入一個頂層模塊(SingleCycleCPU)來調用這些模塊,所以自然地,這些模塊爲頂層模塊的子模塊。
設計流程邏輯圖如下(左邊爲拓展模塊,右邊爲核心模塊)。
四、模塊詳細設計
PCAdd4
- 所處位置
- 模塊功能
作爲PC寄存器的更新信號。
- 實現思路
由於每條指令32位,所以增加一個32位加法器,固定與32位的立即數4進行相加,且得到的結果在當前時鐘信號的上升沿更新進PC寄存器。
- 引腳及控制信號
Addr:當前指令地址,輸入端口
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
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
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
PC
-
所處位置
-
模塊功能
用於給出指令在指令儲存器中的地址。 -
實現思路
爲實現穩定輸出,在時鐘信號的上升沿更新,而且需要一個控制信號,在控制信號爲0的時候初始化PC寄存器,即全部置零。 -
引腳及控制信號
Clk:時鐘週期,輸入信號
Reset:控制信號,輸入信號
Result目標地址,可能是跳轉地址或者是下一條指令的地址,輸入信號
Addr:指令地址,輸出信號 -
主要實現代碼
module PC(Clk,Reset,Result,Address);
input Clk;//時鐘
input Reset;//是否重置地址。0-初始化PC,否則接受新地址
input[31:0] Result;
output reg[31:0] Address;
//reg[31:0] Address;
initial begin
Address <= 0;
end
always @(posedge Clk or negedge Reset)
begin
if (!Reset) //如果爲0則初始化PC,否則接受新地址
begin
Address <= 0;
end
else
begin
Address = Result;
end
end
endmodule
INSTMEM
-
所處位置
-
模塊功能
依據當前pc,讀取指令寄存器中相對應地址Addr[6:2]的指令。 -
實現思路
將pc的輸入作爲敏感變量,當pc發生改變的時候,則進行指令的讀取,根據相關的地址,輸出指令寄存器中相對應的指令,且在設計指令的時候,要用到12條給出的指令且儘量合理。 -
引腳及控制信號
Addr:指令地址,輸入信號
Inst:指令編碼,輸出信號 -
主要實現代碼
module INSTMEM(Addr,Inst);//指令存儲器
input[31:0]Addr;
//input InsMemRW;//狀態爲'0',寫指令寄存器,否則爲讀指令寄存器
output[31:0]Inst;
wire[7:0]Rom[31:0];
assign Rom[5'h00]=32'h20010008;//addi $1,$0,8 $1=8
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'h14220002;//bne $1,$2,2
assign Rom[5'h07]=32'hXXXXXXXX;
assign Rom[5'h08]=32'hXXXXXXXX;
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'h10440003;//beq $2,$4,3
assign Rom[5'h10]=32'hXXXXXXXX;
assign Rom[5'h11]=32'hXXXXXXXX;
assign Rom[5'h12]=32'hXXXXXXXX;
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
DATAMEM
-
所處位置
-
模塊功能
數據存儲器,通過控制信號,對數據寄存器進行讀或者寫操作,並且此處模塊額外合併了輸出DB的數據選擇器,此模塊同時輸出寫回寄存器組的數據DB。 -
實現思路
由於需要支持取數/存數指令,所以要在指令儲存器的基礎上增加寫入數據的數據寫入端口,寫使能信號。又因爲寫操作在時鐘信號的上升沿,所以要增加時鐘信號。 -
引腳及控制信號
當We爲1時,進行sw指令操作,此時Din端口輸入信號實際爲rt,Addr端口輸入信號爲rs和偏移量相加的地址,在時鐘週期上升沿將rt的值寫入改地址的儲存單元。引腳 控制信號 作用 狀態“0” 狀態“1” Addr(輸入) R 訪存地址 Din(輸入) Qb 輸入的值 Clk(輸入) Clk 時鐘週期 We(輸入) Wmem 寫使能信號 信號無效 信號有效 Dout(輸出) Dout 讀取的值
當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
SHIFTER32_L2
-
所處位置
-
模塊功能
一個固定左移兩位的移位器 -
實現思路
使用32位移位器SHIFTER32,固定左移兩位即可 -
引腳及控制信號
EXTIMM:指令中的偏移量,輸入信號
EXTIMML2:偏移量左移後的結果,輸出信號 -
主要實現代碼
module SHIFTER32_L2(X,Sh);
input [31:0] X;
output [31:0] Sh;
parameter z=2'b00;
assign Sh={X[29:0],z};
endmodule
SHIFTER_COMBINATION
-
所處位置
-
模塊功能
J指令中用以產生跳轉的目標地址 -
實現思路
跳轉的目標地址採用拼接的方式形成,最高4位爲PC+4的最高4位,中間26位爲J型指令的26位立即數字段,最低兩位爲0. -
引腳及控制信號
Inst[26:0]:指令編碼的低26位字段,輸入信號。
PCadd4:PC+4的32位字段,輸入信號。
InstL2:32位轉移目標地址,輸出信號。 -
主要實現代碼
module SHIFTER_COMBINATION(X,PCADD4,Sh);
input [26:0] X;
input [31:0] PCADD4;
output [31:0] Sh;
parameter z=2'b00;
assign Sh={PCADD4[3:0],X[26:0],z};
endmodule
MUX4X32
-
所處位置
-
模塊功能
實現目標地址的選擇。 -
實現思路
目標地址可能是PC+4,也可能是beq和bne的跳轉地址或是J型跳轉地址,所以採用一個32位四選一多路選擇器。 -
引腳及控制信號
PCadd4:PC+4的地址,輸入信號
0:空位,輸入信號
mux4x32_2:beq和bne指令的跳轉地址,輸入信號
InstL2:J指令的跳轉地址,輸入信號
Pcsrc:對地址進行選擇的控制信號,輸入信號
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
MUX2X5
-
所處位置
-
模塊功能
R型指令和I行指令的Wr信號不同,所以需要一個5位二選一選擇器進行選擇。 -
實現思路
R型指令Wr選擇rd信號,I型指令Wr選擇rt信號。 -
引腳及控制信號
Inst[15:11],:R型指令的rd信號,輸入信號
nst[20:16]:I型指令的rt信號,輸入信號
Regrt:選擇指令的控制信號,輸入信號
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
EXT16T32
-
所處位置
-
模塊功能
I指令的addi需要對立即數進行符號拓展,andi和ori需要對立即數進行零擴展,所以需要一個擴展模塊。 -
實現思路
採用一個16位擴展成32位的擴展模塊EXT16T32,實現零擴展和符號擴展 -
引腳及控制信號
Inst[15:0]:I型指令的立即數字段,輸入信號。
Se:選擇零擴展或是符號擴展的控制模塊,輸入信號。
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
MUX2X32
-
所處位置
-
模塊功能一
ALU的Y端輸入信號種類根據指令的不同而不同。 -
實現思路一
在執行R型指令時,ALU的Y端輸入信號可能來自Qb,在執行I型指令的addi,andi和ori指令時時,ALU的Y端輸入信號來自EXT16T32,所以需要一個二選一選擇器。 -
引腳及控制信號一
EXTIMM:來自EXT16T32的信號,輸入信號。
Qb:來自REGFLE中Qb端口的信號,輸入信號。
Aluqb:控制信號。
Y:輸入ALU進行後續計算的信號,輸出信號。 -
模塊功能二
對寫入寄存器的數據進行選擇。 -
實現思路二
在lw指令中,需要將DATAMEM中選中儲存器的值保存到REGFILE的寄存器中,而其他會更新REGFILE的指令的更新信號來自於ALU的R輸出端。所以需要一個二選一選擇器進行選擇。 -
引腳及控制信號二
Dout: DATAMEM的輸出值,輸入信號
R:ALU的輸出值,輸入信號
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
CONUNIT
-
所處位置
-
模塊功能
控制器是作爲CPU控制信號產生的器件,通過通過解析op得到該指令的各種控制信號,使其他器件有效或無效。 -
實現思路
參照引腳和控制信號設計 -
引腳
Inst[31:26]:Op,輸入信號。
Inst[5:0]:Func,輸入信號。
Z:零標誌信號,對Pcsrc有影響,輸入信號。
Regrt:控制輸入寄存器的Wr端口,輸出信號。
Se:控制擴展模塊,輸出信號。
Wreg:控制寄存器端的寫使能信號,輸出信號。
Aluqb:控制ALU的Y端口的輸入值,輸出信號。
Aluc:控制ALU的計算種類,輸出信號。
Wmem:控制數據存儲器的寫使能信號,輸出信號。
Pcsrc:控制目標指令地址,輸出信號。
Reg2reg:控制REHFILE更新值的來源。 -
控制信號
輸入端口 | 輸入端口 | 輸入端口 | 輸入端口 | 輸出端口 | 輸出端口 | 輸出端口 | 輸出端口 | 輸出端口 | 輸出端口 | 輸出端口 | 輸出端口 |
Op[5:0] | Func[5:0] | 備註 | Z | Regrt | Se | Wreg | Aluqb | Aluc[1:0] | Wmem | Pcsrc[1:0] | Reg2reg |
000000 | 100000 | add | X | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 |
000000 | 100010 | sub | X | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 |
000000 | 100100 | and | X | 0 | 0 | 1 | 1 | 10 | 0 | 0 | 1 |
000000 | 100101 | or | X | 0 | 0 | 1 | 1 | 11 | 0 | 0 | 1 |
001000 | - | addi | X | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
001100 | - | andi | X | 1 | 0 | 1 | 0 | 10 | 0 | 0 | 1 |
001101 | - | ori | X | 1 | 0 | 1 | 0 | 11 | 0 | 0 | 1 |
100011 | - | lw | X | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
101011 | - | sw | X | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 |
000100 | - | beq | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 1 |
000100 | - | beq | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 10 | 1 |
000101 | - | bne | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 10 | 1 |
000101 | - | bne | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 1 |
000010 | - | j | X | 1 | 0 | 0 | 1 | 0 | 0 | 11 | 1 |
- 主要實現代碼
module CONUNIT(Op,Func,Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg);
input[5:0]Op,Func;
input Z;
output Regrt,Se,Wreg,Aluqb,Wmem,Reg2reg;
output[1:0]Pcsrc,Aluc;
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 I_J=~Op[5]&~Op[4]&~Op[3]&~Op[2]&Op[1]&~Op[0];
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]=I_beq&Z|I_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;
endmodule
REGFILE
-
所處位置
-
模塊功能
給出要讀取的兩個寄存器編號和要寫入的寄存器編號,然後由Qa和Qb端口更新Ra和Rb端口的輸入編號分別輸入其值。 -
實現思路
由32個寄存器組成,增加兩個端口用於接收要讀取的兩個寄存器編號,另一個端口用於接收要寫入的寄存器的編號。且在時鐘上升沿將D寫入。 -
引腳及控制信號
Inst[25:21]:讀取寄存器編號1,輸入信號。
Inst[20:16]:讀取寄存器編號2或立即數,輸入信號。
D:寄存器更新值,輸入信號。
Wr:寫入寄存器編號3,輸入信號。
Wreg:寫使能信號,爲0的時候不能寫入,D值不更新,爲1的時候能寫入,D值更新,輸入信號。
Clk:時鐘週期,輸入信號。
Reset:清零信號,輸入信號。
Qa:輸出寄存器1的值,輸入信號。
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
ALU
-
所處位置
-
模塊功能
算數邏輯部件,需要實現加,減,按位與,按位或。 -
實現思路
需要2位控制信號控制運算類型,核心部件是32位加法器ADDSUB_32。 -
引腳及控制信號
Qa:寄存器1的值。
Y:寄存器2的值或立即數。
Aluc:控制信號。
R:輸入寄存器端口D的計算結果,輸出信號。
Z:當值爲1時代表兩個輸入信號值相等,當值爲0時代表兩個輸入信號不等,輸出信號。 -
主要實現代碼
module ALU(X,Y,Aluc,R,Z);
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
SingleCycleCPU(整合部件)
-
所處位置
-
模塊功能
實現CPU的封裝,設計輸出信號使得在方正時便於觀察其波形圖 -
實現思路
調用各個下層模塊並將他們的輸入和輸出連接到一起。 -
引腳及控制信號
CLk:時鐘週期,外部輸入信號。
Reset:清零信號,外部輸入信號。 -
主要實現代碼
module MAIN(Clk,Reset,Addr,Inst,Qa,Qb,ALU_R,NEXTADDR,D);
input Clk,Reset;
output [31:0] Inst,NEXTADDR,ALU_R,Qb,Qa,Addr,D;
wire [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D,Y,Dout,mux4x32_2,R;
wire Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem;
wire [1:0]Aluc,Pcsrc;
wire [4:0]Wr;
PC pc(Clk,Reset,Result,Addr);
PCadd4 pcadd4(Addr,PCadd4);
INSTMEM instmem(Addr,Inst);
CONUNIT conunit(Inst[31:26],Inst[5:0],Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg);
MUX2X5 mux2x5(Inst[15:11],Inst[20:16],Regrt,Wr);
EXT16T32 ext16t32(Inst[15:0],Se,EXTIMM);
SHIFTER_COMBINATION shifter1(Inst[26:0],PCadd4,InstL2);
SHIFTER32_L2 shifter2(EXTIMM,EXTIMML2);
REGFILE regfile(Inst[25:21],Inst[20:16],D,Wr,Wreg,Clk,Reset,Qa,Qb);
MUX2X32 mux2x321(EXTIMM,Qb,Aluqb,Y);
ALU alu(Qa,Y,Aluc,R,Z);
DATAMEM datamem(R,Qb,Clk,Wmem,Dout);
MUX2X32 mux2x322(Dout,R,Reg2reg,D);
CLA_32 cla_32(PCadd4,EXTIMML2,0,mux4x32_2, Cout);
MUX4X32 mux4x32(PCadd4,0,mux4x32_2,InstL2,Pcsrc,Result);
assign NEXTADDR=Result;
assign ALU_R=R;
endmodule
Test(仿真代碼)
- 主要實現代碼
module TEST;
reg Clk;
reg Reset;
wire [31:0] Addr,Inst,Qa,Qb,ALU_R,NEXTADDR;
MAIN uut(
.Clk(Clk),
.Reset(Reset),
.Addr(Addr),
.Inst(Inst),
.Qa(Qa),
.Qb(Qb),
.ALU_R(ALU_R),
.NEXTADDR(NEXTADDR),
.D(D)
);
五、仿真模擬分析
仿真波形圖
指令代碼分析
-
assign Rom[5’h00]=32’h20010008;
二進制源碼:001000 00000 00001 00000 00000 001000
指令含義:addi $1,$0,8
結果:$1=$0+8=8
-
assign Rom[5’h01]=32’h3402000C;
二進制源碼:001101 00000 00010 00000 00000 001100
指令含義:ori $2,$0,12
結果:$2=12
-
assign Rom[5’h02]=32’h00221820;
二進制源碼:000000 00001 00010 00011 00000 100000
指令含義:add $3,$1,$2
結果:$3=8+12=20
-
assign Rom[5’h03]=32’h00412022;
二進制源碼:000000 00010 00001 00100 00000 100010
指令含義:sub $4,$2,$1
結果:$4=12-8=4
-
assign Rom[5’h04]=32’h00222824;
二進制源碼:000000 00001 00010 00101 00000 100100
指令含義:and $5,$1,$2
結果:$5=b1000&b1100=b1000=h8
-
assign Rom[5’h05]=32’h00223025;
二進制源碼:000000 00001 00010 00110 00000 100101
指令含義:or $1,$2,$6
結果:$6=b1000|b1100=b1100=12=hc
-
assign Rom[5’h06]=32’h14220002;
二進制源碼:000101 00001 00010 0000000000000010
指令含義:bne $1,$2,2
結果:跳轉到本地址+2的地址位置
1):偏移量擴展到32位。
2):左移兩位。
3):加b1000。
4):加PC地址得到Addr。
5):取Addr[6:2]作爲下一個地址。
-
assign Rom[5’h07]=32’hXXXXXXXX;
-
assign Rom[5’h08]=32’hXXXXXXXX;
-
assign Rom[5’h09]=32’h10220002;
二進制源碼:000100 00001 00010 0000000000000010
指令含義:beq $1,$2,2
結果:沒有跳轉
-
assign Rom[5’h0A]=32’h0800000D;
二進制源碼:000010 00000000000000000000001101
指令含義:J OD
結果:跳轉到第14條指令的地址位置
-
assign Rom[5’h0B]=32’hXXXXXXXX;
-
assign Rom[5’h0C]=32’hXXXXXXXX;
-
assign Rom[5’h0D]=32’hAD02000A;
二進制源碼:101011 01000 00010 0000000000001010
y指令含義:sw $2 10($8)
結果:將$2的值寫入10+$8的儲存單元。 -
assign Rom[5’h0E]=32’h8D04000A;
二進制源碼:100011 01000 00100 0000000000001010
指令含義:lw $4 10($8) 數據儲存器中$8和偏移量相加的地址讀取到$4。
結果: $4=$2=12
-
assign Rom[5’h0F]=32’h10440003;
二進制源碼:000100 00010 00100 0000000000000011
指令含義:beq $2,$4,3
結果:跳轉到本地址+3的地址位置
-
assign Rom[5’h10]=32’hXXXXXXXX;
-
assign Rom[5’h11]=32’hXXXXXXXX;
-
assign Rom[5’h12]=32’hXXXXXXXX;
-
assign Rom[5’h13]=32’h30470009;
二進制源碼:001100 00010 00111 0000000000001001
指令含義:andi $2,9,$7
結果:$7=b1100&b1001=b1000=8
-
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;
六、結論和體會
體會感悟
通過此次的CPU設計實驗,讓我對CPU內部組成以及指令在CPU部件上如何運作有了一個更深的理解。在實驗過程中,我們遇到了各種問題,一開始老師佈置下來的CPU任務的時候,完全是懵的,因爲CPU器件和指令運算只在課本上學習,從來沒有真正實踐過,現在需要自己設計CPU的各個部件,而且要將指令在器件上運行,感覺很複雜。但在接下來的日子,我們沒有因爲不會而放棄,而是努力專心去設計好每個部件,對每個部件的功能進行模擬仿真,確保這一部件模塊不出錯,在設計過程中,感覺慢慢可以理清思路,也明白了下一步需要設計的東西通過此次實驗,讓我們對CPU有了更深的理解,而不只是紙上談兵。
對本實驗過程及方法、手段的改進建議
1.過程中,應該每個部件都分別進行調試之後再組裝在一起。
2.各部件儘量再拆分爲更小的部件組合而成。