手寫一個簡易的多周期 MIPS CPU

来源:https://www.cnblogs.com/hez2010/archive/2020/05/15/12897743.html
-Advertisement-
Play Games

一點前言 多周期 CPU 相比單周期 CPU 以及流水線的實現來說其實寫起來要麻煩那麼一些,但是相對於流水線以及單周期 CPU 而言,多周期 CPU 除了能提升主頻之外似乎並沒有什麼卵用。不過我的課題是多周期 CPU 那麼就開始吧。 多周期 CPU 不同於單周期 CPU,多周期 CPU 指的是將整個 ...


一點前言

多周期 CPU 相比單周期 CPU 以及流水線的實現來說其實寫起來要麻煩那麼一些,但是相對於流水線以及單周期 CPU 而言,多周期 CPU 除了能提升主頻之外似乎並沒有什麼卵用。不過我的課題是多周期 CPU 那麼就開始吧。

多周期 CPU

不同於單周期 CPU,多周期 CPU 指的是將整個 CPU 的執行過程分成幾個階段,每個階段用一個時鐘去完 成,然後開始下一條指令的執行,而每種指令執行時所用的時鐘數不盡相同,這就是所謂的多周期CPU。

CPU在處理指令時,一般需要經過以下幾個階段:

(1) 取指令(IF):根據程式計數器 PC 中的指令地址,從存儲器中取出一條指令,同時,PC 根據指令字長度自動遞增產生下一條指令所需要的指令地址,但遇到“地址轉移”指令 時,則控制器把“轉移地址”送入 PC,當然得到的“地址”需要做些變換才送入 PC。

(2) 指令解碼(ID):對取指令操作中得到的指令進行分析並解碼,確定這條指令需要完成的操作,從而產生相應的操作控制信號,用於驅動執行狀態中的各種操作。

(3) 指令執行(EXE):根據指令解碼得到的操作控制信號,具體地執行指令動作,然後轉移到結果寫回狀態。

(4) 存儲器訪問(MEM):所有需要訪問存儲器的操作都將在這個步驟中執行,該步驟給出存儲器的數據地址,把數據寫入到存儲器中數據地址所指定的存儲單元或者從存儲器中得 到數據地址單元中的數據。

(5) 結果寫回(WB):指令執行的結果或者訪問存儲器中得到的數據寫回相應的目的寄存器中。

這也就意味著一條 CPU 指令最長需要 5 個時鐘周期才能執行完畢,至於具體需要多少周期則根據指令的不同而不同。

MIPS 指令集的設計為定長簡單指令集,這為 CPU 的實現帶來了極大的方便。

指令集

MIPS 指令分為三種:R、I 和 J,三種指令有不同的存儲方式:

其中,

  • op:操作碼;
  • rs:第1個源操作數寄存器,寄存器地址(編號)是00000~11111,00~1F;
  • rt:第2個源操作數寄存器,或目的操作數寄存器,寄存器地址(同上);
  • rd:目的操作數寄存器,寄存器地址(同上);
  • sa:位移量(shift amt),移位指令用於指定移多少位;
  • funct:功能碼,在寄存器類型指令中(R類型)用來指定指令的功能;
  • immediate:16位立即數,用作無符號的邏輯操作數、有符號的算術操作數、數據載入(Load)/數據保存(Store)指令的數據地址位元組偏移量和分支指令中相對程式計數器(PC)的有符號偏移量;
  • address:地址。

在執行指令的過程中,需要在不同的時鐘周期之間進行狀態轉移:

本簡易 CPU 姑且只實現以下指令:

OpCode指令功能
000000add rd, rs, rt帶符號加法運算
000001sub rd, rs, rt帶符號減法運算
000010addiu rt, rs, immediate無符號加法運算
010000and rd, rs, rt與運算
010001andi rt, rs, immediate對立即數做 0 擴展後進行與運算
010010ori rt, rs, immediate對立即數做 0 擴展後做或運算
010011xori rt, rs, immediate對立即數做 0 擴展後做異或運算
011000sll rd, rt, sa左移指令
100110slti rt, rs, immediate比較指令
100111slt rd, rs, rt比較指令
110000sw rt, immediate(rs)存數指令
110001lw rt, immediate(rs)讀數指令
110100beq rs, rt, immediate分支指令,相等時跳轉
110101bne rs, rt, immediate分支指令,不等時跳轉
110110bltz rs, immediate分支指令,小於 0 時跳轉
111000j addr跳轉指令
111001jr rs跳轉指令
111010jal addr調用子程式指令
111111halt停機指令

控制單元

一個簡易的多周期 CPU 的數據通路圖如下:

三個 D 觸發器用於保存當前狀態,是時序邏輯電路,RST用於初始化狀態“000“,另外兩個部分都是組合邏輯電路,一個用於產生 下一個階段的狀態,另一個用於產生每個階段的控制信號。從圖上可看出,下個狀態取決於 指令操作碼和當前狀態;而每個階段的控制信號取決於指令操作碼、當前狀態和反映運算結果的狀態 zero 標誌和符號 sign標誌。

