久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

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

 丹楓無跡 2022-06-28 發(fā)布于北京

一點前言

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

多周期 CPU

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

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

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

(2) 指令譯碼(ID):對取指令操作中得到的指令進行分析并譯碼,,確定這條指令需要完成的操作,,從而產(chǎn)生相應的操作控制信號,用于驅(qū)動執(zhí)行狀態(tài)中的各種操作,。

(3) 指令執(zhí)行(EXE):根據(jù)指令譯碼得到的操作控制信號,,具體地執(zhí)行指令動作,然后轉(zhuǎn)移到結(jié)果寫回狀態(tài),。

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

(5) 結(jié)果寫回(WB):指令執(zhí)行的結(jié)果或者訪問存儲器中得到的數(shù)據(jù)寫回相應的目的寄存器中,。

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

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

指令集

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

其中,

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

在執(zhí)行指令的過程中,,需要在不同的時鐘周期之間進行狀態(tài)轉(zhuǎn)移:

本簡易 CPU 姑且只實現(xiàn)以下指令:

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

控制單元

一個簡易的多周期 CPU 的數(shù)據(jù)通路圖如下:

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

其中指令和數(shù)據(jù)各存儲在不同存儲器中,即有指令存儲器和數(shù)據(jù)存儲器,。訪問存儲器時,,先給出內(nèi)存地址,然后由讀或?qū)懶盘柨刂撇僮?。對于寄存器組,, 給出寄存器地址(編號),讀操作時不需要時鐘信號,,輸出端就直接輸出相應數(shù)據(jù),;而在寫操作時,在 WE使能信號為 1時,,在時鐘邊沿觸發(fā)將數(shù)據(jù)寫入寄存器,。

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

各控制信號功能如下:

控制信號名狀態(tài) 0狀態(tài) 1
RST對于PC,,初始化PC為程序首地址對于PC,PC接收下一條指令地址
PCWrePC不更改,,另 外,,除'000’狀態(tài)之外,其余狀態(tài)慎改PC的值,。PC更改,,另外,,在'000’狀態(tài)時,修改PC的值合適,。
ALUSrcA來自寄存器堆 data1 輸出來自移位數(shù)sa,,同時,進行(zeroextend)sa,,即 {{27{1'b0},sa}
ALUSrcB來自寄存器堆 data2 輸出來自 sign或 zero 擴展的立即數(shù)
DBDataSrc來自ALU運算結(jié)果的輸出來自數(shù)據(jù)存儲器(Data MEM)的輸出
RegWre無寫寄存器組寄存器寄存器組寄存器寫使能
WrRegDSrc寫入寄存器組寄存器的數(shù)據(jù)來自 PC+4(PC4)寫入寄存器組寄存器的數(shù)據(jù)來自ALU 運算結(jié)果或存儲器讀出的數(shù)據(jù)
InsMemRW寫指令存儲器讀指令存儲器(Ins. Data)
mRD存儲器輸出高阻態(tài)讀數(shù)據(jù)存儲器
mWR無操作寫數(shù)據(jù)存儲器
IRWreIR(指令寄存器)不更改IR 寄存器寫使能,。向指令存儲器發(fā)出讀指令代碼后,這個信號也接著發(fā)出,,在時鐘上升沿,,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,存儲器數(shù)據(jù)輸入端口
  • DataOut,,存儲器數(shù)據(jù)輸出端口
  • RW,,指令存儲器讀寫控制信號,為0 寫,,為 1讀

Data Memory:數(shù)據(jù)存儲器

  • Daddr,,數(shù)據(jù)地址輸入端口
  • DataIn,存儲器數(shù)據(jù)輸入端口
  • DataOut,,存儲器數(shù)據(jù)輸出端口
  • /RD,,數(shù)據(jù)存儲器讀控制信號,為 0 讀
  • /WR,,數(shù)據(jù)存儲器寫控制信號,,為0 寫

Register File:寄存器組

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

IR: 指令寄存器,,用于存放正在執(zhí)行的指令代碼

ALU: 算術(shù)邏輯單元

  • result,ALU運算結(jié)果
  • zero,,運算結(jié)果標志,,結(jié)果為 0,,則 zero=1;否則 zero=0
  • sign,,運算結(jié)果標志,,結(jié)果最高位為0,則 sign=0,,正數(shù),;否則,sign=1,,負數(shù)

ALU

ALU 為算術(shù)邏輯運算單元,功能如下:

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異或

模塊設計

符號定義

為了更加明晰程序代碼,,并避免因二進制代碼書寫錯誤導致的問題,,對狀態(tài)碼、操 作碼等做出如下定義:

`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

控制單元

狀態(tài)轉(zhuǎn)移

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

控制信號

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

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

控制信號不僅僅取決于操作碼,,還取決于當前的狀態(tài)。各控制信號實現(xiàn)如下:

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:根據(jù)真值表即可得出

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 發(fā)生改變,,需要在上一條指令的最后一個下降沿將 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

邏輯算術(shù)運算單元

該模塊是一個32位的ALU單元,,會根據(jù)控制信號對輸入的操作數(shù)進行不同的運算,例如加,、減,、與、或等,。

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 的輸入,輸出對應寄存器的數(shù)據(jù),,從而實現(xiàn)讀取寄存器里的數(shù)據(jù)的功能,。

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

符號擴展單元

該組件有兩個功能:符號擴展和零擴展,輸入的擴展方法和待擴展的數(shù)據(jù),,輸出擴展后的數(shù)據(jù),。

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

指令存儲器

把指令集以二進制的形式寫成一個文件,然后在指令存儲器中讀進來,,以讀文件的方式把指令存儲到內(nèi)存中,,實現(xiàn)指令的讀取。

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

數(shù)據(jù)存儲單元

數(shù)據(jù)存儲單元負責存取數(shù)據(jù),,且由時鐘下降沿出發(fā)寫操作,。實現(xiàn)為1字節(jié)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

程序計數(shù)器

在時鐘上升沿處給出下條指令的地址,,或在重置信號下降沿處將PC歸零,。

PC的下一條指令可能是當前 PC+4,也可能是跳轉(zhuǎn)指令地址,,還有可能因為停機而不變,。 因此還需要設計一個選擇器來選擇下一條指令地址的計算方式,為此創(chuàng)建了 JumpPCHelper用于計算 j 指令的下一條 PC 地址,和 NextPCHelper 用于根據(jù)指令選擇不同的計算方式,。

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

選擇器

數(shù)據(jù)選擇,,用于數(shù)據(jù)存儲單元之后的選擇,這里需要二選一和三選一數(shù)據(jù)選擇器,。

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 驅(qū)動,,采用邊緣觸發(fā)寫入指令二進制碼。

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

數(shù)據(jù)延遲處理

這部分模塊用于切割數(shù)據(jù)通路,。

module XDR(
    input CLK,
    input [31:0] DataIn,
    output reg [31:0] DataOut
    );
    always @(negedge CLK) DataOut <= DataIn;
endmodule

CPU

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

完結(jié)撒花,。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多