多週期 CPU 相比單週期 CPU 以及流水線的實現來講其實寫起來要麻煩那麼一些,可是相對於流水線以及單週期 CPU 而言,多週期 CPU 除了能提高主頻以外彷佛並無什麼卵用。不過個人課題是多週期 CPU 那麼就開始吧。設計
不一樣於單週期 CPU,多週期 CPU 指的是將整個 CPU 的執行過程分紅幾個階段,每一個階段用一個時鐘去完 成,而後開始下一條指令的執行,而每種指令執行時所用的時鐘數不盡相同,這就是所謂的多週期CPU。code
CPU在處理指令時,通常須要通過如下幾個階段:blog
(1) 取指令(IF):根據程序計數器 PC 中的指令地址,從存儲器中取出一條指令,同時,PC 根據指令字長度自動遞增產生下一條指令所須要的指令地址,但遇到「地址轉移」指令 時,則控制器把「轉移地址」送入 PC,固然獲得的「地址」須要作些變換才送入 PC。ip
(2) 指令譯碼(ID):對取指令操做中獲得的指令進行分析並譯碼,肯定這條指令須要完成的操做,從而產生相應的操做控制信號,用於驅動執行狀態中的各類操做。內存
(3) 指令執行(EXE):根據指令譯碼獲得的操做控制信號,具體地執行指令動做,而後轉移到結果寫回狀態。input
(4) 存儲器訪問(MEM):全部須要訪問存儲器的操做都將在這個步驟中執行,該步驟給出存儲器的數據地址,把數據寫入到存儲器中數據地址所指定的存儲單元或者從存儲器中得 到數據地址單元中的數據。it
(5) 結果寫回(WB):指令執行的結果或者訪問存儲器中獲得的數據寫回相應的目的寄存器中。io
這也就意味着一條 CPU 指令最長鬚要 5 個時鐘週期才能執行完畢,至於具體須要多少週期則根據指令的不一樣而不一樣。table
MIPS 指令集的設計爲定長簡單指令集,這爲 CPU 的實現帶來了極大的方便。class
MIPS 指令分爲三種:R、I 和 J,三種指令有不一樣的存儲方式:
其中,
在執行指令的過程當中,須要在不一樣的時鐘週期之間進行狀態轉移:
本簡易 CPU 姑且只實現如下指令:
OpCode | 指令 | 功能 |
---|---|---|
000000 | add rd, rs, rt | 帶符號加法運算 |
000001 | sub rd, rs, rt | 帶符號減法運算 |
000010 | addiu rt, rs, immediate | 無符號加法運算 |
010000 | and rd, rs, rt | 與運算 |
010001 | andi rt, rs, immediate | 對當即數作 0 擴展後進行與運算 |
010010 | ori rt, rs, immediate | 對當即數作 0 擴展後作或運算 |
010011 | xori rt, rs, immediate | 對當即數作 0 擴展後作異或運算 |
011000 | sll rd, rt, sa | 左移指令 |
100110 | slti rt, rs, immediate | 比較指令 |
100111 | slt rd, rs, rt | 比較指令 |
110000 | sw rt, immediate(rs) | 存數指令 |
110001 | lw rt, immediate(rs) | 讀數指令 |
110100 | beq rs, rt, immediate | 分支指令,相等時跳轉 |
110101 | bne rs, rt, immediate | 分支指令,不等時跳轉 |
110110 | bltz rs, immediate | 分支指令,小於 0 時跳轉 |
111000 | j addr | 跳轉指令 |
111001 | jr rs | 跳轉指令 |
111010 | jal addr | 調用子程序指令 |
111111 | halt | 停機指令 |
一個簡易的多週期 CPU 的數據通路圖以下:
三個 D 觸發器用於保存當前狀態,是時序邏輯電路,RST用於初始化狀態「000「,另外兩個部分都是組合邏輯電路,一個用於產生 下一個階段的狀態,另外一個用於產生每一個階段的控制信號。從圖上可看出,下個狀態取決於 指令操做碼和當前狀態;而每一個階段的控制信號取決於指令操做碼、當前狀態和反映運算結果的狀態 zero 標誌和符號 sign標誌。
其中指令和數據各存儲在不一樣存儲器中,即有指令存儲器和數據存儲器。訪問存儲器時,先給出內存地址,而後由讀或寫信號控制操做。對於寄存器組, 給出寄存器地址(編號),讀操做時不須要時鐘信號,輸出端就直接輸出相應數據;而在寫操做時,在 WE使能信號爲 1時,在時鐘邊沿觸發將數據寫入寄存器。
IR 指令寄存器目的是使指令代碼保持穩定,PC 寫使能控制信號PCWre,是確保PC 適時修改,緣由都是和多週期工做的CPU有關。ADR、BDR、 ALUoutDR、DBDR四個寄存器不須要寫使能信號,其做用是切分數據通路,將大組合邏輯切分爲若干個小組合邏輯,大延遲變爲多個分段小延遲。
各控制信號功能以下:
控制信號名 | 狀態 0 | 狀態 1 |
---|---|---|
RST | 對於PC,初始化PC爲程序首地址 | 對於PC,PC接收下一條指令地址 |
PCWre | PC不更改,另 外,除‘000’狀態以外,其他狀態慎改PC的值。 | PC更改,另外,在‘000’狀態時,修改PC的值合適。 |
ALUSrcA | 來自寄存器堆 data1 輸出 | 來自移位數sa,同時,進行(zeroextend)sa,即 {{27{1'b0},sa} |
ALUSrcB | 來自寄存器堆 data2 輸出 | 來自 sign或 zero 擴展的當即數 |
DBDataSrc | 來自ALU運算結果的輸出 | 來自數據存儲器(Data MEM)的輸出 |
RegWre | 無寫寄存器組寄存器 | 寄存器組寄存器寫使能 |
WrRegDSrc | 寫入寄存器組寄存器的數據來自 PC+4(PC4) | 寫入寄存器組寄存器的數據來自ALU 運算結果或存儲器讀出的數據 |
InsMemRW | 寫指令存儲器 | 讀指令存儲器(Ins. Data) |
mRD | 存儲器輸出高阻態 | 讀數據存儲器 |
mWR | 無操做 | 寫數據存儲器 |
IRWre | IR(指令寄存器)不更改 | IR 寄存器寫使能。向指令存儲器發出讀指令代碼後,這個信號也接着發出,在時鐘上升沿,IR 接收從指令存儲器送來的指令代碼。 |
ExtSel | 零擴展 | 符號擴展 |
PCSrc[1..0] | 00:PC<-PC+4 01:PC<-PC+4+((sign-extend)immediate<<2) 10:PC<-rs 11:PC<-{PC[31:28], addr[27:2],2'b00} |
RegDst[1..0] | 寫寄存器組寄存器的地址,來自: 00:0x1F($31) 01:rt 字段 10:rd 字段 11:未用 |
ALUOp[2..0] | ALU 8種運算功能選擇(000-111) |
Instruction Memory:指令存儲器
Data Memory:數據存儲器
Register File:寄存器組
IR: 指令寄存器,用於存放正在執行的指令代碼
ALU: 算術邏輯單元
ALU 爲算術邏輯運算單元,功能以下:
ALUOp[2..0] | 功能 | 功能 |
---|---|---|
000 | Y=A+B | 加法運算 |
001 | Y=A-B | 減法運算 |
010 | Y=B<<A | 左移運算 |
011 | Y=A∨B | 或運算 |
100 | Y=A∧B | 與運算 |
101 | Y=(A<B) ? 1 : 0 | 無符號比較 |
110 | Y=(((A<B)&&(A[31] == B[31])) || ((A[31]==1&& B[31] == 0))) ? 1 : 0 |
帶符號比較 |
111 | Y=A⊕B | 異或 |
爲了更加明晰程序代碼,並避免因二進制代碼書寫錯誤致使的問題,對狀態碼、操 做碼等作出以下定義:
`define ALU_OP_ADD 3'b000 `define ALU_OP_SUB 3'b001 `define ALU_OP_SLL 3'b010 `define ALU_OP_OR 3'b011 `define ALU_OP_AND 3'b100 `define ALU_OP_LT 3'b101 `define ALU_OP_SLT 3'b110 `define ALU_OP_XOR 3'b111 `define OP_ADD 6'b000000 `define OP_SUB 6'b000001 `define OP_ADDIU 6'b000010 `define OP_AND 6'b010000 `define OP_ANDI 6'b010001 `define OP_ORI 6'b010010 `define OP_XORI 6'b010011 `define OP_SLL 6'b011000 `define OP_SLTI 6'b100110 `define OP_SLT 6'b100111 `define OP_SW 6'b110000 `define OP_LW 6'b110001 `define OP_BEQ 6'b110100 `define OP_BNE 6'b110101 `define OP_BLTZ 6'b110110 `define OP_J 6'b111000 `define OP_JR 6'b111001 `define OP_JAL 6'b111010 `define OP_HALT 6'b111111 `define PC_NEXT 2'b00 `define PC_REL_JUMP 2'b01 `define PC_REG_JUMP 2'b10 `define PC_ABS_JUMP 2'b11 `define STATE_IF 3'b000 `define STATE_ID 3'b001 `define STATE_EXE_AL 3'b110 `define STATE_EXE_BR 3'b101 `define STATE_EXE_LS 3'b010 `define STATE_MEM 3'b011 `define STATE_WB_AL 3'b111 `define STATE_WB_LD 3'b100
always @(posedge CLK or negedge RST) begin if (!RST) State <= `STATE_IF; else begin case (State) `STATE_IF: State <= `STATE_ID; `STATE_ID: begin case (OpCode) `OP_ADD, `OP_SUB, `OP_ADDIU, `OP_AND, `OP_ANDI, `OP_ORI, `OP_XORI, `OP_SLL, `OP_SLTI, `OP_SLT: State <= `STATE_EXE_AL; `OP_BNE, `OP_BEQ, `OP_BLTZ: State <= `STATE_EXE_BR; `OP_SW, `OP_LW: State <= `STATE_EXE_LS; `OP_J, `OP_JAL, `OP_JR, `OP_HALT: State <= `STATE_IF; default: State <= `STATE_EXE_AL; endcase end `STATE_EXE_AL: State <= `STATE_WB_AL; `STATE_EXE_BR: State <= `STATE_IF; `STATE_EXE_LS: State <= `STATE_MEM; `STATE_WB_AL: State <= `STATE_IF; `STATE_MEM: begin case (OpCode) `OP_SW: State <= `STATE_IF; `OP_LW: State <= `STATE_WB_LD; endcase end `STATE_WB_LD: State <= `STATE_IF; default: State <= `STATE_IF; endcase end end
不一樣控制信號根據不一樣的操做碼獲得,所以能夠列出對於不一樣操做碼的各控制信號的真值表:
Op | PCWre | ALUSrcA | ALUSrcB | DBDataSrc | RegWre | WrRegDSrc | InsMemRW | mRD | mWR | IRWre | ExtSel | PCSrc | RegDst | ALUOp |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
add | 0 | 0 | 0 | 0 | 1 | 1 | 1 | X | X | 1 | X | 00 | 10 | 000 |
sub | 0 | 0 | 0 | 0 | 1 | 1 | 1 | X | X | 1 | X | 00 | 10 | 001 |
addiu | 0 | 0 | 1 | 0 | 1 | 1 | 1 | X | X | 1 | 1 | 00 | 01 | 000 |
and | 0 | 0 | 0 | 0 | 1 | 1 | 1 | X | X | 1 | X | 00 | 10 | 100 |
andi | 0 | 0 | 1 | 0 | 1 | 1 | 1 | X | X | 1 | 0 | 00 | 01 | 100 |
ori | 0 | 0 | 1 | 0 | 1 | 1 | 1 | X | X | 1 | 0 | 00 | 01 | 011 |
xori | 0 | 0 | 1 | 0 | 1 | 1 | 1 | X | X | 1 | 0 | 00 | 01 | 111 |
sll | 0 | 1 | 0 | 0 | 1 | 1 | 1 | X | X | 1 | X | 00 | 10 | 010 |
slti | 0 | 0 | 1 | 0 | 1 | 1 | 1 | X | X | 1 | 1 | 00 | 01 | 110 |
slt | 0 | 0 | 0 | 0 | 1 | 1 | 1 | X | X | 1 | X | 00 | 10 | 110 |
sw | 0 | 0 | 1 | X | 0 | X | 1 | X | 1 | 1 | 1 | 00 | XX | 000 |
lw | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | X | 1 | 1 | 00 | 01 | 000 |
beq | 0 | 0 | 0 | X | 0 | X | 1 | X | X | 1 | 1 | 00(Zero=0) 01(Zero=1) | XX | 001 |
bne | 0 | 0 | 0 | X | 0 | X | 1 | X | X | 1 | 1 | 00(Zero=1) 01(Zero=0) | XX | 001 |
bltz | 0 | 0 | 0 | X | 0 | X | 1 | X | X | 1 | 1 | 00(Sign=0) 01(Sign=1) | XX | 001 |
j | 0 | X | X | X | 0 | X | 1 | X | X | 1 | X | 11 | XX | XXX |
jr | 0 | X | X | X | 0 | X | 1 | X | X | 1 | X | 10 | XX | XXX |
jal | 0 | X | X | X | 1 | 0 | 1 | X | X | 1 | X | 11 | 00 | XXX |
halt | 1 | X | X | X | 0 | X | 1 | X | X | 1 | X | XX | XX | XXX |
控制信號不只僅取決於操做碼,還取決於當前的狀態。各控制信號實現以下:
ALUSrcA:EXE 階段 LS、SLL
ALUSrcA = ((State == `STATE_EXE_AL || State == `STATE_EXE_BR || State == `STATE_EXE_LS) && OpCode == `OP_SLL) ? 1 : 0;
ALUSrcB:EXE 階段 ADDIU、ANDI、ORI、XORI、SLTI、LW、SW
ALUSrcB = ((State == `STATE_EXE_AL || State == `STATE_EXE_BR || State == `STATE_EXE_LS) && (OpCode == `OP_ADDIU || OpCode == `OP_ANDI || OpCode == `OP_ORI || OpCode == `OP_XORI || OpCode == `OP_SLTI || OpCode == `OP_LW || OpCode == `OP_SW)) ? 1 : 0;
RegWre:ID 階段 JAL,或 WB 階段 LD
RegWre = ((State == `STATE_ID && OpCode == `OP_JAL) || (State == `STATE_WB_AL || State == `STATE_WB_LD)) ? 1 : 0;
WrRegDSrc:ID 階段 JAL
WrRegDSrc = (State == `STATE_ID && OpCode == `OP_JAL) ? 0 : 1;
mRD:MEM 或 WB 階段 LW
mRD = ((State == `STATE_MEM || State == `STATE_WB_LD) && OpCode == `OP_LW) ? 1 : 0;
mWR:MEM 階段 SW
mWR = (State == `STATE_MEM && OpCode == `OP_SW) ? 1 : 0;
IRWre:IF 階段
IRWre = (State == `STATE_IF) ? 1 : 0;
ExtSel:EXE 階段 ANDI、ORI、XORI
ExtSel = ((State == `STATE_EXE_AL || State == `STATE_EXE_BR || State == `STATE_EXE_LS) && (OpCode == `OP_ANDI || OpCode == `OP_ORI || OpCode == `OP_XORI)) ? 0 : 1;
PCSrc:IF 或 ID 階段 JR 爲 PC_REG_JUMP,IF 或 ID 階段 J、JAL 爲 PC_ABS_JUMP,EXE 階段 BEQ、BNE、BLTZ 爲 PC_REL_JUMP,不然均爲 PC_NEXT
if ((State == `STATE_IF || State == `STATE_ID) && OpCode == `OP_JR) PCSrc = `PC_REG_JUMP; else if ((State == `STATE_IF || State == `STATE_ID) && (OpCode == `OP_J || OpCode == `OP_JAL)) PCSrc = `PC_ABS_JUMP; else if ((State == `STATE_EXE_AL || State == `STATE_EXE_BR || State == `STATE_EXE_LS) && (OpCode == `OP_BEQ && Zero) || (OpCode == `OP_BNE && !Zero) || (OpCode == `OP_BLTZ && Sign)) PCSrc = `PC_REL_JUMP; else PCSrc = `PC_NEXT;
RegDst:ID 階段 JAL 爲 b00,WB 階段 ADDIU、ANDI、ORI、XORI、SLTI、LW 爲 b01,不然均爲 b10
if (State == `STATE_ID && OpCode == `OP_JAL) RegDst = 2'b00; else if ((State == `STATE_WB_AL || State == `STATE_WB_LD) && (OpCode == `OP_ADDIU || OpCode == `OP_ANDI || OpCode == `OP_ORI || OpCode == `OP_XORI || OpCode == `OP_SLTI || OpCode == `OP_LW)) RegDst = 2'b01; else RegDst = 2'b10;
ALUOp:根據真值表便可得出
case (OpCode) `OP_ADD, `OP_ADDIU, `OP_SW, `OP_LW: ALUOp = `ALU_OP_ADD; `OP_SUB, `OP_BEQ, `OP_BNE, `OP_BLTZ: ALUOp = `ALU_OP_SUB; `OP_SLL: ALUOp = `ALU_OP_SLL; `OP_ORI: ALUOp = `ALU_OP_OR; `OP_AND, `OP_ANDI: ALUOp = `ALU_OP_AND; `OP_SLTI, `OP_SLT: ALUOp = `ALU_OP_SLT; `OP_XORI: ALUOp = `ALU_OP_XOR; endcase
PCWre:ID 階段 J、JAL、JR,或 EXE 階段 BEQ、BNE、BLTZ,或 MEM 階段 SW,或 WB 階段。另外,爲保證在每條指令最初階段的時鐘上升沿 PC 發生改變,須要在上一條指令的最後一個降低沿將 PCWre 設置爲 1,這樣才能保證 PC 在每條指令最開始的時鐘上升沿改變。
always @(negedge CLK) begin case (State) `STATE_ID: begin if (OpCode == `OP_J || OpCode == `OP_JAL || OpCode == `OP_JR) PCWre <= 1; end `STATE_EXE_AL, `STATE_EXE_BR, `STATE_EXE_LS: begin if (OpCode == `OP_BEQ || OpCode == `OP_BNE || OpCode == `OP_BLTZ) PCWre <= 1; end `STATE_MEM: begin if (OpCode == `OP_SW) PCWre <= 1; end `STATE_WB_AL, `STATE_WB_LD: PCWre <= 1; default: PCWre <= 0; endcase end
該模塊是一個32位的ALU單元,會根據控制信號對輸入的操做數進行不一樣的運算,例如加、減、與、或等。
module ALU( input [2:0] ALUOp, input [31:0] A, input [31:0] B, output Sign, output Zero, output reg [31:0] Result ); always @(*) begin case (ALUOp) `ALU_OP_ADD: Result = (A + B); `ALU_OP_SUB: Result = (A - B); `ALU_OP_SLL: Result = (B << A); `ALU_OP_OR: Result = (A | B); `ALU_OP_AND: Result = (A & B); `ALU_OP_LT: Result = (A < B) ? 1 : 0; `ALU_OP_SLT: Result = (((A < B) && (A[31] == B[31])) || ((A[31] && !B[31]))) ? 1 : 0; `ALU_OP_XOR: Result = (A ^ B); endcase $display("[ALU] calculated result [%h] from a = [%h] aluOpCode = [%b] b = [%h]", Result, A, ALUOp, B); end assign Zero = (Result == 0) ? 1 : 0; assign Sign = Result[31]; endmodule
該模塊爲一個32位而擁有32個寄存的寄存器組。寄存器組接受 InstructionMemory 的輸入,輸出對應寄存器的數據,從而實現讀取寄存器裏的數據的功能。
module RegisterFile( input CLK, input RST, input WE, input [4:0] ReadReg1, input [4:0] ReadReg2, input [4:0] WriteReg, input [31:0] WriteData, output [31:0] ReadData1, output [31:0] ReadData2 ); reg [31:0] register[1:31]; integer i; assign ReadData1 = ReadReg1 == 0 ? 0 : register[ReadReg1]; assign ReadData2 = ReadReg2 == 0 ? 0 : register[ReadReg2]; always @(negedge CLK or negedge RST) begin if (!RST) begin for (i = 1; i < 32; i = i + 1) begin register[i] = 0; end end else if (WE && WriteReg) begin register[WriteReg] <= WriteData; $display("[RegisterFile] wrote data [%h] into reg $[%d]", WriteData, WriteReg); end end endmodule
該組件有兩個功能:符號擴展和零擴展,輸入的擴展方法和待擴展的數據,輸出擴展後的數據。
module SignZeroExtend( input ExtSel, // 0 - 0 extend, 1 - sign extend input [15:0] Immediate, output [31:0] DataOut ); assign DataOut[15:0] = Immediate[15:0]; assign DataOut[31:16] = (ExtSel && Immediate[15]) ? 16'hFFFF : 16'h0000; endmodule
把指令集以二進制的形式寫成一個文件,而後在指令存儲器中讀進來,以讀文件的方式把指令存儲到內存中,實現指令的讀取。
module InstructionMemory( input RW, input [31:0] IAddr, output reg [31:0] DataOut ); reg [7:0] memory[0:95]; initial begin $readmemb(`MEMORY_FILE_PATH, memory); end always @(IAddr or RW) begin if (RW) begin DataOut[31:24] = memory[IAddr]; DataOut[23:16] = memory[IAddr + 1]; DataOut[15:8] = memory[IAddr + 2]; DataOut[7:0] = memory[IAddr + 3]; $display("[InstructionMemory] Loaded instruction [%h] from address [%h]", DataOut, IAddr); end end endmodule
數據存儲單元負責存取數據,且由時鐘降低沿出發寫操做。實現爲1字節8位的大端方式存儲。
module DataMemory( input CLK, input mRD, input mWR, input [31:0] DAddr, input [31:0] DataIn, output [31:0] DataOut ); reg [7:0] memory[0:127]; assign DataOut[7:0] = mRD ? memory[DAddr + 3] : 8'bz; assign DataOut[15:8] = mRD ? memory[DAddr + 2] : 8'bz; assign DataOut[23:16] = mRD ? memory[DAddr + 1] : 8'bz; assign DataOut[31:24] = mRD ? memory[DAddr] : 8'bz; always @(negedge CLK) begin if (mWR) begin memory[DAddr] <= DataIn[31:24]; memory[DAddr + 1] <= DataIn[23:16]; memory[DAddr + 2] <= DataIn[15:8]; memory[DAddr + 3] <= DataIn[7:0]; $display("[DataMemory] saved data [%h] into address [%h]", DataIn, DAddr); end end endmodule
在時鐘上升沿處給出下條指令的地址,或在重置信號降低沿處將PC歸零。
PC的下一條指令多是當前 PC+4,也多是跳轉指令地址,還有可能由於停機而不變。 所以還須要設計一個選擇器來選擇下一條指令地址的計算方式,爲此建立了 JumpPCHelper用於計算 j 指令的下一條 PC 地址,和 NextPCHelper 用於根據指令選擇不一樣的計算方式。
module PC( input CLK, input RST, input PCWre, input [31:0] PCAddr, output reg [31:0] NextPCAddr ); initial NextPCAddr = 0; always @(posedge CLK or negedge RST) begin if (!RST) NextPCAddr <= 0; else if (PCWre || !PCAddr) NextPCAddr <= PCAddr; end endmodule module JumpPCHelper( input [31:0] PC, input [25:0] NextPCAddr, output reg [31:0] JumpPC); wire [27:0] tmp; assign tmp = NextPCAddr << 2; // address * 4 always @(*) begin JumpPC[31:28] = PC[31:28]; JumpPC[27:2] = tmp[27:2]; JumpPC[1:0] = 0; end endmodule module NextPCHelper( input RST, input [1:0] PCSrc, input [31:0] PC, input [31:0] Immediate, input [31:0] RegPC, input [31:0] JumpPC, output reg [31:0] NextPC); always @(RST or PCSrc or PC or Immediate or RegPC or JumpPC) begin if (!RST) NextPC = PC + 4; else begin case (PCSrc) `PC_NEXT: NextPC = PC + 4; `PC_REL_JUMP: NextPC = PC + 4 + (Immediate << 2); `PC_REG_JUMP: NextPC = RegPC; `PC_ABS_JUMP: NextPC = JumpPC; default: NextPC = PC + 4; endcase end end endmodule
數據選擇,用於數據存儲單元以後的選擇,這裏須要二選一和三選一數據選擇器。
module Selector1In2#( parameter WIDTH = 5 )( input Sel, input [WIDTH-1:0] A, input [WIDTH-1:0] B, output [WIDTH-1:0] Y); assign Y = Sel ? B : A; endmodule module Selector1In3#( parameter WIDTH = 5 )( input [1:0] Sel, input [WIDTH-1:0] A, input [WIDTH-1:0] B, input [WIDTH-1:0] C, output reg [WIDTH-1:0] Y); always @(Sel or A or B or C) begin case (Sel) 2'b00: Y <= A; 2'b01: Y <= B; 2'b10: Y <= C; default: Y <= 0; endcase end endmodule
用時鐘信號 CLK 驅動,採用邊緣觸發寫入指令二進制碼。
module IR( input CLK, input IRWre, input [31:0] DataIn, output reg [31:0] DataOut ); always @(posedge CLK) begin if (IRWre) begin DataOut <= DataIn; end end endmodule
這部分模塊用於切割數據通路。
module XDR( input CLK, input [31:0] DataIn, output reg [31:0] DataOut ); always @(negedge CLK) DataOut <= DataIn; endmodule
有了以上各個模塊,一個簡單的 CPU 基本就完成了,最後再將他們串起來便可。
完結撒花。