其中指令和數據各存儲在不同存儲器中,即有指令存儲器和數據存儲器。訪問存儲器時,先給出記憶體地址,然後由讀或寫信號控制操作。對於寄存器組, 給出寄存器地址(編號),讀操作時不需要時鐘信號,輸出端就直接輸出相應數據;而在寫操作時,在 WE使能信號為 1時,在時鐘邊沿觸發將數據寫入寄存器。

IR 指令寄存器目的是使指令代碼保持穩定,PC 寫使能控制信號PCWre,是確保PC 適時修改,原因都是和多周期工作的CPU有關。ADR、BDR、 ALUoutDR、DBDR四個寄存器不需要寫使能信號,其作用是切分數據通路,將大組合邏輯切分為若幹個小組合邏輯,大延遲變為多個分段小延遲。

各控制信號功能如下:

控制信號名狀態 0狀態 1
RST對於PC,初始化PC為程式首地址對於PC,PC接收下一條指令地址
PCWrePC不更改,另 外,除‘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無操作寫數據存儲器
IRWreIR(指令寄存器)不更改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:指令存儲器

  • Iaddr,指令地址輸入埠
  • DataIn,存儲器數據輸入埠
  • DataOut,存儲器數據輸出埠
  • RW,指令存儲器讀寫控制信號,為0 寫,為 1讀

Data Memory:數據存儲器

  • Daddr,數據地址輸入埠
  • DataIn,存儲器數據輸入埠
  • DataOut,存儲器數據輸出埠
  • /RD,數據存儲器讀控制信號,為 0 讀
  • /WR,數據存儲器寫控制信號,為0 寫

Register File:寄存器組

  • Read Reg1,rs 寄存器地址輸入埠
  • Read Reg2,rt 寄存器地址輸入埠
  • Write Reg,將數據寫入的寄存器,其地址輸入埠(rt、rd)
  • Write Data,寫入寄存器的數據輸入埠
  • Read Data1,rs 寄存器數據輸出埠
  • Read Data2,rt 寄存器數據輸出埠
  • WE,寫使能信號,為1 時,在時鐘邊沿觸發寫入

IR: 指令寄存器,用於存放正在執行的指令代碼

ALU: 算術邏輯單元

  • result,ALU運算結果
  • zero,運算結果標誌,結果為 0,則 zero=1;否則 zero=0
  • sign,運算結果標誌,結果最高位為0,則 sign=0,正數;否則,sign=1,負數

ALU

ALU 為算術邏輯運算單元,功能如下:

ALUOp[2..0]功能功能
000Y=A+B加法運算
001Y=A-B減法運算
010Y=B<<A左移運算
011Y=A∨B或運算
100Y=A∧B與運算
101Y=(A<B) ? 1 : 0無符號比較
110Y=(((A<B)&&(A[31] == B[31])) ||
((A[31]==1&& B[31] == 0))) ? 1 : 0
帶符號比較
111Y=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

控制信號

不同控制信號根據不同的操作碼得到,因此可以列出對於不同操作碼的各控制信號的真值表:

OpPCWreALUSrcAALUSrcBDBDataSrcRegWreWrRegDSrcInsMemRWmRDmWRIRWreExtSelPCSrcRegDstALUOp
add0000111XX1X0010000
sub0000111XX1X0010001
addiu0010111XX110001000
and0000111XX1X0010100
andi0010111XX100001100
ori0010111XX100001011
xori0010111XX100001111
sll0100111XX1X0010010
slti0010111XX110001110
slt0000111XX1X0010110
sw001X0X1X11100XX000
lw00111111X110001000
beq000X0X1XX1100(Zero=0) 01(Zero=1)XX001
bne000X0X1XX1100(Zero=1) 01(Zero=0)XX001
bltz000X0X1XX1100(Sign=0) 01(Sign=1)XX001
j0XXX0X1XX1X11XXXXX
jr0XXX0X1XX1X10XXXXX
jal0XXX101XX1X1100XXX
halt1XXX0X1XX1XXXXXXXX

控制信號不僅僅取決於操作碼,還取決於當前的狀態。各控制信號實現如下:

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

有了以上各個模塊,一個簡單的 CPU 基本就完成了,最後再將他們串起來即可。

