【计算机系统设计】实践笔记(2)数据通路构建:第一类R型指令分析(2)
待辦事項(xiàng)
- 時(shí)鐘頻率高,取指周期長(zhǎng),遠(yuǎn)大于執(zhí)行周期,如何處理?
- 不可綜合邏輯的處理
接上一篇
【計(jì)算機(jī)系統(tǒng)設(shè)計(jì)】實(shí)踐筆記(2)數(shù)據(jù)通路構(gòu)建:第一類R型指令分析(1)
8.2 ALU運(yùn)算器
`timescale 1ns / 1ps // // Engineer:jht // Create Date: 2020/11/14 22:30:23 // Module Name: ALU_1 //module ALU_1(// datainput [31:0] A,input [31:0] B,// controlinput [3:0] ALUop,output reg [31:0] ALUresult);// convert A and B to signed numbers wire signed [31:0] A_signed = A; wire signed [31:0] B_signed = B;always @(*) begincase (ALUop)4'b0000: // addbeginALUresult <= A + B;end4'b0001: // addubeginALUresult <= A + B;end4'b0010: // subbeginALUresult <= A - B;end4'b0011: // sububeginALUresult <= A - B;end4'b0100: // andbeginALUresult <= A & B;end4'b0101: // orbeginALUresult <= A | B;end4'b0110: // xorbeginALUresult <= A ^ B;end4'b0111: // norbeginALUresult <= ~(A | B);end4'b1000: // slt // note:********signed********//beginif(A_signed < B_signed)ALUresult <= 1;elseALUresult <= 0;end4'b1001: // sltubeginif(A < B)ALUresult <= 1;elseALUresult <= 0;end4'b1010: // sllvbeginALUresult <= A << B;end4'b1011: // srlvbeginALUresult <= A >> B;end4'b1100: // srav // note: ******signed*******//beginALUresult <= A_signed >>> B;enddefault:beginALUresult <= 0;endendcase endendmodule測(cè)試文件tb_ALU_1.v
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/27 10:36:19 // Design Name: // Module Name: tb_ALU_1 // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module tb_ALU_1;// ALU_1 Inputs reg [31:0] A = 0 ; reg [31:0] B = 0 ; reg [3:0] ALUop = 0 ;// ALU_1 Outputs wire [31:0] ALUresult ;ALU_1 u_ALU_1 (.A ( A [31:0] ),.B ( B [31:0] ),.ALUop ( ALUop [3:0] ),.ALUresult ( ALUresult [31:0] ));initial begin#10ALUop = 0;A = 1;B = 4;#10ALUop = 1;A = 1;B = 5;#10ALUop = 2;A = 4;B = 1;#10ALUop = 3;A = 4;B = 2;// and#10ALUop = 4;A = 32'b1001111;B = 32'b1001001;#10ALUop = 5;A = 32'b1001111;B = 32'b1001001;#10ALUop = 6;A = 32'b1001111;B = 32'b1001001;#10ALUop = 7;A = 32'b1001111;B = 32'b1001001;// slt#30ALUop = 8;A = -1;B = 3;#10ALUop = 9;A = -1;B = 3;#10ALUop = 9;A = 1;B = 3;// sllv#30ALUop = 10;A = 32'b1001111;B = 32'd4;#10ALUop = 11;A = 32'hABCDabcd;B = 32'd4;// srav#30ALUop = 12;A = 32'hABCDabcd;B = 32'd4;#40ALUop = 4'b1111;endendmodule功能仿真成功!
8.2.1 注意事項(xiàng):有無符號(hào)數(shù)的運(yùn)算和比較
主要針對(duì)slt sltu srlv srav這幾條指令中,涉及到的對(duì)有無符號(hào)數(shù)進(jìn)行的操作。
原則:Verilog默認(rèn)都是無符號(hào)數(shù),需要顯式地聲明signed才能進(jìn)行帶符號(hào)數(shù)運(yùn)算。
注意代碼中的
// convert A and B to signed numbers wire signed [31:0] A_signed = A; wire signed [31:0] B_signed = B;這是將無符號(hào)數(shù)聲明為帶符號(hào)數(shù)的方法。
8.3 Register Files 寄存器堆
reg_files.v
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/14 22:31:09 // Design Name: // Module Name: reg_files_1 // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module reg_files_1(input clk,input rst_n,/*** read port 1 ***/input [4:0] rA, // rs fieldoutput reg [31:0] A,/*** read port 2 ***/input [4:0] rB, // rtoutput reg [31:0] B,/*** write port ***/input [4:0] rW, // rd or rtinput [31:0] writeData, // datainput RegWrite // if RegWrite == 1,you can write data to reg files);// reg files reg [31:0] register [0:31]; integer i; initial beginfor (i = 0;i < 32;i = i + 1)beginregister[i] <= 0;end end/******* write operation *******/always @(posedge clk) // sequential logic beginif(rst_n == 0) // reset is invalidbeginif((RegWrite == 1'b1) && (rW != 5'b0)) // write is valid and address is not equal zerobeginregister[rW] <= writeData;endelse;endelse; end/******* rA read operation *******/ always @(*) // combinational logic beginif(rst_n == 1)beginA <= 32'b0;endelse if(rA == 5'b0)beginA <= 32'b0;endelsebeginA <= register[rA];end end/******* rB read operation *******/ always @(*) // combinational logic beginif(rst_n == 1)beginB <= 32'b0;endelse if(rB == 5'b0) // $zerobeginB <= 32'b0;endelsebeginB <= register[rB];end endendmodule測(cè)試文件tb_reg_files_1.v
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/27 10:11:14 // Design Name: // Module Name: tb_reg_files_1 // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module tb_reg_files_1;// reg_files_1 Parameters parameter PERIOD = 10;// reg_files_1 Inputs reg clk = 0 ; reg rst_n = 1 ; reg [4:0] rA = 0 ; reg [4:0] rB = 0 ; reg [4:0] rW = 0 ; reg [31:0] writeData = 0 ; reg RegWrite = 0 ;// reg_files_1 Outputs wire [31:0] A ; wire [31:0] B ;initial beginforever#(PERIOD/2) clk=~clk; endinitial begin#(PERIOD*2) rst_n = 0; endreg_files_1 u_reg_files_1 (.clk ( clk ),.rst_n ( rst_n ),.rA ( rA [4:0] ),.rB ( rB [4:0] ),.rW ( rW [4:0] ),.writeData ( writeData [31:0] ),.RegWrite ( RegWrite ),.A ( A [31:0] ),.B ( B [31:0] ));initial begin#20RegWrite = 1;rW = 0;writeData = 32'hff;#10rW = 1;writeData = 32'hff;#10rA = 1;#10rB = 1;#10rA = 0;rB = 0; endendmodule初步功能仿真成功!
9 連接已有器件
9.1 增加ROM
使用IP核,參考東南大學(xué)計(jì)算機(jī)系統(tǒng)設(shè)計(jì)MOOC9.1節(jié)的做法。
9.2 將已有部件連接起來!
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/27 11:41:34 // Design Name: // Module Name: datapath_1 // Project Name: // Target Devices: // Tool Versions: // Description: 僅僅實(shí)現(xiàn)了幾個(gè)簡(jiǎn)單的R類指令的最簡(jiǎn)單的數(shù)據(jù)通路,不與外界交互 // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module datapath_1(input clk,input rst_n);/******** PC ********/// pc_1 Outputs wire [31:0] pcOld;pc_1 u_pc_1 (.clk ( clk ),.rst_n ( rst_n ),.pcNew ( pcOld ), // pcNew = pcOld + 4; no selection.pcOld ( pcOld ));/******** Instruction ROM ********/// blk_mem_gen_0 Inputs wire [13:0] addra = pcOld[15:2];// blk_mem_gen_0 Outputs // instructions wire [31:0] instruction;blk_mem_gen_0 u_blk_mem_gen_0 (.clka ( clk ),.addra ( addra ),.douta ( instruction ));/******** Reg Files ********/// reg_files_1 Inputs wire [4:0] rA = instruction[25:21]; wire [4:0] rB = instruction[20:16]; wire [4:0] rW = instruction[15:11]; wire [31:0] writeData; wire RegWrite;// reg_files_1 Outputs wire [31:0] A; wire [31:0] B;reg_files_1 u_reg_files_1 (.clk ( clk ),.rst_n ( rst_n ),.rA ( rA ),.rB ( rB ),.rW ( rW ),.writeData ( writeData ),.RegWrite ( RegWrite ),.A ( A ),.B ( B ) );/******** ALU ********/// ALU_1 Inputs // wire [31:0] A; // wire [31:0] B; wire [3:0] ALUop;// ALU_1 Outputs wire [31:0] ALUresult = writeData;ALU_1 u_ALU_1 (.A ( A ),.B ( B ),.ALUop ( ALUop ),.ALUresult ( ALUresult ) );/******** controler ********/// control_1 Inputs wire [5:0] op = instruction[31:26]; wire [5:0] func = instruction[5:0];// control_1 Outputs // wire RegWrite // wire [3:0] ALUop;control_1 u_control_1 (.op ( op ),.func ( func ),.RegWrite ( RegWrite ),.ALUop ( ALUop ) );endmodule9.3 測(cè)試我們的數(shù)據(jù)通路
注意賦值的是i而不是0了。
測(cè)試文件如下
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/27 12:12:14 // Design Name: // Module Name: tb_datapath_1 // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module tb_datapath_1;// datapath_1 Parameters parameter PERIOD = 10;// datapath_1 Inputs reg clk = 0 ; reg rst_n = 1 ;// datapath_1 Outputsinitial beginforever #(PERIOD/2) clk=~clk; endinitial begin#(PERIOD*2) rst_n = 0; enddatapath_1 u_datapath_1 (.clk ( clk ),.rst_n ( rst_n ) );endmoduleRTL優(yōu)化
測(cè)試指令如下
以上錯(cuò)誤!因?yàn)橐淮涡赃B接了太多器件,不符合單元測(cè)試原則,重新開始,重要的是,ROM的IP核沒有測(cè)試!
9.4 構(gòu)建取值模塊
我們先把PC和ROM連接起來測(cè)試。
然后發(fā)現(xiàn)……很詭異,PC似乎對(duì)ROM不起作用?
這個(gè)IP核……居然有延遲??不是瞬間取得指令……需要等一個(gè)周期后,再等待上升沿,才能取指。也就是說,ROM的取指需要等待一個(gè)額外的時(shí)鐘周期,這才是真實(shí)世界。
9.5 插敘:帶延遲的ROM
實(shí)際上,訪存時(shí)間更長(zhǎng),取指比較慢,這個(gè)事實(shí)我們都知道,現(xiàn)在,我們真地面臨這個(gè)問題了。
理想取指的時(shí)序圖,與帶一個(gè)時(shí)鐘周期延遲的時(shí)序圖,是不一樣的!
在單周期CPU中
- 理想瞬間取指,那么更新PC值需要是下降沿,而寫寄存器堆需要是上升沿
- 帶一個(gè)時(shí)鐘周期延遲的,就可以都是上升沿,因?yàn)槿∠乱粭l指令的過程占一個(gè)時(shí)鐘周期,此時(shí)CPU就講當(dāng)前指令執(zhí)行完了,也就是取下一條指令和CPU執(zhí)行當(dāng)前指令,同步進(jìn)行。
特別注意:默認(rèn)值0的指令會(huì)比較詭異
在我們的設(shè)計(jì)中,pc默認(rèn)是0,因此……0號(hào)地址的指令會(huì)被直接取出來,但是如果沒有復(fù)位也是不能指令的,這個(gè)情況下,其實(shí)可以用nop指令(全是0)作為0號(hào)地址的指令。
9.6 完整數(shù)據(jù)通路的實(shí)現(xiàn)與測(cè)試
讓我們返回看看。
datapath_1.v
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/27 11:41:34 // Design Name: // Module Name: datapath_1 // Project Name: // Target Devices: // Tool Versions: // Description: 僅僅實(shí)現(xiàn)了幾個(gè)簡(jiǎn)單的R類指令的最簡(jiǎn)單的數(shù)據(jù)通路,不與外界交互 // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module datapath_1(input clk,input rst_n);/******** PC ********/// pc_1 Outputs wire [31:0] pcOld;pc_1 u_pc_1 (.clk ( clk ),.rst_n ( rst_n ),.pcNew ( pcOld ), // pcNew = pcOld + 4; no selection.pcOld ( pcOld ));/******** Instruction ROM ********/// blk_mem_gen_0 Inputs wire [13:0] addra = pcOld[15:2];// blk_mem_gen_0 Outputs // instructions wire [31:0] instruction;blk_mem_gen_0 u_blk_mem_gen_0 (.clka ( clk ),.addra ( addra ),.douta ( instruction ));/******** Reg Files ********/// reg_files_1 Inputs wire [4:0] rA = instruction[25:21]; wire [4:0] rB = instruction[20:16]; wire [4:0] rW = instruction[15:11]; wire [31:0] writeData; wire RegWrite;// reg_files_1 Outputs wire [31:0] A; wire [31:0] B;reg_files_1 u_reg_files_1 (.clk ( clk ),.rst_n ( rst_n ),.rA ( rA ),.rB ( rB ),.rW ( rW ),.writeData ( writeData ),.RegWrite ( RegWrite ),.A ( A ),.B ( B ));/******** ALU ********/// ALU_1 Inputs // wire [31:0] A; // wire [31:0] B; wire [3:0] ALUop;// ALU_1 Outputs // wire [31:0] ALUresult = writeData;【】【為什么不能用?】ALU_1 u_ALU_1 (.A ( A ),.B ( B ),.ALUop ( ALUop ),.ALUresult ( writeData ));/******** controler ********/// control_1 Inputs wire [5:0] op = instruction[31:26]; wire [5:0] func = instruction[5:0];// control_1 Outputs // wire RegWrite // wire [3:0] ALUop;control_1 u_control_1 (.op ( op ),.func ( func ),.RegWrite ( RegWrite ),.ALUop ( ALUop ));endmoduleRTL優(yōu)化
測(cè)試文件 tb_datapath_1.v
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/27 12:12:14 // Design Name: // Module Name: tb_datapath_1 // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module tb_datapath_1;// datapath_1 Parameters parameter PERIOD = 10;// datapath_1 Inputs reg clk = 0 ; reg rst_n = 1 ;// datapath_1 Outputsinitial beginforever #(PERIOD/2) clk=~clk; endinitial begin#(PERIOD*2) rst_n = 0; enddatapath_1 u_datapath_1 (.clk ( clk ),.rst_n ( rst_n ) );endmodule仿真結(jié)果
測(cè)試指令
指令編碼
00000000 00430820 00811021 00412022 00832823 00e83024 00c83825 00c83826 00e64027 016c502a 018b502b 01a56004 01a56006 01e57007注意事項(xiàng)
10 驚人的事實(shí):我們已經(jīng)構(gòu)建了完整的數(shù)據(jù)通路
你可能感到驚訝,但這就是事實(shí),我們已經(jīng),構(gòu)建好了一個(gè)CPU,并且它能夠執(zhí)行13條指令!
這簡(jiǎn)直太酷了不是嗎!難以想象……你可能會(huì)說?這……就完成了?是的沒錯(cuò),如果我們只需要13條指令的CPU,并且不需要與外界交互的話,真的已經(jīng)完成了,當(dāng)然……這個(gè)CPU沒什么價(jià)值,不過后續(xù)我們會(huì)改進(jìn)它的不是嗎?這很有趣的!
我們會(huì)一步步地完成一個(gè)完整的CPU,最終變成五級(jí)流水線CPU,這簡(jiǎn)直太棒了!讓我們一起加油!
來看看示意圖,注意,只是示意圖,pc的位寬并不是標(biāo)準(zhǔn)的32位而是8位,總之,這就是完整的數(shù)據(jù)通路了。
只不過這個(gè)CPU還不能與外界交互……但是,它的確能夠執(zhí)行指令了不是嗎?后續(xù)我們慢慢改進(jìn)就是了。
10.1 構(gòu)建我們的第一個(gè)CPU
在上面我們已經(jīng)運(yùn)行測(cè)試過了,不再重復(fù)。
綜合實(shí)現(xiàn)看下面:
實(shí)踐筆記(2)插敘:綜合與實(shí)現(xiàn)
10.2 值得優(yōu)化的點(diǎn)
在有些時(shí)候,我們的指令沒有準(zhǔn)備好,但是Reg已經(jīng)讀取出去了,可能有隱患,我們可以給Reg Files加上讀使能信號(hào),但是本次不加了。
11 數(shù)據(jù)流建模傳輸問題:不止連線
數(shù)據(jù)流建模傳輸問題:賦值傳輸有方向
經(jīng)驗(yàn)教訓(xùn),RTL建??床怀鰜韨鬏敺较?#xff01; 行為仿真很必要呀!
12 取指延遲
我們都知道,訪存是很慢的(相比于CPU執(zhí)行),在本次示例中,ROM取指,需要2個(gè)時(shí)鐘周期,因此,我們的單周期CPU,更新PC和更新寄存器堆,都可以上升沿觸發(fā)。
PC也能夠保存下一條指令的地址,在取下一條指令的同時(shí),當(dāng)前指令也在執(zhí)行,取完下一條指令,當(dāng)前指令也執(zhí)行完成了。
我們看時(shí)序圖,從PC更新,到取得PC對(duì)應(yīng)的指令,需要2個(gè)時(shí)鐘周期。
也就是說,向內(nèi)存發(fā)出指令地址之后,需要兩個(gè)時(shí)鐘周期,指令才能被取得,在此期間,CPU內(nèi)的指令還是原來的指令,該指令也已經(jīng)在一個(gè)時(shí)鐘周期執(zhí)行完成,執(zhí)行一條指令需要三個(gè)時(shí)鐘周期。
你可能疑惑,那pc每個(gè)時(shí)鐘周期更新一次,兩個(gè)時(shí)鐘周期才能夠取到指令,不會(huì)沖突嗎?當(dāng)然不會(huì)。想象一下高速公路的汽車! pc發(fā)出的時(shí)間不一樣,是可以排隊(duì)的,沒關(guān)系的,不會(huì)超車插隊(duì),另外,數(shù)據(jù)變化需要時(shí)間的,暫時(shí)淺顯理解即可。
13 疑惑點(diǎn)
當(dāng)取指非常慢的時(shí)候,由應(yīng)該如何處理?此時(shí)如果仍然每個(gè)周期PC + 4,但是10個(gè)周期(假設(shè))才能取指,是否仍然可行?
我們?cè)囋?#xff0c;將周期改為2ns,也就是500MHz的時(shí)鐘頻率。
好吧……看起來沒有問題,這個(gè)問題以后再解決。
500MHz都沒問題了,那基本就沒事了其實(shí)。
開發(fā)板使用的100MHz,也就是時(shí)鐘周期是10ns,足夠滿足本科階段需求了,這個(gè)細(xì)節(jié)問題目前不需要關(guān)注,無傷大雅,應(yīng)當(dāng)先抓住主要矛盾。
總結(jié)
以上是生活随笔為你收集整理的【计算机系统设计】实践笔记(2)数据通路构建:第一类R型指令分析(2)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 打胎不孕不育
- 下一篇: 问一个超级新手的问题,关于css百分比