直接上代码
module SCPU( CLK, Reset, CurPC, instcode );
input CLK; // 时钟信号
input Reset; // 置零信号
output [31:0] CurPC; // 当前指令的地址
output [31:0] instcode; // 指令寄存器中获取的指令码
wire ExtSel; // 位扩展信号,1为符号扩展,0为0扩展
wire PCWre; // PC工作信号,0不更改,1更改
wire InsMemRW; // 指令寄存器信号,0为写,1为读
wire RegDst; // 指令读取时判断是rt还是rd进入寄存器组的写数据端,0为rt,1为rd
wire RegWre; // 寄存器组是否需要写功能,0为无写功能,1为些功能
wire [2:0] ALUOp; // ALU8种运算功能选择
wire PCSrc; // PC正常+4还是要跳转,0为正常+4,1为跳转
wire ALUSrcA; // 寄存器组Data1的输出,0为寄存器本身输出,1为指令码的最后16位立即数
wire ALUSrcB; // 寄存器组Data2的输出,0位本身的输出,1为扩展后的立即数
wire RD; // 读数据存储器功能,0时读取
wire WR; // 写数据存储器功能,1时写
wire DBDataSrc; // 决定将什么数据传入寄存器组Write Data端,0为ALU结果,1为存储器
wire [4:0] WriteRegAddr; // 寄存器组Write Reg输入端
wire [31:0] Reg1Out; // 寄存器组rs寄存器的值
wire [31:0] Reg2Out; // 寄存器组rt寄存器的值,也是存储器的DataIn输入端
wire [31:0] WriteData; // 寄存器组Write Data输入端的值
wire [31:0] ALU_Input_A; // ALU的A输入端
wire [31:0] ALU_Input_B; // ALU的B输入端
wire [31:0] ALU_Out; // ALU的result输出,也是存储器的地址输入端
wire zero; // ALU的zero输出
wire [31:0] MemOut; // 存储器的输出
wire [31:0] Ext_Imm; // 位扩展后的立即数
// PC( CLK, Reset, PCWre, PCSrc, immediate, Address)
PC my_PC( CLK, Reset, PCWre, PCSrc, Ext_Imm, CurPC );
// ALU( Reg1, Reg2, ALUOp, result, zero )
ALU my_ALU( ALU_Input_A, ALU_Input_B, ALUOp, ALU_Out, zero );
// DataMemory( DAddr, CLK, RD, WR, DataIn, DataOut)
DataMemory my_DataMemory( ALU_Out, CLK, RD, WR, Reg2Out, MemOut );
// Sign_Zero_Extend( Imm_Number, ExtSel, Result )
Sign_Zero_Extend my_Sign_Zero_Extend( instcode[15:0], ExtSel, Ext_Imm );
// Mux_TwoToOneReg( Select, DataIn1, DataIn2, DataOut )
Mux_TwoToOneReg my_Mux_TwoToOneReg( RegDst, instcode[20:16], instcode[15:11], WriteRegAddr );
// Mux_TwoToOne( Select, DataIn1, DataIn2, DataOut )
Mux_TwoToOne_ForInputA my_Mux_TwoToOne_For_ALU_InputA( ALUSrcA, Reg1Out, instcode[10:6], ALU_Input_A );
Mux_TwoToOne my_Mux_TwoToOne_For_ALU_InputB( ALUSrcB, Reg2Out, Ext_Imm, ALU_Input_B );
Mux_TwoToOne my_Mux_TwoToOne_For_RegisterFile_WriteData( DBDataSrc, ALU_Out, MemOut, WriteData );
// RegisterFile( RegWre, CLK, Reg1, Reg2, WriteReg, WriteData, DataOut1, DataOut2 )
RegisterFile my_RegisterFile( RegWre, CLK, instcode[25:21], instcode[20:16], WriteRegAddr, WriteData, Reg1Out, Reg2Out );
// ControlUnit( ExtSel, PCWre, InsMemRW, RegDst, RegWre, ALUOp, PCSrc, ALUSrcB, RD, WR, DBDataSrc, opCode, zero )
ControlUnit my_ControlUnit( ExtSel, PCWre, InsMemRW, RegDst, RegWre, ALUOp, PCSrc, ALUSrcA, ALUSrcB, RD, WR, DBDataSrc, instcode[31:26], zero );
// InstructionMemory( CurPC, instMemRW, instcode )
InstructionMemory my_InstructionMemory( CurPC, InsMemRW, instcode );
endmodule
module PC( CLK, Reset, PCWre, PCSrc, immediate, Address);
input CLK, Reset, PCWre, PCSrc;
input [31:0] immediate;
output reg [31:0] Address;
/*initial begin
Address = 0;
end*/
always @(posedge CLK or negedge Reset)
begin
if (Reset == 0) begin
Address = 0;
end
else if (PCWre) begin
if (PCSrc)
Address = Address + 4 + immediate * 4; // 需要跳转,是按当前指令地址的下一个地址为参考来跳转的
else Address = Address + 4; // 不需要跳转,一个指令是32位,一个指令存储器存储长度是8位,所以需要4个存储单元,所以+4
end
end
endmodule
module ALU( Reg1, Reg2, ALUOp, result, zero );
input [31:0] Reg1;
input [31:0] Reg2;
input [2:0] ALUOp;
output reg [31:0] result;
output zero;
assign zero = ( result == 0 ) ? 1 : 0;
always@( ALUOp or Reg1 or Reg2 ) begin
case (ALUOp)
3'b000 : result = Reg1 + Reg2; // Y = A + B
3'b001 : result = Reg1 - Reg2; // Y = A - B
3'b010 : result = (Reg1 < Reg2) ? 1 : 0; // Y=(A < B)? 1 : 0
3'b011 : result = Reg2 << Reg1; // Y = B << A
3'b100 : result = Reg1 & Reg2; // Y = A & B
3'b101 : result = Reg1 | Reg2; // Y = A | B
3'b110 : result = Reg1 ^ Reg2; // Y = A ^ B
3'b111 : result = Reg1 ^~ Reg2; // Y = A ^~ B
endcase
end
endmodule
module DataMemory( DAddr, CLK, RD, WR, DataIn, DataOut);
input [31:0] DAddr;
input CLK;
input RD;
input WR;
input [31:0] DataIn;
output reg [31:0] DataOut;
reg [7:0] dataMemory [0:60];
always@( RD or DAddr ) begin // 读,随时的
if (RD == 0) begin
DataOut[7:0] = dataMemory[DAddr + 3];
DataOut[15:8] = dataMemory[DAddr + 2];
DataOut[23:16] = dataMemory[DAddr + 1];
DataOut[31:24] = dataMemory[DAddr];
end
end
always@( negedge CLK ) begin // 写,时钟下降沿触发
if (WR == 0) begin
dataMemory[DAddr + 3] <= DataIn[7:0];
dataMemory[DAddr + 2] <= DataIn[15:8];
dataMemory[DAddr + 1] <= DataIn[23:16];
dataMemory[DAddr] <= DataIn[31:24];
end
end
endmodule
module Sign_Zero_Extend( Imm_Number, ExtSel, Result );
input [15 :0] Imm_Number;
input ExtSel;
output reg [31:0] Result;
always@( Imm_Number or ExtSel) begin
if (ExtSel == 0 || Imm_Number[15] == 0)
Result = { 16'b0000000000000000, Imm_Number };
else
Result = { 16'b1111111111111111, Imm_Number };
end
endmodule
module Mux_TwoToOneReg( Select, DataIn1, DataIn2, DataOut );
input Select;
input [4:0] DataIn1;
input [4:0] DataIn2;
output reg [4:0] DataOut;
always@( Select or DataIn1 or DataIn2 ) begin
if ( Select == 0 )
DataOut = DataIn1;
else
DataOut = DataIn2;
end
endmodule
module Mux_TwoToOne_ForInputA( Select, DataIn1, sa, DataOut );
input Select;
input [31:0] DataIn1;
input [4:0] sa;
output reg [31:0] DataOut;
always@( Select or DataIn1 or sa ) begin
if ( Select == 0 )
DataOut = DataIn1;
else
DataOut = { 27'b000000000000000000000000000, sa }; // 对sa进行扩展
end
endmodule
module Mux_TwoToOne( Select, DataIn1, DataIn2, DataOut );
input Select;
input [31:0] DataIn1;
input [31:0] DataIn2;
output reg [31:0] DataOut;
always@( Select or DataIn1 or DataIn2 ) begin
if ( Select == 0 )
DataOut = DataIn1;
else
DataOut = DataIn2;
end
endmodule
module RegisterFile( RegWre, CLK, Reg1, Reg2, WriteReg, WriteData, DataOut1, DataOut2 );
input RegWre;
input CLK;
input [4:0] Reg1, Reg2;
input [4:0] WriteReg;
input [31:0] WriteData;
output [31:0] DataOut1, DataOut2;
reg [31:0] registers[1:31];
assign DataOut1 = ( Reg1 == 0 ) ? 0 : registers[Reg1];
assign DataOut2 = ( Reg2 == 0 ) ? 0 : registers[Reg2];
always@( negedge CLK ) begin // 写操作
if (( WriteReg != 0 ) && ( RegWre == 1 )) begin
$display("WriteData: ", WriteData, " WriteReg: ", WriteReg);
registers[WriteReg] <= WriteData;
end
end
endmodule
module ControlUnit( ExtSel, PCWre, InsMemRW, RegDst, RegWre, ALUOp, PCSrc, ALUSrcA, ALUSrcB, RD, WR, DBDataSrc, opCode, zero );
output ExtSel;
output PCWre;
output InsMemRW;
output RegDst;
output RegWre;
output [2:0] ALUOp;
output PCSrc;
output ALUSrcA;
output ALUSrcB;
output RD;
output WR;
output DBDataSrc;
input [5:0] opCode;
input zero;
assign ExtSel = (opCode == 6'b010000) ? 0 : 1;
assign PCWre = (opCode == 6'b111111) ? 0 : 1;
assign InsMemRW = 0;
assign RegDst = (opCode == 6'b000001 || opCode == 6'b010000 || opCode == 6'b100110 || opCode == 6'b100111 || opCode == 6'b110000) ? 0 : 1;
assign RegWre = (opCode == 6'b100110 || opCode == 6'b110000) ? 0 : 1;
assign PCSrc = (opCode == 6'b110000 && zero == 1) ? 1 : 0;
assign RegWre = (opCode == 6'b100110 || opCode == 6'b110000) ? 0 : 1;
assign ALUSrcA = (opCode == 6'b011000) ? 1 : 0;
assign ALUSrcB = (opCode == 6'b000000 || opCode == 6'b000010 || opCode == 6'b010001 || opCode == 6'b010010 || opCode == 6'b110000 || opCode == 6'b011000) ? 0 : 1;
assign RD = (opCode == 6'b100111) ? 0 : 1;
assign WR = (opCode == 6'b100110) ? 0 : 1;
assign DBDataSrc = (opCode == 6'b100111) ? 1 : 0;
assign ALUOp[0] = (opCode == 6'b000000 || opCode == 6'b000001 || opCode == 6'b010001 || opCode == 6'b100110 || opCode == 6'b100111) ? 0: 1;
assign ALUOp[1] = (opCode == 6'b000000 || opCode == 6'b000001 || opCode == 6'b000010 || opCode == 6'b010000 || opCode == 6'b010001 || opCode == 6'b010010 || opCode == 6'b100110 || opCode == 6'b100111 || opCode == 6'b110000) ? 0 : 1;
assign ALUOp[2] = (opCode == 6'b010000 || opCode == 6'b010001 || opCode == 6'b010010) ? 1 : 0;
endmodule
这里readmemb的地址需要修改,具体看你的指令文件存储位置
module InstructionMemory( CurPC, instMemRW, instcode );
input [31:0] CurPC;
input instMemRW;
output reg [31:0] instcode;
reg [7:0] InstMemory [255:0];
initial begin
$readmemb("H:/Single_CPU/instruction.txt", InstMemory);
end
always@(CurPC or instMemRW) begin
if (instMemRW == 0) begin
instcode = { InstMemory[CurPC], InstMemory[CurPC + 1], InstMemory[CurPC + 2], InstMemory[CurPC + 3] };
end
$display("InstMem PC", CurPC, " INST: ", instcode);
end
endmodule
仿真模块
module SCPU_sim;
reg CLK; // 时钟信号
reg Reset; // 置零信号
wire [31:0] CurPC; // 当前指令的地址
wire [31:0] instcode; // 指令寄存器中获取的指令码
SCPU my_SCPU( .CLK(CLK),
.Reset(Reset),
.CurPC(CurPC),
.instcode(instcode)
);
always #30 CLK = !CLK; // 60ns为一周期
initial begin
CLK = 1;
Reset = 0;
#90;
Reset = 1;
end
endmodule