手写一个简易的多周期MIPSCPU

编程

一点前言

多周期 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状态 1RST对于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

控制信号

不同控制信号根据不同的操作码得到,因此可以列出对于不同操作码的各控制信号的真值表:

OpPCWreALUSrcAALUSrcBDBDataSrcRegWreWrRegDSrcInsMemRWmRDmWRIRWreExtSelPCSrcRegDstALUOpadd0000111XX1X0010000sub0000111XX1X0010001addiu0010111XX110001000and0000111XX1X0010100andi0010111XX100001100ori0010111XX100001011xori0010111XX100001111sll0100111XX1X0010010slti0010111XX110001110slt0000111XX1X0010110sw001X0X1X11100XX000lw00111111X110001000beq000X0X1XX1100(Zero=0) 01(Zero=1)XX001bne000X0X1XX1100(Zero=1) 01(Zero=0)XX001bltz000X0X1XX1100(Sign=0) 01(Sign=1)XX001j0XXX0X1XX1X11XXXXXjr0XXX0X1XX1X10XXXXXjal0XXX101XX1X1100XXXhalt1XXX0X1XX1XXXXXXXX

控制信号不仅仅取决于操作码,还取决于当前的状态。各控制信号实现如下:

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 基本就完成了,最后再将他们串起来即可。

完结撒花。

以上是 手写一个简易的多周期MIPSCPU 的全部内容, 来源链接: utcz.com/z/516468.html

回到顶部