手写一个简易的多周期 MIPS CPU (6)

该组件有两个功能:符号扩展和零扩展,输入的扩展方法和待扩展的数据,输出扩展后的数据。

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

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyjsgx.html