怎么做php網(wǎng)站一鍵開發(fā)小程序
Lab3 Rewrite V1.0
版本控制
版本 | 描述 |
---|---|
V0 | |
V1.0 | 相對V0變化: 修改了文件名,各階段以_stage結尾(因為if是關鍵詞,所以module名不能叫if,遂改為if_stage,為了統(tǒng)一命名,將所有module后綴加上_stage) 刪除了imm_sign信號(默認對立即數(shù)進行有符號數(shù)擴展) 由于對sw指令進行了重新理解:無論如何都是需要將rt_data傳遞給EXE階段,故將部分譯碼邏輯進行后移至EXE階段,避免id_to_exe_data總線過于龐大 將ins_shmat剔除出id_to_exe_data,因為imm包括ins_shamt 對信號進行重命名(例如在ID階段有個信號叫rf_we,最終要傳遞給WB階段,那么在EXE階段,該信號叫作exe_rf_we,同理mem_rf_we,wb_rf_we),不然都叫rf_we,Debug的時候太痛苦了。 |
Top頂層
接口信號
MYCPU_TOP.v(TOP)
名稱 | 寬度 | 方向 | 描述 |
---|---|---|---|
時鐘與復位 | |||
clk | 1 | I | 時鐘信號,來自clk_pll的輸出時鐘 |
resetn | 1 | I | 復位信號,低電平同步復位 |
取指端訪存接口 | |||
inst_sram_en | 1 | O | 指令RAM使能信號,高電平有效 |
inst_sram_wen | 4 | O | 指令RAM字節(jié)寫使能信號,高電平有效 |
inst_sram_addr | 32 | O | 指令RMA讀寫地址,字節(jié)尋址 |
inst_sram_wdata | 32 | O | 指令RAM寫數(shù)據(jù) |
inst_sram_rdata | 32 | I | 指令RAM讀數(shù)據(jù) |
數(shù)據(jù)端訪存接口 | |||
data_sram_en | 1 | O | 數(shù)據(jù)RAM使能信號,高電平有效 |
data_sram_wen | 4 | O | 數(shù)據(jù)RAM字節(jié)寫使能信號,高電平有效 |
data_sram_addr | 32 | O | 數(shù)據(jù)RAM讀寫地址,字節(jié)尋址 |
data_sram_wdata | 32 | O | 數(shù)據(jù)RAM寫數(shù)據(jù) |
data_sram_rdata | 32 | I | 數(shù)據(jù)RAM讀數(shù)據(jù) |
debug信號,供驗證平臺使用 | |||
debug_wb_pc | 32 | O | 寫回級(多周期最后一級)的PC,需要myCPU里將PC一路傳遞到寫回級 |
debug_wb_rf_wen | 4 | O | 寫回級寫寄存器堆(regfiles)的寫使能,為字節(jié)使能,如果myCPU寫regfiles為單字節(jié)寫使能,則將寫使能擴展成4位即可 |
debug_wb_rf_wnum | 5 | O | 寫回級寫regfiles的目的寄存器號 |
debug_wb_rf_wdata | 32 | O | 寫回級寫regfiles的寫數(shù)據(jù) |
接口時序
略(MIPS經(jīng)典五級流水線)
代碼結構
MYCPU_TOP.v
|____IF.v
|____ID.v
|____RF.v(2個讀端口,1個寫端口)
|____EXE.v
|____ALU.v
|____MEM.v
|____WB.v
|____MYCPU.h
DATA_RAM.v
IF.v(修改為IF_STAGE,因為會與關鍵詞if沖突)
接口信號
名稱 | 寬度 | 方向 | 描述 |
---|---|---|---|
時鐘與復位 | |||
clk | 1 | I | 時鐘信號,來自clk_pll的輸出時鐘 |
resetn | 1 | I | 復位信號,低電平同步復位 |
與TOP | |||
inst_sram_en | 1 | O | RAM使能信號,高電平有效 |
inst_sram_wen | 4 | O | RAM字節(jié)寫使能信號,高電平有效 |
inst_sram_addr | 32 | O | RMA讀寫地址,字節(jié)尋址 |
inst_sram_wdata | 32 | O | RAM寫數(shù)據(jù) |
inst_sram_rdata | 32 | I | RAM讀數(shù)據(jù) |
與ID | |||
id_to_if_allowin | 1 | I | pipe allowin |
if_to_id_vld | 1 | O | pipe valid |
if_to_id_data | 64 | O | pipe data(instruction 32-bits, pc 32-bits) |
jump_bus | 33 | I | branch instructions(enable 1bit,address 32-bits) |
接口時序
ID.v
接口信號
名稱 | 寬度 | 方向 | 描述 |
---|---|---|---|
時鐘與復位 | |||
clk | 1 | I | 時鐘信號,來自clk_pll的輸出時鐘 |
resetn | 1 | I | 復位信號,低電平同步復位 |
與IF | |||
id_to_if_allowin | 1 | O | pipe allowin |
if_to_id_vld | 1 | I | pipe valid |
if_to_id_data | 64 | I | pipe data(instruction 32-bits, pc 32-bits) |
jump_bus | 33 | O | branch instructions(enable 1bit,address 32-bits) |
與EXE | |||
exe_to_id_allowin | 1 | I | pipe allowin |
id_to_exe_vld | 1 | O | pipe valid |
id_to_exe_data | 135 | O | {ins_R:1, ins_I:1, imm:16, alu_op:13, mem_rd:1, mem_we:1, rf_we:1, rf_dst_addr:5, data_1:32, data_2:32, pc:32} |
與WB | |||
wb_to_rf_bus | 38 | I | {rf_we:1, rf_addr:5, rf_data:32} |
接口信號(RF.v)
名稱 | 寬度 | 方向 | 描述 |
---|---|---|---|
時鐘與復位 | |||
clk | 1 | I | 時鐘信號,來自clk_pll的輸出時鐘 |
與ID內(nèi)部信號 | |||
rf_r_addr1 | 5 | I | RF讀地址1 |
rf_r_data1 | 32 | O | RF讀數(shù)據(jù)1 |
rf_r_addr2 | 5 | I | RF讀地址2 |
rf_r_data2 | 32 | O | RF讀數(shù)據(jù)2 |
rf_wen1 | 1 | I | RF寫使能1 |
rf_w_addr1 | 5 | I | RF寫地址1 |
rf_w_data1 | 32 | O | RF寫數(shù)據(jù)1 |
接口時序
電路設計
圖3-4-1 譯碼電路分組(注:黃線少畫了兩條)
根據(jù)附錄——MIPS指令。由于跳轉指令不傳遞給EXE階段,直接傳遞給IF階段,且為純組合邏輯輸出,有可能成為關鍵路徑,故對跳轉指令單獨處理。除了跳轉指令外,涉及加法(減法歸為加法)的指令如圖3-4-1所示,即ins_addu、ins_addiu、ins_subu、ins_lw、ins_sw。
對于圖3-4-1的拼接運算,可以當作移位運算執(zhí)行。
EXE.v
接口信號
名稱 | 寬度 | 方向 | 描述 |
---|---|---|---|
時鐘與復位 | |||
clk | 1 | I | 時鐘信號,來自clk_pll的輸出時鐘 |
resetn | 1 | I | 復位信號,低電平同步復位 |
與TOP(外接的DATA_RAM) | |||
data_sram_en | 1 | O | 數(shù)據(jù)RAM使能信號,高電平有效 |
data_sram_wen | 4 | O | 數(shù)據(jù)RAM字節(jié)寫使能信號,高電平有效(4個比特,應該代表32 = 4 bytes) |
data_sram_addr | 32 | O | 數(shù)據(jù)RAM讀寫地址,字節(jié)尋址 |
data_sram_wdata | 32 | O | 數(shù)據(jù)RAM寫數(shù)據(jù) |
與ID | |||
exe_to_id_allowin | 1 | O | pipe allowin |
id_to_exe_vld | 1 | I | pipe valid |
id_to_exe_data | 135 | I | {ins_R:1, ins_I:1, imm:16, alu_op:13, mem_rd:1, mem_we:1, rf_we:1, rf_dst_addr:5, data_1:32, data_2:32, pc:32} |
與MEM | |||
mem_to_id_allowin | 1 | I | pipe allowin |
exe_to_mem_vld | 1 | O | pipe valid |
exe_to_mem_data | 71 | O | {mem_rd:1, rf_we:1, rf_dst_addr:5, pc:32(其實可以刪掉pc,這里是debug顯示用的,可以叫debug_pc), exe_result:32 |
接口信號(ALU.v)
暫時不需要時鐘和復位,純組合邏輯
名稱 | 寬度 | 方向 | 描述 |
---|---|---|---|
時鐘與復位 | |||
clk | 1 | I | 時鐘信號,來自clk_pll的輸出時鐘 |
resetn | 1 | I | 復位信號,低電平同步復位 |
與ID內(nèi)部信號 | |||
alu_shamt | 6 | I | ALU移位(R-指令的shamt部分) |
alu_op | 13 | I | ALU操作(加、減、乘除、位運算) |
alu_din1 | 32 | I | ALU輸入1 |
alu_din2 | 32 | I | ALU輸入2 |
alu_out | 32 | O | ALU輸出 |
接口時序
MEM.v
接口信號
名稱 | 寬度 | 方向 | 描述 |
---|---|---|---|
時鐘與復位 | |||
clk | 1 | I | 時鐘信號,來自clk_pll的輸出時鐘 |
resetn | 1 | I | 復位信號,低電平同步復位 |
與TOP(外接的DATA_RAM) | |||
data_sram_rdata | 32 | I | 數(shù)據(jù)RAM讀數(shù)據(jù) |
與EXE | |||
mem_to_exe_allowin | 1 | O | pipe allowin |
exe_to_mem_vld | 1 | I | pipe valid |
exe_to_mem_data | 71 | I | {mem_rd:1, rf_we:1, rf_dst_addr:5, pc:32(其實可以刪掉pc,這里是debug顯示用的,可以叫debug_pc), exe_result:32} |
與WB | |||
wb_to_mem_allowin | 1 | I | pipe allowin |
mem_to_wb_vld | 1 | O | pipe valid |
mem_to_wb_data | 70 | O | { rf_we:1, rf_dst_addr:5, mem_result:32, pc:32(其實可以刪掉pc,這里是debug顯示用的,可以叫debug_pc)} |
WB.v
接口信號
名稱 | 寬度 | 方向 | 描述 |
---|---|---|---|
時鐘與復位 | |||
clk | 1 | I | 時鐘信號,來自clk_pll的輸出時鐘 |
resetn | 1 | I | 復位信號,低電平同步復位 |
與TOP | |||
debug_wb_pc | 32 | O | 寫回級(多周期最后一級)的PC,需要myCPU里將PC一路傳遞到寫回級(與原書保持一致) |
debug_wb_rf_wen | 4 | O | 寫回級寫寄存器堆(regfiles)的寫使能,為字節(jié)使能,如果myCPU寫regfiles為單字節(jié)寫使能,則將寫使能擴展成4位即可(與原書保持一致) |
debug_wb_rf_wnum | 5 | O | 寫回級寫regfiles的目的寄存器號(與原書保持一致) |
debug_wb_rf_wdata | 32 | O | 寫回級寫regfiles的寫數(shù)據(jù)(與原書保持一致) |
與MEM | |||
wb_to_mem_allowin | 1 | O | pipe allowin |
mem_to_wb_vld | 1 | I | pipe valid |
mem_to_wb_data | 70 | I | { rf_we:1, rf_dst_addr:5, mem_result:32, pc:32(其實可以刪掉pc,這里是debug顯示用的,可以叫debug_pc)} |
與ID | |||
wb_to_rf_bus | 38 | O | {rf_we:1, rf_addr:5, rf_data:32} |
接口時序
附錄——參考
- 參考:處理機流水線------經(jīng)典五段流水線-CSDN博客
附錄——原書指令
指令 | sel_nextpc | inst_ram_wen | inst_ram_wen | sel_alu_src1 | sel_alu_src2 | alu_op | data_ram_en | data_ram_wen | rf_we | sel_rf_dst | sel_rf_res |
---|---|---|---|---|---|---|---|---|---|---|---|
ADDU | 0001 | 1 | 0 | 001 | 001 | 000000000001 | 0 | 0 | 1 | 001 | 0 |
ADDIU | 0001 | 1 | 0 | 001 | 010 | 000000000001 | 0 | 0 | 1 | 010 | 0 |
SUBU | 0001 | 1 | 0 | 001 | 001 | 000000000010 | 0 | 0 | 1 | 001 | 0 |
LW | 0001 | 1 | 0 | 001 | 010 | 000000000001 | 1 | 0 | 1 | 010 | 1 |
SW | 0001 | 1 | 0 | 001 | 010 | 000000000001 | 1 | 1 | 0 | 000 | 0 |
BEQ | 0010 | 1 | 0 | 000 | 000 | 000000000000 | 0 | 0 | 0 | 000 | 0 |
BNE | 0010 | 1 | 0 | 000 | 000 | 000000000000 | 0 | 0 | 0 | 000 | 0 |
JAL | 0100 | 1 | 0 | 010 | 100 | 000000000001 | 0 | 0 | 1 | 100 | 0 |
JR | 1000 | 1 | 0 | 000 | 000 | 000000000000 | 0 | 0 | 0 | 000 | 0 |
SLT | 0001 | 1 | 0 | 001 | 001 | 000000000100 | 0 | 0 | 1 | 001 | 0 |
SLTU | 0001 | 1 | 0 | 001 | 001 | 000000001000 | 0 | 0 | 1 | 001 | 0 |
SLL | 0001 | 1 | 0 | 100 | 001 | 000100000000 | 0 | 0 | 1 | 001 | 0 |
SRL | 0001 | 1 | 0 | 100 | 001 | 001000000000 | 0 | 0 | 1 | 001 | 0 |
SRA | 0001 | 1 | 0 | 100 | 001 | 010000000000 | 0 | 0 | 1 | 001 | 0 |
LUI | 0001 | 1 | 0 | 000 | 010 | 100000000000 | 0 | 0 | 1 | 010 | 0 |
AND | 0001 | 1 | 0 | 001 | 001 | 000000010000 | 0 | 0 | 1 | 001 | 0 |
OR | 0001 | 1 | 0 | 001 | 001 | 000001000000 | 0 | 0 | 1 | 001 | 0 |
XOR | 0001 | 1 | 0 | 001 | 001 | 000010000000 | 0 | 0 | 1 | 001 | 0 |
NOR | 0001 | 1 | 0 | 001 | 001 | 000000100000 | 0 | 0 | 1 | 001 | 0 |
附錄——Debug
PC復位問題
PC的跳轉有誤,直接看IF_STAGE.v
修改代碼為:
RegFile的零寄存器問題
修改如下:
RF數(shù)據(jù)高阻
發(fā)現(xiàn)數(shù)據(jù)有錯,應當為63:32
跳轉指令的PC值
跳轉指令的PC,本人使用的都是ID階段的pc,經(jīng)過vivado調(diào)試,發(fā)現(xiàn)有誤,隧改為如下,即使用IF階段的pc:
lui譯碼錯誤
在ID階段,lui指令譯碼錯誤,具體如下:
assign ins_lui = op_ext[6’h15] & rs_ext[5’h00];//錯誤
改為如下:
assign ins_lui = op_ext[6’h0f] & rs_ext[5’h00];
addiu執(zhí)行錯誤
經(jīng)排查,發(fā)現(xiàn)在ID階段,忘了聲明rs_data和rt_data這兩個變量,導致被默認為1 bit(實際都是32 bit的變量)
addiu執(zhí)行錯誤
(影響Debug了)
經(jīng)排查,發(fā)現(xiàn)rs數(shù)據(jù)讀取為高阻,向前追溯,發(fā)現(xiàn)是寫寄存器的時候,寫入的是高阻,最終發(fā)現(xiàn)在WB階段的,rf_we始終為高,更改如下:
assign rf_we = wb_data[69] ;
assign debug_wb_rf_wen = {4{rf_we}} ;
assign wb_to_rf_bus[37] = rf_we & wb_vld;
改為:
assign rf_we = wb_data[69] & wb_vld;
assign debug_wb_rf_wen = {4{rf_we}} ;
assign wb_to_rf_bus[37] = rf_we ;
然而還是有錯,遂向前回溯,發(fā)現(xiàn)RF.v中的rf_group聲明有誤:
reg [31:0] rf_group [4:0];
改為:
reg [31:0] rf_group [31:0];
lw錯誤
發(fā)現(xiàn)電路設計本身就有問題,原因為:從CSDN上的一個MIPS指令集設計的電路,但是該CSDN上的內(nèi)容是錯的!!!
電路設計錯誤:發(fā)現(xiàn)rf_we漏掉了ins_lw
更改如下:
assign rf_we = ins_addu
|ins_addiu
|ins_subu
|ins_lw
|ins_jal
|ins_slt xxxxxx ;
subu錯誤
assign alu_din2_two_cmpl[31] = 1’b1;
assign alu_din2_two_cmpl[30:0] = (~alu_din2) + 1’b1;
上面兩句,修改為下:
assign alu_din2_two_cmpl[31:0] = (~alu_din2) + 1’b1;
在MIPS指令中有subu和sub兩種指令,(lab3只要求實現(xiàn)subu,不要求實現(xiàn)sub指令)而在代碼中本人將subu簡寫為sub是不合適的,已全部修改為subu
slt報錯
原始代碼:
assign result_slt = ($signed(alu_din1) < $signed(alu_din2)) ? 32’h1: 32’h0;
學習了下原書上的源碼,發(fā)現(xiàn)可以將比較運算合并至減法運算,于是修改了slt(同時也修改了sltu)如下:
- assign add_din2 = (alu_subu | alu_slt | alu_sltu) ? alu_din2_two_cmpl
- alu_din2;
另外,我發(fā)現(xiàn)單獨進行求補碼運算,可能會浪費加法器,不利于vivado優(yōu)化,遂修改
修改為:
nor報錯
assign result_nor = ~result_xor ;
更改為:
assign result_nor = ~ result_or ;
srl報錯
發(fā)現(xiàn)ID階段的譯碼錯誤:
assign ins_srl = op_ext[6’h00] & sa_ext[5’h00] & fun_ext[6’h06];
更改為:
assign ins_srl = op_ext[6’h00] & rs_ext[5’h00] & fun_ext[6’h02];
sra報錯
assign result_sra = alu_din2 >>> alu_shamt ;
更改為:
assign result_sra = $signed(alu_din2) >>> alu_shamt ;
sw/lw報錯
lw報錯,經(jīng)排查是因為sw命令有誤
本人設計的時候沒有認真分析sw指令,導致EXE階段的sram_wdata數(shù)據(jù)有誤。
具體地講,由于本人設計階段欠缺,誤認為加法結果給到sram_wdata(實際上加法結果是給sram_addr),導致出錯。
由于欠思考導致總線也需要更改,需要將rt_data從ID階段傳遞給exe階段,因為sw指令執(zhí)行中需要將rt_data賦給sram_wdata。
bne出錯
又是碼錯了
assign jump_bne = (rt_equ_rs == 1’b0) & ins_beq ;
更改為:
assign jump_bne = (rt_equ_rs == 1’b0) & ins_bne ;
完結
還有一些小bug沒有記錄,終于pass了,完結。
后記
-
原書是將regfile.v當作ID_stage的一個子模塊,WB_stage寫回時,也是通過ID_stage的頂層將信號傳遞到regfile模塊。本設計將regfile.v置于與ID、WB的同一hierarchy
-
原書將跳轉指令(如JAL)的譯碼放在ID_stage模塊中(沒有問題,因為譯碼就是在ID_stage階段),并以組合邏輯的形式傳遞給IF_stage(必須用組合邏輯,否則會影響流水)。本設計將跳轉指令放在IF模塊中,避免組合邏輯穿越模塊邊界。(還是不要合并,因為R、I、J型指令均含有跳轉指令,合并至IF模塊,會增加大量的額外譯碼邏輯。)
-
原書的譯碼方式值得學習:
若是按我之前的寫法,大概率會寫成如下形式:
always@(*)begin
case(xxxx)
…
endcase
end
always-case的形式容易寫錯,而且不夠清晰。使用原書的寫法,避免寫成:
inst_addu = (op == 6’h0 ) & (func == 6’h21) & (sa == 5’h00);
- 小括號太多,看著就亂
- 等號也影響糾錯
- 原書將0寫成00,格式上是對齊的,更舒服
- 另外我猜測將判斷邏輯寫成generate—endgenerate的形式,也更容易讓編譯器進行優(yōu)化
-
在自己設計譯碼的時候,本人遇到一個問題,譯碼到什么程度才算“譯碼”。是譯碼出R\I\J型指令(每種類型用1bit標志位表示),還是譯碼至具體的加減乘除?
我的思想:EXE除了負責寄存一些必要數(shù)據(jù)外(比如WB需要的數(shù)據(jù)),其核心執(zhí)行內(nèi)容應當只有:加、減、乘、除、移位、與、或、非、異或。也就是說,ID階段負責輸入的數(shù)據(jù)給準備好級EXE。然后我就在想Regfile怎么搞,因為Regfile讀是不需要周期的(即本周期給出地址,本周就可以得到數(shù)據(jù)),但是WB寫Regfile的時候,如果同時讀Regfile的同一地址,怎么辦呢?這個讀寫沖突應當放在Regfile中處理嗎?
另一方面,EXE的執(zhí)行時,輸入可以是寄存器(比如and指令),也可以是pc(比如跳轉指令)。當輸入是寄存器時,需要讀Regfile,當輸入是pc時,不需要讀Regfile,將Regfile置于與ID、EXE同一hierarchy,意味著需要在EXE階段判斷輸入是pc還是來自Regfile,這樣增加了復雜性。(我現(xiàn)在理解了原書為什么要把Regfile當作ID的子模塊,還是有道理的)
-
原lab3中的ID_stage.v中的ds_to_es_bus是136bits,但是在EXE階段還存在少量的譯碼,我認為譯碼這種東西應當在ID階段全部完成,不應當在EXE階段還進行譯碼。
-
譯碼邏輯我寫的是:assign {ins_op, ins_rs, ins_rt, ins_rd, ins_sa, ins_fun} = ins;
原書代碼給的是:
assign ins_op = ins[31:26];
assign ins_rs = ins[25:21];
assign ins_rt = ins[20:16];
assign ins_rd = ins[15:11];
assign ins_sa = ins[10:6] ;
assign ins_fun = ins[5:0] ;
assign ins_index = ins[25:0] ;
assign ins_imm = ins[15:0] ;
感覺還是書上寫的比較易讀,隧寫成書上的這種形式
- 實例化,我寫的是:
decoder_6_64 U_decoder_6_64(
.din ( ins ),
.dout( ins_ext )
);
原書上寫的是:
decoder_6_64 U_decoder_6_64(.din ( ins ),.dout( ins_ext ) );
感覺還是差不多,我還是按我自己的寫
-
在譯碼過程中對于5bits轉32bits,和6bits轉64bits。本人可以理解opcode和function需要轉換成64bits、32bits,但是不明白rs、rt、rd、sa為何還需要轉換。我現(xiàn)在是懷疑,后續(xù)指令會擴展,然后譯碼的時候?qū)t、rd、sa也加進去,可以確保指令譯碼的唯一性。
-
我原本想將ID階段中的譯碼中的rf_dst_addr按下圖進行Coding:
但是看了原書的代碼后,認為,沒默認ins_R選項即可,只需要判斷其他寫寄存器地址,遂改為如下:
assign rf_dst_addr = ins_jal ? 5’d31 : ins_I ? ins_rt : ins_rd ;
同理,也對data_2的生成進行了類似的修改。
- 原書源碼中每個階段的pc值叫作fs_pc、ds_pc等,而我寫的代碼中都叫作pc,導致使用vivado調(diào)試時,都叫作pc不好定位
- 在跑通了全程后(共兩周,包括Debug兩天),感覺自己的Coding水平還是不如原書,而且控制信號和數(shù)據(jù)通路結構層次不好,級與級之間的bus編碼(信號的放置位置等)也不夠完美,數(shù)據(jù)的命名相同不利于debug(比如都叫pc,分不清是if的pc,還是exe的pc)。數(shù)據(jù)的耦合嚴重,尤其是ID與EXE階段,兩個階段的信號耦合過于嚴重。除此之外還有資源上的復用也有所欠缺。還有一點就是出現(xiàn)了許多Coding的問題,比如wire信號忘記聲明就使用,bne卻使用了beq的信號。最后一點就是9.14節(jié)的sw/lw報錯問題,這個bug,本人解決了一個晚上加一個上午,因為在設計階段,是按照CSDN上的一篇博客上給出的MIPS指令設計的,所以一直沒意識到博客本身就有問題,這種先入為主的指令加上本人設計的代碼結構在sw/lw指令上耦合嚴重,導致后來閱讀龍芯給的PDF時也沒意識的問題,最后閱讀了lab3原書上的源碼才發(fā)現(xiàn)問題。