流水线处理器的verilog实现
是的我刚刚验收完最后一个实验,所以怀着激动的心情,把当时其中一个留档的代码发出来,还算较为清晰,仅供没有思路的同学参考。造完cache,我的生活终于可以恢复正轨了,这几天折磨的寝食不安呀~~ 另外最近感悟颇多呀,美好的假期真正开始了!!
先讲一个鬼故事,我花了一天造出基本框架后跑了一下就PASS了,但是波形完全不对。。(但是感谢这个PASS给我调下去的勇气)
然后就开始修锅,修了将近10个大bug,着实的锻炼了观察波形调试的能力,咱就是说我是干了多少负负得正的事儿PASS的啊!(还是这个测试环境本身就有点问题)
学弟学妹们请慎重参考,相信你们做完非流水的这个都能做出来,另外不出意外的话,你们会遇见我们班规格严格的ywy学长(验收建议找老师哦哈哈就俩人一起笑着笑着就完事儿了),人肉查重哦~
个人认为我码风还算可以,如果我的代码有写的错误/值得改进之处,欢迎指出!!
base 版本
设计图如下,WB阶段有一个MUX没画,因为没地方了(懒了)哈哈
最后版本跟我最开始写出来的框架有些许变化,如果你看到了让你不知所以然的代码,大概率是修锅的时候造的。。
CPU.v
`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
PC.v
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
ADD.v
`timescale 1ns / 1ps module ADD( input [31:0] data1, input [31:0] data2, output [31:0] result ); assign result = data1 + data2; endmodule
IMEM.v
//组合逻辑 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
MUX.v
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
IF_ID_registers.v
`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
Extender.v
`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
Registers.v
`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
ID_EX_registers.v
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
MUX1.v
`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
MUX2.v
`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
Equal.v
`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
ALU.v
`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
EX_MEM_registers.v
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
DMEM.v
`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初始化方式,不好使他女装!
(看见了吗,颜牛字里行间都写着“贴心”二字)
MEM_WB_registers.v
`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
Writeback_aMUX.v
`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
Writeback_dMUX.v
`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
没有回复内容