是的我刚刚验收完最后一个实验,所以怀着激动的心情,把当时其中一个留档的代码发出来,还算较为清晰,仅供没有思路的同学参考。造完cache,我的生活终于可以恢复正轨了,这几天折磨的寝食不安呀~~ 另外最近感悟颇多呀,美好的假期真正开始了!!
先讲一个鬼故事,我花了一天造出基本框架后跑了一下就PASS了,但是波形完全不对。。(但是感谢这个PASS给我调下去的勇气)
然后就开始修锅,修了将近10个大bug,着实的锻炼了观察波形调试的能力,咱就是说我是干了多少负负得正的事儿PASS的啊!(还是这个测试环境本身就有点问题)
学弟学妹们请慎重参考,相信你们做完非流水的这个都能做出来,另外不出意外的话,你们会遇见我们班规格严格的ywy学长(验收建议找老师哦哈哈就俩人一起笑着笑着就完事儿了),人肉查重哦~
个人认为我码风还算可以,如果我的代码有写的错误/值得改进之处,欢迎指出!!
设计图如下,WB阶段有一个MUX没画,因为没地方了(懒了)哈哈
最后版本跟我最开始写出来的框架有些许变化,如果你看到了让你不知所以然的代码,大概率是修锅的时候造的。。
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/11/08 19:26:40
// Design Name:
// Module Name: cpu
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module cpu(
input clk , // clock, 100MHz
input resetn , // active low, board switch 0 (SW0)
// debug signals
output [31:0] debug_wb_pc , // 当前正在执行指令的PC
output debug_wb_rf_wen , // 当前通用寄存器组的写使能信号
output [4 :0] debug_wb_rf_addr, // 当前通用寄存器组写回的寄存器编号
output [31:0] debug_wb_rf_wdata // 当前指令需要写回的数据
);
//IF
wire [31:0] IF_PC; //output_new_address; //PC output
wire [31:0] next_PC; //PC+4
wire [31:0] IF_NPC; //PC input以下是段寄存器输入输出
wire [31:0] IF_IR; //IR的指令
//ID
wire [31:0] ID_PC;
wire [31:0] ID_NPC;
wire [31:0] ID_A;
wire [31:0] ID_B;
wire [31:0] ID_Imm;
wire [31:0] ID_IR;
//EX
wire [31:0] EX_PC;
wire [31:0] EX_NPC;
wire [31:0] EX_A;
wire [31:0] EX_B;
wire [31:0] EX_Imm;
wire [31:0] EX_IR;
wire EX_Cond;
wire [31:0] EX_ALU_output;
wire [31:0] alu_data_1;
wire [31:0] alu_data_2;
//MEM
wire [31:0] MEM_PC;
// wire MEM_Cond;
wire [31:0] MEM_ALU_output;
wire [31:0] MEM_B;
wire [31:0] MEM_IR;
wire [31:0] MEM_LMD;
//WB
wire [31:0] WB_PC;
wire [31:0] WB_LMD;
wire [31:0] WB_ALU_output;
wire [31:0] WB_IR;
wire reg_write_enable;
wire [4:0] WB_address;//WB_mux output
wire [31:0] WB_data;//MUX3 output
assign debug_wb_pc = WB_PC;
assign debug_wb_rf_wen = reg_write_enable;
assign debug_wb_rf_addr = WB_address;
assign debug_wb_rf_wdata = WB_data;
//IF
PC pc(
.clk (clk),
.reset_n (resetn),
.new_address (IF_NPC),
.output_address (IF_PC)
);
ADD PC_plus4(
.data1 (IF_PC),
.data2 (32'h00000004),
.result (next_PC)
);
IMEM inst_mem(
.address (IF_PC),
.output_instruction(IF_IR)
);
MUX PC_mux(
.data0 (MEM_ALU_output),
.data1 (next_PC),
.select (EX_Cond),
.result (IF_NPC)
);
IF_ID_registers IF_ID_reg(
.clk (clk),
.reset_n (resetn),
.NPC_i (IF_NPC),
.IR_i (IF_IR),
.PC_i (IF_PC),
.NPC_o (ID_NPC),
.IR_o (ID_IR),
.PC_o (ID_PC)
);
//ID
Extender extender(
.opcode (ID_IR[31:26]),
.input_26bImm (ID_IR[25:0]),
.output_32bImm (ID_Imm)
);
Registers Registers_set(
.clk (clk),
.reset_n (resetn),
.write_enable (reg_write_enable),
.rs (ID_IR[25:21]),
.rt (ID_IR[20:16]),
.writeback_address(WB_address),
.writeback_data (WB_data),
.output_data_A (ID_A),
.output_data_B (ID_B)
);
ID_EX_registers ID_EX_reg(
.clk (clk),
.reset_n (resetn),
.NPC_i (ID_NPC),
.A_i (ID_A),
.B_i (ID_B),
.Imm32_i (ID_Imm),
.IR_i (ID_IR),
.PC_i (ID_PC),
.NPC_o (EX_NPC),
.A_o (EX_A),
.B_o (EX_B),
.Imm32_o (EX_Imm),
.IR_o (EX_IR),
.PC_o (EX_PC)
);
//EX
MUX1 mux1(
.opcode (EX_IR[31:26]),
.data0 (EX_NPC),
.data1 (EX_A),
.result (alu_data_1)
);
MUX2 mux2(
.opcode (EX_IR[31:26]),
.data0 (EX_B),
.data1 (EX_Imm),
.result (alu_data_2)
);
Equal equal(
.opcode (IF_IR[31:26]),
.data_A (EX_A),
.data_B (EX_B),
.equal (EX_Cond)
);
ALU alu(
.opcode (EX_IR[31:26]),
.alu_op (EX_IR[5:0]),
.input_data1 (alu_data_1),
.input_data2 (alu_data_2),
.result (EX_ALU_output)
);
EX_MEM_registers EX_MEM_reg(
.clk (clk),
.reset_n (resetn),
.ALU_output_i (EX_ALU_output),
.B_i (EX_B),
.IR_i (EX_IR),
.PC_i (EX_PC),
.ALU_output_o (MEM_ALU_output),
.B_o (MEM_B),
.IR_o (MEM_IR),
.PC_o (MEM_PC)
);
//MEM
DMEM data_mem(
.clk (clk),
.reset_n (resetn),
.opcode (MEM_IR[31:26]),
.alu_address (MEM_ALU_output),
.WD (MEM_B),
.RD (MEM_LMD)
);
MEM_WB_registers MEM_WB_reg(
.clk (clk),
.reset_n (resetn),
.LMD_i (MEM_LMD),
.ALU_output_i (MEM_ALU_output),
.IR_i (MEM_IR),
.PC_i (MEM_PC),
.LMD_o (WB_LMD),
.ALU_output_o (WB_ALU_output),
.IR_o (WB_IR),
.PC_o (WB_PC),
.write_enable (reg_write_enable)
);
//WB
Writeback_aMUX Writeback_aMUX_inst( //写回寄存器编号选择
.opcode (WB_IR[31:26]),
.rt_address (WB_IR[20:16]),
.rd_address (WB_IR[15:11]),
.writeback_address(WB_address)
);
Writeback_dMUX Writeback_dMUX_inst(
.opcode (WB_IR[31:26]),
.data0 (WB_LMD),
.data1 (WB_ALU_output),
.writeback_data (WB_data)
);
endmodule
module PC(
input clk,
input reset_n,
input [31:0] new_address, // 下一个地址
output reg [31:0] output_address // 当前PC内容
);
always@(posedge clk) begin
if(reset_n == 1'b0)
output_address <= 0;
else
output_address <= new_address;
end
endmodule
`timescale 1ns / 1ps
module ADD(
input [31:0] data1,
input [31:0] data2,
output [31:0] result
);
assign result = data1 + data2;
endmodule
//组合逻辑
module IMEM(
input [31:0] address, // 指令的地址
output [31:0] output_instruction // 取出的指令
);
reg [31:0] instruction [255:0]; // 32位*256大小的指令存储缓冲器
initial begin
// 读取文件内容并进行初始化
$readmemh("E:/FPGA_code/CPU_pipeline/CPU_pipeline.sim/sim_1/behav/xsim/base_inst_data", instruction);
end
assign output_instruction = instruction[address / 4];
endmodule
module MUX(
input [31:0] data0, //跳转地址
input [31:0] data1, //顺序执行的地址
input select,
output [31:0] result
);
assign result = (select == 1)? data0 : data1; //cond == 1则跳转
endmodule
`timescale 1ns / 1ps
module IF_ID_registers(
input clk,
input reset_n,
input [31:0] NPC_i,
input [31:0] IR_i,
input [31:0] PC_i,
output reg [31:0] NPC_o,
output reg [31:0] IR_o,
output reg [31:0] PC_o
);
always@(posedge clk)
if(reset_n == 1'b0) begin
NPC_o <= 0;
IR_o <= 0;
PC_o <= 0;
end
else begin
NPC_o <= NPC_i;
IR_o <= IR_i;
PC_o <= PC_i;
end
endmodule
`timescale 1ns / 1ps
`define JMP 6'b000010
module Extender(
input [ 5:0] opcode, // 指令的操作码
input [25:0] input_26bImm, // 待扩展的数据
output [31:0] output_32bImm // 扩展到32位后的数据
);
assign output_32bImm[31:0] = (opcode == `JMP)? { {6{input_26bImm[25]}}, input_26bImm[25:0]} // JMP将后25位符号扩展为32位
: { {16{input_26bImm[15]}}, input_26bImm[15:0]};//其他的指令如 SW/LW将后16位符号扩展为32位
endmodule
`timescale 1ns / 1ps
module Registers(
input clk,
input reset_n,
input write_enable,
input [4:0] rs, // rt的值
input [4:0] rt, // rd的值
input [4:0] writeback_address, // 要写的寄存器的地址
input [31:0] writeback_data, // 要写入的内容
output [31:0] output_data_A, // Reg[rt]
output [31:0] output_data_B // Reg[rd]
);
reg [31:0] registers [31:0]; // 32个32位寄存器
assign output_data_A = registers[rs];
assign output_data_B = registers[rt];
// 使用本地文件初始化寄存器组
initial begin
$readmemb("E:/FPGA_code/lab2/lab2.sim/sim_1/behav/xsim/reg_data.txt" , registers);
end
always@(posedge clk) begin
if(reset_n == 0) begin
//output_data_1 = 32'h00000000;
//output_data_2 = 32'h00000000;
end
else begin
if(write_enable == 1) begin
registers[writeback_address] <= writeback_data; //WB写回寄存器
end
end
end
endmodule
module ID_EX_registers(
input clk,
input reset_n,
input [31:0] NPC_i,
input [31:0] A_i,
input [31:0] B_i,
input [31:0] Imm32_i,
input [31:0] IR_i,
input [31:0] PC_i,
output reg [31:0] NPC_o,
output reg [31:0] A_o,
output reg [31:0] B_o,
output reg [31:0] Imm32_o,
output reg [31:0] IR_o,
output reg [31:0] PC_o
);
always@(posedge clk)
if(reset_n == 1'b0) begin
NPC_o <= 0;
A_o <= 0;
B_o <= 0;
Imm32_o <= 0;
IR_o <= 0;
PC_o <= 0;
end
else begin
NPC_o <= NPC_i;
A_o <= A_i;
B_o <= B_i;
Imm32_o <= Imm32_i;
IR_o <= IR_i;
PC_o <= PC_i;
end
endmodule
`timescale 1ns / 1ps
`define JMP 6'b000010
`define BEQ 6'b000100
module MUX1(
input [5:0] opcode,
input [31:0] data0,
input [31:0] data1,
output [31:0] result
);
//JMP BEQ
assign result = (opcode == `JMP || opcode == `BEQ) ? data0 : data1; //NPC:A
endmodule
`define JMP 6'b000010
`define BEQ 6'b000100
`define LW 6'b100011
`define SW 6'b101011
module MUX2(
input [5:0] opcode,
input [31:0] data0,
input [31:0] data1,
output [31:0] result
);
assign result = (opcode == `JMP || opcode == `BEQ || opcode == `LW || opcode == `SW) ? data1 : data0; //Imm:B
endmodule
`define JMP 6'b000010
`define BEQ 6'b000100
module Equal(
input [5:0] opcode,
input [31:0] data_A,
input [31:0] data_B,
output equal
);
assign equal = (opcode == `JMP || (opcode == `BEQ && data_A == data_B)) ? 1:0;
endmodule
`define ALU 6'b000000
`define BEQ 6'b000100
`define JMP 6'b000010
`define MOVZ 6'b001010
`define SW 6'b101011
`define LW 6'b100011
module ALU(
input [5:0] opcode,
input [5:0] alu_op, //执行哪种运算
input [31:0] input_data1,
input [31:0] input_data2,
output wire [31:0] result
);
wire [31:0] temp;
//若为JMP指令,将其中的26位形式地址instr_index左移2位作为低28位,放于temp
assign temp = (alu_op == 6'b110000) ? input_data2 << 2 : 32'b0; //JMP (NPC[31:28]) ## (instr_index << 2),
assign result = ({32{alu_op == 6'b101000 && opcode == `BEQ}} & ((input_data2 << 2) + input_data1)) | //BEQ:sign_extend(offset) << 2 + NPC
({32{alu_op == 6'b100000 && opcode == `ALU}} & (input_data1 + input_data2)) | //ADD
({32{alu_op == 6'b100010 && opcode == `ALU}} & (input_data1 - input_data2)) | //SUB
({32{alu_op == 6'b100100 && opcode == `ALU}} & (input_data1 & input_data2)) | //AND
({32{alu_op == 6'b100101 && opcode == `ALU}} & (input_data1 | input_data2)) | //OR
({32{alu_op == 6'b100110 && opcode == `ALU}} & (input_data1 ^ input_data2)) | //XOR
({32{alu_op == 6'b101010 && opcode == `ALU}} & (input_data1 < input_data2)? 32'h000000001 : 32'h00000000) | //SLT:[rd] <- [rs] < [rt] ? 1 : 0
({32{alu_op == 6'b001010 && opcode == `MOVZ}} & (input_data2 == 0)? input_data1 : 32'h00000000) | // MOVZ:if([rt] == 0)then[rd] ← [rs] else 不做任何
({32{alu_op == 6'b110000 && opcode == `JMP}} & {input_data1[31:26], temp[25:0]} ) | //JMP
({32{opcode == `LW}} & (input_data1 + input_data2)) | //LW
({32{opcode == `SW}} & (input_data1 + input_data2)); //SW
endmodule
module EX_MEM_registers(
input clk,
input reset_n,
// input Cond_i,
input [31:0] ALU_output_i,
input [31:0] B_i,
input [31:0] IR_i,
input [31:0] PC_i,
// output reg Cond_o,
output reg [31:0] ALU_output_o,
output reg [31:0] B_o,
output reg [31:0] IR_o,
output reg [31:0] PC_o
);
always@(posedge clk)
if(reset_n == 1'b0) begin
// Cond_o <= 0;
ALU_output_o <= 0;
B_o <= 0;
IR_o <= 0;
PC_o <= 0;
end
else begin
// Cond_o <= Cond_i;
ALU_output_o <= ALU_output_i;
B_o <= B_i;
IR_o <= IR_i;
PC_o <= PC_i;
end
endmodule
`timescale 1ns / 1ps
module DMEM(
input clk,
input reset_n,
input [5:0] opcode,
input [31:0] alu_address, // 要访问或写入的地址
input [31:0] WD, // 要写入的数据
output [31:0] RD // 读出的数据
);
reg [31:0] memory [255:0]; // 256*32的数据存储缓冲器
// 用本地文件初始化存储器
initial begin
$readmemh("E:/FPGA_code/CPU_pipeline/CPU_pipeline.sim/sim_1/behav/xsim/base_data_data", memory);
end
assign RD = memory[alu_address / 4];
always@(negedge clk) begin
if(reset_n == 0) begin
end
else begin
if(opcode == 6'b101011) //SW
memory[alu_address / 4] <= WD;
end
end
endmodule
注意,如果你reset_n == 0重置的时候啥也不干,仿真并不会出错,但是上板后就“不符合要求”了,所以在此提供颜神的简单又靠谱的DMEM初始化方式,不好使他女装!
(看见了吗,颜牛字里行间都写着“贴心”二字)
`define ALU 6'b000000
`define MOVZ 6'b001010
`define LW 6'b100011
module MEM_WB_registers(
input clk,
input reset_n,
input [31:0] LMD_i,
input [31:0] ALU_output_i,
input [31:0] IR_i,
input [31:0] PC_i,
output reg [31:0] LMD_o,
output reg [31:0] ALU_output_o,
output reg [31:0] IR_o,
output reg [31:0] PC_o,
output reg write_enable
);
wire [5:0] opcode;
wire [5:0] ALUfunc;
wire [4:0] rt;
assign opcode = IR_i[31:26];
assign ALUfunc = IR_i[5:0];
assign rt = IR_i[20:16];
always@(posedge clk)
if(reset_n == 1'b0) begin
LMD_o <= 0;
ALU_output_o <= 0;
IR_o <= 0;
PC_o <= 0;
write_enable <= 0;
end
else begin
LMD_o <= LMD_i;
ALU_output_o <= ALU_output_i;
IR_o <= IR_i;
PC_o <= PC_i;
write_enable <= (opcode == `LW || (opcode == `ALU && !(ALUfunc == `MOVZ && rt != 0))) && IR_i != 32'h00000000? 1 : 0;
end
endmodule
`timescale 1ns / 1ps
`define LW 6'b100011
module Writeback_aMUX(
input [5:0] opcode,
input [4:0] rt_address,
input [4:0] rd_address,
output [4:0] writeback_address
);
assign writeback_address = (opcode == `LW)? rt_address : rd_address; //Load-rt:其他-rd
endmodule
`define LW 6'b100011
module Writeback_dMUX(
input [5:0] opcode,
input [31:0] data0,
input [31:0] data1,
output [31:0] writeback_data
);
assign writeback_data = (opcode == `LW)? data0 : data1; //LMD:ALU_output
endmodule