完結撒花。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 公司自動開始用釘釘後,企業的相關信息化軟體開始使用釘釘上的應用程式。與銷售公司相關的就是CRM系統。 CRM系統中客戶是私人的,如果想多個人同時負責,需要添加客戶負責人。由於公司的特殊性質,客戶特別多,經常會有人要求增加客戶負責人。每天都有幾個小時在做這樣的工作。釘釘消息、添加負責人,太繁瑣了。 學 ...
  • 互聯網時代,軟體開發所使用的的模式也在逐漸變化。 如今,各行各業都追求信息化,努力跟上時代的步伐,可是這天天跟電腦、跟信息化打交道的軟體業卻其實跟互聯網沒半點關係。因為,互聯網是以人為中心,走的是高流量高流水的模式;而軟體業做的是產品,一個項目即是一個產品,產品之間無法複製通用。 有人說,不一直都 ...
  • 0.前言 上一章簡單介紹了一下ORM框架,並手寫了一個類似ORM的工具類。這一章將介紹一個在C 世界里大名鼎鼎的ORM框架——Entity Framework的Core版。 Entity Framework 非Core版目前已經更新到了6代,這是一款經過檢驗的ORM框架。在這裡簡單介紹一下Entit ...
  • 淺析微軟的網關項目 Intro 最近微軟新開了一個項目 "ReverseProxy" ,也叫做 YARP(A Reverse Proxy) 官方介紹如下: YARP is a reverse proxy toolkit for building fast proxy servers in .NET ...
  • C#實現FTP傳送文件 簡介: 接上文實現對FTP的傳送文件,此文和上文可以說是如出一轍,不過此文是通過cmd進行建立連接的,建立連接後也是通過以下幾個步驟實現操作。建立文件的層級結構如上文,這裡就不啰嗦了。C#實現FTP上傳資料 1.主方法進行調用: this.ftpOperation.Uploa ...
  • 一:背景 1. 講故事 下決心做好自媒體到現在有一個月了,關註我的兄弟應該知道我產出了不少文章,號里的粉絲也多起來了,我也盡最大努力做到有問必回,現在是基礎的、高深的問題都接踵而來,可我也只是一隻小菜鳥,想飛也飛不動了(┬_┬),昨天號里有位朋友被面試官問到可空類型的原理,回答的不好,面試官也是,面 ...
  • Magicodes.IE Csv導入導出 說明 本章主要說明如何使用Magicodes.IE.Csv進行Csv導入導出. 主要步驟 1.安裝包Magicodes.IE.Csv 2.使用Magicodes.IE.Csv導出Csv 通過如下代碼片段我們將導出的內容通過相應的特性做出相應的處理. Expo ...
  • exec函數族 fork()函數創建子進程後,子進程往往要調用一種e x e c函數以執行另一個程式。當進程調用一種exec函數時,該進程完全由新程式代換,而新程式則從其 ma i n函數開始執行。 因為調用exec並不創建新進程,所以前後的進程ID並未改變。exec只是用另一個新程式替換了當前進程 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 插件化的需求主要源於對軟體架構靈活性的追求,特別是在開發大型、複雜或需要不斷更新的軟體系統時,插件化可以提高軟體系統的可擴展性、可定製性、隔離性、安全性、可維護性、模塊化、易於升級和更新以及支持第三方開發等方面的能力,從而滿足不斷變化的業務需求和技術挑戰。 一、插件化探索 在WPF中我們想要開 ...
  • 歡迎ReaLTaiizor是一個用戶友好的、以設計為中心的.NET WinForms項目控制項庫,包含廣泛的組件。您可以使用不同的主題選項對項目進行個性化設置,並自定義用戶控制項,以使您的應用程式更加專業。 項目地址:https://github.com/Taiizor/ReaLTaiizor 步驟1: ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • Channel 是乾什麼的 The System.Threading.Channels namespace provides a set of synchronization data structures for passing data between producers and consume ...
  • efcore如何優雅的實現按年分庫按月分表 介紹 本文ShardinfCore版本 本期主角: ShardingCore 一款ef-core下高性能、輕量級針對分表分庫讀寫分離的解決方案,具有零依賴、零學習成本、零業務代碼入侵適配 距離上次發文.net相關的已經有很久了,期間一直在從事java相關的 ...
  • 前言 Spacesniffer 是一個免費的文件掃描工具,通過使用樹狀圖可視化佈局,可以立即瞭解大文件夾的位置,幫助用戶處理找到這些文件夾 當前系統C盤空間 清理後系統C盤空間 下載 Spacesniffer 下載地址:https://spacesniffer.en.softonic.com/dow ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • 一、ReZero簡介 ReZero是一款.NET中間件 : 全網唯一開源界面操作就能生成API , 可以集成到任何.NET6+ API項目,無破壞性,也可讓非.NET用戶使用exe文件 免費開源:MIT最寬鬆協議 , 一直從事開源事業十年,一直堅持開源 1.1 純ReZero開發 適合.Net Co ...
  • 一:背景 1. 講故事 停了一個月沒有更新文章了,主要是忙於寫 C#內功修煉系列的PPT,現在基本上接近尾聲,可以回頭繼續更新這段時間分析dump的一些事故報告,有朋友微信上找到我,說他們的系統出現了大量的http超時,程式不響應處理了,讓我幫忙看下怎麼回事,dump也抓到了。 二:WinDbg分析 ...
  • 開始做項目管理了(本人3年java,來到這邊之後真沒想到...),天天開會溝通整理需求,他們講話的時候忙裡偷閑整理一下常用的方法,其實語言還是有共通性的,基本上看到方法名就大概能猜出來用法。出去打水的時候看到外面太陽好好,真想在外面坐著曬太陽,回來的時候好兄弟三年前送給我的鍵盤D鍵不靈了,在打"等待 ...