哈工大体系结构lab3 —— 流水线处理器的verilog实现

流水线处理器的verilog实现

是的我刚刚验收完最后一个实验,所以怀着激动的心情,把当时其中一个留档的代码发出来,还算较为清晰,仅供没有思路的同学参考。造完cache,我的生活终于可以恢复正轨了,这几天折磨的寝食不安呀~~ 另外最近感悟颇多呀,美好的假期真正开始了!!

先讲一个鬼故事,我花了一天造出基本框架后跑了一下就PASS了,但是波形完全不对。。(但是感谢这个PASS给我调下去的勇气)

然后就开始修锅,修了将近10个大bug,着实的锻炼了观察波形调试的能力,咱就是说我是干了多少负负得正的事儿PASS的啊!(还是这个测试环境本身就有点问题)

学弟学妹们请慎重参考,相信你们做完非流水的这个都能做出来,另外不出意外的话,你们会遇见我们班规格严格的ywy学长(验收建议找老师哦哈哈就俩人一起笑着笑着就完事儿了),人肉查重哦~

个人认为我码风还算可以,如果我的代码有写的错误/值得改进之处,欢迎指出!!

base 版本

设计图如下,WB阶段有一个MUX没画,因为没地方了(懒了)哈哈

哈工大体系结构lab3 —— 流水线处理器的verilog实现_第1张图片

最后版本跟我最开始写出来的框架有些许变化,如果你看到了让你不知所以然的代码,大概率是修锅的时候造的。。

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初始化方式,不好使他女装!

哈工大体系结构lab3 —— 流水线处理器的verilog实现_第2张图片

(看见了吗,颜牛字里行间都写着“贴心”二字)

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

你可能感兴趣的:(FPGA,fpga开发)