直接上代碼
SCPU
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
PC
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
ALU
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
DataMemory
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
Sign_Zero_Extend
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
Mux_TwoToOneReg
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
Mux_TwoToOne_ForInputA
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
Mux_TwoToOne
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
RegisterFile
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
ControlUnit
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
InstructionMemory
這裏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
SCPU_sim
仿真模塊
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