简记一次计算机系统课程实验-RISCV流水线处理器设计与实现

本文档详细介绍了RISCV流水线处理器的设计与实现过程,包括运算单元、存储单元和控制单元的构建,以及解决数据冒险和控制冒险的方法。通过模块化设计,实现了IF/ID、ID/EX、EX/MEM、MEM/WB等阶段的数据路径,并通过分支单元和ALU控制器来处理分支和算术逻辑操作。此外,还涉及到了数据存储器的读写操作和立即数拓展。
摘要由CSDN通过智能技术生成

实验-RISCV流水线处理器设计与实现

一.流水线设计

![TheStructureChart]在这里插入图片描述

二、rtl文件

在根目录文件夹下。

三、流水线实现思路

(一)完成各组件的设计

1.运算单元的设计

包含adder,alu,data_mem,imm_gen,mux,基本上都很简单。
根据《RISC_V_Experiment-2022》PPT里的内容就可以实现imm_gen,

比较复杂的是data_mem文件,首先RISC-V采用小端格式,而且根据load/store指令funct3的不同,要进行读/写的长度不一样,不同的指令还要进行不同的符号/无符号扩展,如lhu和lh。
2.存储单元的设计
3.控制单元的设计
①proc_controller

proc_controller是非常重要的控制单元。
在PPT《2021-第4章第3讲-单周期控制器》里的这张图给出了一些主控制单元控制信号设计的方法
在这里插入图片描述

在proc_controller文件中也给出了一些控制信号设计的提示:

(ps:这里额外设置了jar_sel变量,是因为branch进行特殊运算的需要,后面branch会提到)
(pss:这里ALUop信号的设置与付老师一开始给的不一样,将rtype与IType分离开,而且将IType中的store/load与addi、ori和jalr也分离开,因为觉得这样方便设计alu_controller)
我们这里首先将各所需要设计的指令用变量表示出来,方便之后代码的书写
在这里插入图片描述

②branch_unit

branch_unit是为了确定下一条指令的地址,如果不执行jal、jalr、branch,则下一条指令地址为pc+4,否则更改branch_target的值为相应指令的跳转地址,这里额外设置了jal_sel变量,是因为jal与jalr所执行的跳转动作是不一样的,jalr指令中包含源寄存器。如图:
在这里插入图片描述

(ps:其实这里jalr的运算结果不应该放在pc_plus_imm变量里的,这个变量里应该只放jal和branch的pc+imm,因为jalr指令的pc_plu_imm里实际为pc+imm+rs1)

③alu_controller

(ps:首先这里input的变量额外添加了opcode,是为了为了区分IType和UType中的funct部分与imm部分,例如UType没有funct,但是imm有部分可能与funct有相同的值)
这里的output的operation是可以自己定义的,只要定义的4位operation预期执行的运算与alu文件中的alu_ctrl控制执行的运算保持一致即可。

4.解决冒险问题
①结构冒险

在本实验中寄存器等存储器同时被多条指令使用貌似不会产生结构冒险、资源冲突,如寄存器的读、写口是类似于分离开的,可以同时被读写

②数据冒险

用stall和forward,即hazard文件和forward_unit文件解决该问题,解决写后读的问题,
采取PPt《2021-第4章第4讲-流水线原理》上的此图设计思路
在这里插入图片描述

即:如图:
在这里插入图片描述

如果当前指令经过ID部分译码后,检测到出现数据冒险(forward文件的代码实现后续附上)
则当前指令ID后阻塞,pc值不发生改变,下一条执行的指令依然为该条指令,且ALU的操作数通过如下图所示的ALU数据选择器,将上一条指令写入的,当前指令的,其中一源寄存器的结果,取代未修改的寄存器的值:
在这里插入图片描述

至此实现了如PPT上所示的阻塞操作。
forward文件代码如下:
在这里插入图片描述
③控制冒险
控制冒险解决方法的基本思路有三种,如图:
在这里插入图片描述

本实验采用第一种方法,即阻塞分支指令后的三条指令,首先如果出现了分支指令且满足跳转条件,在BrFlush文件中,先将BrFlush控制信号置为1:
pc_sel = ((branch_taken && alu_result == 0) || jalr_sel || jal_sel) ? 1'b1 : 1'b0;
而后阻塞接下来的指令,
当前指令的下一条指令由下图所示代码完成阻塞,将ID/IF寄存器“冲刷”为“空”:
在这里插入图片描述

当前指令的再下一条指令由下图代码完成阻塞,将本条指令设为“空指令”:
在这里插入图片描述

当branch_unit中的branch_target值计算出来后,通过下图的代码,将当前应该执行的指令的地址置为branch_target:
在这里插入图片描述

(二)、将各组件连接起来(data_path)

1.设计IF单元并向IF/ID寄存器传入当前指令地址和未解码指令。

①如果遇到reset指令,则将pc进行初始化,pc值为0,如果遇到stall则不执行下一条指令,PC保持不变
PC <= reset ? 0 : stall ? PC : PC_mux_result;
②设置Brflush的值,根据branch_unit的output来设置。
assign PC_mux_result = BrFlush ? BrPC : PCplus4
③向IF/ID寄存器传入当前指令地址和未解码指令
在这里插入图片描述
(ps:这两条指令要控制其在非阻塞的情况下)

2.对data_path的理解

这个文件的代码条理很清晰,简单来说就是类似下图的结构:

data_path
`timescale 1ns / 1ps
// 数据通路
`include "pipeline_regs.sv"
import Pipe_Buf_Reg_PKG::*;

module Datapath #(
    parameter PC_W       = 9,   // width of the Program Counter
    parameter INS_W      = 32,  // Instruction Width
    parameter DATA_W     = 32,  // reigster width
    parameter DM_ADDRESS = 9,   // width of the data memory address
    parameter ALU_CC_W   = 4    // width of the ALU Control code
) (
    input logic clock,

    // reset , sets the PC to zero
    input logic reset,

    // Register file writing enable
    input logic reg_write_en,

    // Selection signal for the source of value used to write the regiser
    input logic WrRegDataSrc,

    // Register file or Immediate MUX
    input logic alu_src,

    // Memroy Writing Enable
    input logic mem_write_en,

    // Memroy Reading Enable
    input logic mem_read_en,

    // Branch Enable
    input logic branch_taken,

    // Jalr Mux Select
    input logic jalr_sel,
    // Jal Instruction distinguish from jalr used in the branch function
    input logic jal_sel,

    input logic [1:0] alu_op,

    // Mux4to1 Select
    input logic [1:0] RWSel,

    // ALU Control Code ( input of the ALU )
    input logic [ALU_CC_W -1:0] alu_cc,

    output logic [6:0] opcode,

    output logic [6:0] opcode_IDEX,

    output logic [6:0] funct7,

    output logic [2:0] funct3,

    output logic [1:0] aluop_current,

    // data write back to register
    output logic [DATA_W-1:0] wb_data
);

  // define the pipeline registers
  if_id_reg  PipeRegIFID;
  id_ex_reg  PipeRegIDEX;
  ex_mem_reg PipeRegEXMEM;
  mem_wb_reg PipeRegMEWB;
  /*todo: analysis here
    * the registers are declared here.
    * each struct in pipeline_regs.sv is a set of registers

  */

  // ====================================================================================
  //                                Instruction Fetch (IF)
  // ====================================================================================
  //
  // peripheral logic here.
  //
  logic BrFlush, stall;
  logic [31:0] PC_mux_result, PC, PCplus4, BrPC, instr;

  // add your code here for PC generation
  
  assign PCplus4 = PC + 4;
  assign PC_mux_result = BrFlush ? BrPC : PCplus4;
  always @(posedge clock) begin
    //stall, do not update the PC
    PC <= reset ? 0 :  stall  ? PC : PC_mux_result;//①:如果遇到reset指令,则将pc进行初始化//②:如果遇到stall则不执行下一条指令
    //这里pc赋值必须用<=而不能用=
  end

  

  // your instruction memory
  Insn_mem IM (
      .read_address(PC[PC_W-1 : 0]),
      .insn(instr)
  );

  // ====================================================================================
  //                             End of Instruction Fetch (IF)
  // ====================================================================================

// ====================================================================================
  //IF/ID寄存器,有控制pc和instr同步的功能 ,也有遇到stall时,将后续元件清空(设为bubble)的作用
  // ====================================================================================
  always @(posedge clock, posedge reset) begin
    // add your logic here to update the IF_ID_Register
    if(!stall) begin
      PipeRegIFID.Curr_Pc    <= (reset|BrFlush) ? 9'b0:PC;
      PipeRegIFID.Curr_Instr <= (reset|BrFlush) ? 32'b0:instr;
    end
  end

  // ====================================================================================
  //                                Instruction Decoding (ID)
  // ====================================================================================
  //
  // peripheral logic here.
  //
  assign opcode = PipeRegIFID.Curr_Instr[6:0];
  logic [31:0] rd1, rd2, ImmG;

  //
  // add your register file here.
  //
  // ====================================================================================
  //  寄存器文件(空间?),放的是寄存器的内容,在tb文件夹的test_r文件夹下的的rf_init文件的内容从上至下为寄存器x0-x31
  // ====================================================================================
  Reg_file RF (
      .clock(clock),
      .reset(reset),
      .write_en(PipeRegMEWB.RegWrite),
      .write_addr(PipeRegMEWB.rd),
      .data_in(wb_data),
      .read_addr1(PipeRegIFID.Curr_Instr[19:15]),
      .read_addr2(PipeRegIFID.Curr_Instr[24:20]),
      .data_out1(rd1),
      .data_out2(rd2)
  );

  //
  // add your immediate generator here
  //
  // ====================================================================================
  //  立即数拓展元件
  // ====================================================================================
  Imm_gen Imm_Gen (
      .inst_code(PipeRegIFID.Curr_Instr),
      .imm_out  (ImmG)
  );

  // ====================================================================================
  //                                End of Instruction Decoding (ID)
  // ====================================================================================
  always @(posedge clock, posedge reset) begin
    // add your logic here to update the ID_EX_Register
    if (reset | stall | BrFlush) begin
      PipeRegIDEX.ALU2ndOperandSrc <= 1'b0;
      PipeRegIDEX.WrRegDataSrc     <= 1'b0;
      PipeRegIDEX.RegWrite         <= 1'b0;
      PipeRegIDEX.MemRead          <= 1'b0;
      PipeRegIDEX.MemWrite         <= 1'b0;
      PipeRegIDEX.ALUOp            <= 2'b0;
      PipeRegIDEX.Branch           <= 1'b0;
      PipeRegIDEX.JalrSel          <= 1'b0;
      PipeRegIDEX.JalSel           <= 1'b0; 
      PipeRegIDEX.RWSel            <= 2'b0;
      PipeRegIDEX.Curr_Pc          <= 9'b0;
      PipeRegIDEX.RD_One           <= 32'b0;
      PipeRegIDEX.RD_Two           <= 32'b0;
      PipeRegIDEX.RS_One           <= 5'b0;
      PipeRegIDEX.RS_Two           <= 5'b0;
      PipeRegIDEX.rd               <= 5'b0;
      PipeRegIDEX.ImmG             <= 32'b0;
      PipeRegIDEX.func3            <= 3'b0;
      PipeRegIDEX.func7            <= 7'b0;
      PipeRegIDEX.Curr_Instr       <= 32'b0;
    end else if (!stall) begin
      PipeRegIDEX.ALU2ndOperandSrc <= alu_src;
      PipeRegIDEX.WrRegDataSrc     <= WrRegDataSrc;
      PipeRegIDEX.RegWrite         <= reg_write_en;
      PipeRegIDEX.MemRead          <= mem_read_en;
      PipeRegIDEX.MemWrite         <= mem_write_en;
      PipeRegIDEX.ALUOp            <= alu_op;
      PipeRegIDEX.Branch           <= branch_taken;
      PipeRegIDEX.JalrSel          <= jalr_sel;
      PipeRegIDEX.JalSel           <= jal_sel;
      PipeRegIDEX.RWSel            <= RWSel;
      PipeRegIDEX.Curr_Pc          <= PipeRegIFID.Curr_Pc;
      PipeRegIDEX.RD_One           <= rd1;
      PipeRegIDEX.RD_Two           <= rd2;
      PipeRegIDEX.RS_One           <= PipeRegIFID.Curr_Instr[19:15];
      PipeRegIDEX.RS_Two           <= PipeRegIFID.Curr_Instr[24:20];
      PipeRegIDEX.rd               <= PipeRegIFID.Curr_Instr[11:7];
      PipeRegIDEX.ImmG             <= ImmG;
      PipeRegIDEX.func3            <= PipeRegIFID.Curr_Instr[14:12];
      PipeRegIDEX.func7            <= PipeRegIFID.Curr_Instr[31:25];
      PipeRegIDEX.Curr_Instr       <= PipeRegIFID.Curr_Instr;
    end
  end

  // ====================================================================================
  //                                    Execution (EX)
  // ====================================================================================
  //
  // add your ALU, branch unit and with peripheral logic here
  //
  logic [31:0] FA_mux_result, FB_mux_result;
  logic [31:0] ALU_result, PCplusImm, PCplus4_EX, src_mux_result, mem_data;
  logic [1:0] ForwardA, ForwardB;
  logic zero;

  assign aluop_current = PipeRegIDEX.ALUOp;

  assign opcode_IDEX = PipeRegIDEX.Curr_Instr[6:0];

  assign funct3 = PipeRegIDEX.func3;

  assign funct7 = PipeRegIDEX.func7;

  alu ALU (
      .operand_a(FA_mux_result),
      .operand_b(src_mux_result),
      .alu_ctrl(alu_cc),
      .alu_result(ALU_result),
      .zero(zero)
  );
// ====================================================================================
  //  若有有传递则更新传递
  // ====================================================================================
  BranchUnit Branch_unit (
      .cur_pc(PipeRegIDEX.Curr_Pc),
      .imm(PipeRegIDEX.ImmG),
      .jalr_sel(PipeRegIDEX.JalrSel),
      .jal_sel(PipeRegIDEX.JalSel),
      .branch_taken(PipeRegIDEX.Branch),
      .alu_result(ALU_result),
      .pc_plus_imm(PCplusImm),
      .pc_plus_4(PCplus4_EX),
      .branch_target(BrPC),
      .pc_sel(BrFlush)
  );
  mux4 FA_mux (
      .d00(PipeRegIDEX.RD_One),
      .d10(mem_data),
      .d01(wb_data),
      .d11(32'b0),
      .s  (ForwardA),
      .y  (FA_mux_result)
  );

  mux4 FB_mux (
      .d00(PipeRegIDEX.RD_Two),
      .d10(mem_data),
      .d01(wb_data),
      .d11(32'b0),
      .s  (ForwardB),
      .y  (FB_mux_result)
  );

  mux2 src_mux (
      .d0(FB_mux_result),
      .d1(PipeRegIDEX.ImmG),
      .s (PipeRegIDEX.ALU2ndOperandSrc),
      .y (src_mux_result)
  );

  // ====================================================================================
  //                                End of Execution (EX)
  // ====================================================================================
  always @(posedge clock, posedge reset) begin
    // add your logic here to update the EX_MEM_Register
    if (reset) begin
      PipeRegEXMEM.RegWrite     <= 1'b0;
      PipeRegEXMEM.WrRegDataSrc <= 1'b0;
      PipeRegEXMEM.MemRead      <= 1'b0;
      PipeRegEXMEM.MemWrite     <= 1'b0;
      PipeRegEXMEM.RWSel        <= 2'b0;
      PipeRegEXMEM.Pc_Imm       <= 32'b0;
      PipeRegEXMEM.Pc_Four      <= 32'b0;
      PipeRegEXMEM.Imm_Out      <= 32'b0;
      PipeRegEXMEM.Alu_Result   <= 32'b0;
      PipeRegEXMEM.MemWrData    <= 32'b0;
      PipeRegEXMEM.rd           <= 5'b0;
      PipeRegEXMEM.func3        <= 3'b0;
      PipeRegEXMEM.func7        <= 7'b0;
      PipeRegEXMEM.Curr_Instr   <= 32'b0;
    end else begin
      PipeRegEXMEM.RegWrite     <= PipeRegIDEX.RegWrite;
      PipeRegEXMEM.WrRegDataSrc <= PipeRegIDEX.WrRegDataSrc;
      PipeRegEXMEM.MemRead      <= PipeRegIDEX.MemRead;
      PipeRegEXMEM.MemWrite     <= PipeRegIDEX.MemWrite;
      PipeRegEXMEM.RWSel        <= PipeRegIDEX.RWSel;
      PipeRegEXMEM.Pc_Imm       <= PCplusImm;
      PipeRegEXMEM.Pc_Four      <= PCplus4_EX;
      PipeRegEXMEM.Imm_Out      <= PipeRegIDEX.ImmG;
      PipeRegEXMEM.Alu_Result   <= ALU_result;
      PipeRegEXMEM.MemWrData    <= FB_mux_result;
      PipeRegEXMEM.rd           <= PipeRegIDEX.rd;
      PipeRegEXMEM.func3        <= PipeRegIDEX.func3;
      PipeRegEXMEM.func7        <= PipeRegIDEX.func7;
      PipeRegEXMEM.Curr_Instr   <= PipeRegIDEX.Curr_Instr;
    end
  end
  // ====================================================================================
  //                                    Memory Access (MEM)
  // ====================================================================================
  // add your data memory here.
  logic [31:0] ReadData;
  datamemory DM (
      .clock(clock),
      .read_en(PipeRegEXMEM.MemRead),
      .write_en(PipeRegEXMEM.MemWrite),
      .address(PipeRegEXMEM.Alu_Result[11:0]),
      .data_in(PipeRegEXMEM.MemWrData),
      .funct3(PipeRegEXMEM.func3),
      .data_out(ReadData)
  );

  mux4 mem_mux (
      .d00(PipeRegEXMEM.Alu_Result),
      .d01(PipeRegEXMEM.Pc_Four),
      .d10(PipeRegEXMEM.Imm_Out),
      .d11(PipeRegEXMEM.Pc_Imm),
      .s  (PipeRegEXMEM.RWSel),
      .y  (mem_data)
  );

  // ====================================================================================
  //                                End of Memory Access (MEM)
  // ====================================================================================
  always @(posedge clock) begin
    // add your logic here to update the MEM_WB_Register
    if (reset) begin
      PipeRegMEWB.RegWrite     <= 1'b0;
      PipeRegMEWB.WrRegDataSrc <= 1'b0;
      PipeRegMEWB.RWSel        <= 2'b0;
      PipeRegMEWB.Pc_Imm       <= 32'b0;
      PipeRegMEWB.Pc_Four      <= 32'b0;
      PipeRegMEWB.Imm_Out      <= 32'b0;
      PipeRegMEWB.Alu_Result   <= 32'b0;
      PipeRegMEWB.MemReadData  <= 32'b0;
      PipeRegMEWB.rd           <= 5'b0;
      PipeRegMEWB.Curr_Instr   <= 5'b0;
    end else begin
      PipeRegMEWB.RegWrite     <= PipeRegEXMEM.RegWrite;
      PipeRegMEWB.WrRegDataSrc <= PipeRegEXMEM.WrRegDataSrc;
      PipeRegMEWB.RWSel        <= PipeRegEXMEM.RWSel;
      PipeRegMEWB.Pc_Imm       <= PipeRegEXMEM.Pc_Imm;
      PipeRegMEWB.Pc_Four      <= PipeRegEXMEM.Pc_Four;
      PipeRegMEWB.Imm_Out      <= PipeRegEXMEM.Imm_Out;
      PipeRegMEWB.Alu_Result   <= PipeRegEXMEM.Alu_Result;
      PipeRegMEWB.MemReadData  <= ReadData;
      PipeRegMEWB.rd           <= PipeRegEXMEM.rd;
      PipeRegMEWB.Curr_Instr   <= PipeRegEXMEM.Curr_Instr;
      // $display("------------------------------------------------------------------");
      // $display("PipeRegMEWB.Curr_Instr = %x",PCplus4);
      // // $display("PipeRegMEWB.Imm_Out = %x", PipeRegMEWB.Imm_Out);
      // // $display("PipeRegMEWB.Pc_Four = %x", PipeRegMEWB.Pc_Four);
      // $display("PCplusImm = %x", PipeRegMEWB.Imm_Out);
      // $display("PCplus4_EX = %x", PipeRegMEWB.Pc_Four);
      // $display("PipeRegMEWB.WrRegDataSrc = %x", BrPC);
      // $display("PipeRegIDEX.Curr_Pc = %x", PipeRegIDEX.Curr_Pc);
      // $display("PipeRegMEWB.Alu_Result = %x", PipeRegMEWB.Alu_Result);
      // $display("wb_data = %x",wb_data);
    end
  end

  // ====================================================================================
  //                                  Write Back (WB)
  // ====================================================================================
  //
  // add your write back logic here.
  //
  logic [31:0] res_mux_result;
  mux2 res_mux (
      .d0(PipeRegMEWB.Alu_Result),
      .d1(PipeRegMEWB.MemReadData),
      .s (PipeRegMEWB.WrRegDataSrc),
      .y (res_mux_result)
  );

  mux4 wrs_mux (
      .d00(res_mux_result),
      .d01(PipeRegMEWB.Pc_Four),
      .d10(PipeRegMEWB.Imm_Out),
      .d11(PipeRegMEWB.Pc_Imm),
      .s  (PipeRegMEWB.RWSel),
      .y  (wb_data)
  );

  // ====================================================================================
  //                               End of Write Back (WB)
  // ====================================================================================
  // ====================================================================================
  //                                   other logic
  // ====================================================================================
  //
  // add your hazard detection logic here
  //
  Hazard_detector hazard_unit (
      .clock(clock),
      .reset(reset),
      .if_id_rs1(PipeRegIFID.Curr_Instr[19:15]),
      .if_id_rs2(PipeRegIFID.Curr_Instr[24:20]),
      .id_ex_rd(PipeRegIDEX.rd),
      .id_ex_memread(PipeRegIDEX.MemRead),
      .stall(stall)
  );

  //
  // add your forwarding logic here
  //
  ForwardingUnit forwarding_unit (
      .rs1(PipeRegIDEX.RS_One),
      .rs2(PipeRegIDEX.RS_Two),
      .ex_mem_rd(PipeRegEXMEM.rd),
      .mem_wb_rd(PipeRegMEWB.rd),
      .ex_mem_regwrite(PipeRegEXMEM.RegWrite),
      .mem_wb_regwrite(PipeRegMEWB.RegWrite),
      .forward_a(ForwardA),
      .forward_b(ForwardB)
  );
  //
  // possible extra code
  //


endmodule

pipeline_regs
//
package  Pipe_Buf_Reg_PKG;
    // Reg A
    typedef struct packed{
        logic [8:0]     Curr_Pc;
        logic [31:0]    Curr_Instr;
    } if_id_reg;

    // Reg B
    typedef struct packed{
        logic ALU2ndOperandSrc;
        logic WrRegDataSrc;
        logic RegWrite;
        logic MemRead;
        logic MemWrite;
        logic [1:0]     ALUOp;
        logic Branch;
        logic JalrSel;
        logic JalSel;               
        logic [1:0]     RWSel;
        logic [8:0]     Curr_Pc;
        logic [31:0]    RD_One;     // 1st read data from the reg file.
        logic [31:0]    RD_Two;     // 2nd read data from the reg file.
        logic [4:0]     RS_One;     // 1st read register address of Curr_Instr
        logic [4:0]     RS_Two;     // 2nd read register address of Curr_Instr
        logic [4:0]     rd;         // dst register to write of Curr_Instr
        logic [31:0]    ImmG;       // Immediate value generated by Imm_gen
        logic [2:0]     func3;
        logic [6:0]     func7;
        logic [31:0]    Curr_Instr;
    } id_ex_reg;

    // Reg C
    typedef struct packed{
        logic RegWrite;
        logic WrRegDataSrc;
        logic MemRead;
        logic MemWrite;
        logic [1:0]     RWSel;
        logic [31:0]    Pc_Imm;
        logic [31:0]    Pc_Four;
        logic [31:0]    Imm_Out;
        logic [31:0]    Alu_Result;
        logic [31:0]    MemWrData;  // data written to the memory
        logic [4:0]     rd;         // dst register to write of Curr_Instr
        logic [2:0]     func3;
        logic [6:0]     func7;
        logic [31:0]    Curr_Instr;
    } ex_mem_reg;

    // Reg D
    typedef struct packed{
        logic RegWrite;
        logic WrRegDataSrc;
        logic [1:0]     RWSel;
        logic [31:0]    Pc_Imm;
        logic [31:0]    Pc_Four;
        logic [31:0]    Imm_Out;
        logic [31:0]    Alu_Result;
        logic [31:0]    MemReadData;
        logic [4:0]     rd;
        logic [31:0]    Curr_Instr;
    } mem_wb_reg;
endpackage

proc_controller
`timescale 1ns / 1ps
// 主控制器
module Proc_controller (

    // ======================== Inputs ========================
    input logic [6:0] Opcode,  // 7-bit opcode field of the instruction

    // ======================== Outputs ========================
    // Selection signal for the source of the 2nd ALU operand:
    //0: the 2nd read data from the register file;
    //1: the immediate value generated by Imm_gen
    output logic ALU2ndOperandSrc,

    // Selection signal for the source of value used to write the regiser:
    // 0: ALU; 1: data memory.
    output logic WrRegDataSrc,

    // Write register enable
    output logic WrRegEn,

    // Write memory enable
    output logic WrMemEn,

    // Read memory enable
    output logic RdMemEn,

    // 00: LW/SW/AUIPC;  01: Branch;
    // 10: Rtype/Itype(Itype wrong!);  11: JAL/LUI/JALR//Itype(include jalr)//这里是考虑到itype中的funct3和rtype中的有重合交叉的指令,辨别不清
    output logic [1:0] ALUOp,

    //0: branch is not taken; 1: branch is taken
    output logic Branch,

    //0: Jalr is not taken; 1: jalr is taken
    output logic JalrSel,
    output logic JalSel,//分开是因为branch选择器对jalr和jal做出不同的处理,jal直接加

    // 00:Register Write Back;
    // 01: PC+4 write back(JAL/JALR);
    // 10: imm-gen write back(LUI);
    // 11: pc+imm-gen write back(AUIPC)
    output logic [1:0] RWSel
);

  logic [10:0] con;
  /*****************************************************instruction's Type*****************************************************/
  //RType
  logic [6:0] RType= 7'b0110011;//include slt
  //IType
  logic [6:0] ori= 7'b0010011;//andi
  logic [6:0] lw= 7'b0000011;//lb,lh,lbu,lhu
  logic [6:0] jalr= 7'b1100111;
  //UType
  logic [6:0] jal= 7'b1101111;//
  logic [6:0] lui= 7'b0110111;//
  //SType
  logic [6:0] sw= 7'b0100011;//sb,sh
  logic [6:0] beq= 7'b1100011;//bne,blt,bge,bltu,bgeu
  /*****************************************************controll signs*****************************************************/
  assign ALU2ndOperandSrc = (Opcode == lw || Opcode == sw || Opcode == ori || Opcode == jalr || Opcode == jal) ? 1'b1 : 1'b0;
  assign WrRegDataSrc = (Opcode == lw) ? 1'b1 : 1'b0;
  assign WrRegEn = (Opcode == RType || Opcode == lw || Opcode == ori || Opcode == lui || Opcode == jal || Opcode == jalr) ? 1'b1 : 1'b0;
  assign RdMemEn = (Opcode == lw) ? 1'b1 : 1'b0;
  assign WrMemEn = (Opcode == sw) ? 1'b1 : 1'b0;
  assign ALUOp = (Opcode == RType) ? 2'b10 : (Opcode == beq) ? 2'b01 : (Opcode == jal || Opcode == jalr || Opcode == lui || Opcode == ori) ? 2'b11 : 2'b00;
  assign Branch = (Opcode == beq) ? 1'b1 : 1'b0;
  assign JalrSel = (Opcode == jalr) ? 1'b1 : 1'b0;
  assign JalSel = (Opcode == jal) ? 1'b1 : 1'b0;
  assign RWSel = (Opcode == jalr || Opcode == jal)? 2'b01 : (Opcode == lui) ? 2'b10 :(Opcode == beq) ? 2'b11 : 2'b00;
endmodule

reg_file
`timescale 1ns / 1ps
// 寄存器文件
module Reg_file #(
    parameter DATA_WIDTH    = 32,  // number of bits in each register
    parameter ADDRESS_WIDTH = 5, //number of registers = 2^ADDRESS_WIDTH
    parameter NUM_REGS      = 2 ** ADDRESS_WIDTH
)(
   // Inputs
   input  clock,                  //clock
   input  reset,                  //synchronous reset; reset all regs to 0  upon assertion.
   input  write_en,            //write enable
   input  [ADDRESS_WIDTH-1:0] write_addr, //address of the register that supposed to written into
   input  [DATA_WIDTH-1:0]    data_in, // data that supposed to be written into the register file
   input  [ADDRESS_WIDTH-1:0] read_addr1, //first address to be read from
   input  [ADDRESS_WIDTH-1:0] read_addr2, //second address to be read from

   // Outputs
   output logic [DATA_WIDTH-1:0] data_out1, //content of reg_file[read_addr1] is loaded into
   output logic [DATA_WIDTH-1:0] data_out2  //content of reg_file[read_addr2] is loaded into
);


integer i;

logic [DATA_WIDTH-1:0] register_file [NUM_REGS-1:0];

always @( negedge clock )
begin
    if( reset == 1'b1 )
        for (i = 0; i < NUM_REGS ; i = i + 1) begin
            register_file [i] <= 0;
        end
    else if( write_en == 1'b1 && write_addr ) begin
        register_file [ write_addr ] <=    data_in;
    end
end
assign data_out1 = register_file[read_addr1];
assign data_out2 = register_file[read_addr2];


endmodule

riscv_proc
`timescale 1ns / 1ps
// the top module for the RISC-V processor
// basically, you do not need to modify this file.
module riscv #(
    parameter DATA_W = 32
) (
    input logic clock,
    input logic reset,
    output logic [31:0] WB_Data  // The ALU_Result
);

  logic [6:0] opcode;
  logic [6:0] opcode_IDEX;
  logic
      ALU2ndOperandSrc,
      WrRegDataSrc,
      RegWrite,
      MemRead,
      MemWrite,
      Branch,
      JalrSel;
  logic [1:0] RWSel;

  logic [1:0] ALUop;
  logic [1:0] ALUop_Reg;
  logic [6:0] Funct7;
  logic [2:0] Funct3;
  logic [3:0] Operation;

  Proc_controller proc_controller (
      opcode,
      ALU2ndOperandSrc,
      WrRegDataSrc,
      RegWrite,
      MemWrite,
      MemRead,
      ALUop,
      Branch,
      JalrSel,
      JalSel,
      RWSel
  );

  ALU_Controller proc_alu_controller (
      ALUop_Reg,
      Funct7,
      Funct3,
      opcode_IDEX,
      Operation
  );

  Datapath proc_data_path (
      clock,
      reset,
      RegWrite,
      WrRegDataSrc,
      ALU2ndOperandSrc,
      MemWrite,
      MemRead,
      Branch,
      JalrSel,
      JalSel,
      ALUop,
      RWSel,
      Operation,
      opcode,
      opcode_IDEX,
      Funct7,
      Funct3,
      ALUop_Reg,
      WB_Data
  );

endmodule

adder
`timescale 1ns / 1ps
// 加法器
module adder #(
    parameter WIDTH = 8
) (
    input  logic [WIDTH-1:0] a,
    b,
    output logic [WIDTH-1:0] y
);

  // add your adder logic here
  assign y = a + b;
endmodule

alu_controller
`timescale 1ns / 1ps
// 算数逻辑单元控制器
module ALU_Controller (
    input  logic [1:0] alu_op,    // 2-bit opcode field from the Proc_controller
    input  logic [6:0] funct7,    // insn[31:25]
    input  logic [2:0] funct3,    // insn[14:12]
    input  logic [6:0] opcode,    // insn[6:0] //为了区分Itype和UType中的funct部分与imm部分,例如utype没有funct,但是imm有部分可能与funct有相同的值
    output logic [3:0] operation  // operation selection for ALU
);

  // add your code here.
  
  always_comb begin
    
    case (alu_op)
      2'b00: //Store & load
        operation = 4'b0010;

      2'b01://branch 
      case (funct3)
        3'b000: 
          operation = 4'b0110; // beq -> sub
        3'b001: 
          operation = 4'b1001; // bne
        3'b100: 
          operation = 4'b1010; // blt
        3'b101: 
          operation = 4'b1011; // bge
        3'b110: 
          operation = 4'b1100; // bltu
        3'b111: 
          operation = 4'b1101; // bgeu
        default:begin
          operation = 4'b0000;
          $display("---------------------------------->Undfined_ALU_controller_Type\n");
        end 
      endcase

      2'b10://RType
      case (funct3)
        3'b000:
        case (funct7)
          //add
          7'b0000000: 
            operation = 4'b0010;
          //sub
          7'b0100000: 
            operation = 4'b0110;
          //addi
          default: 
            operation = 4'b0000;
        endcase
        3'b111://and
          operation = 4'b0000;
        3'b110://or
          operation = 4'b0001;
        3'b100://xor
          operation = 4'b0111;
        3'b101://mul//funct7=0funct3=101
          operation = 4'b0011;
        3'b010:
        case (funct7)
          //slt
          7'b0000000: operation = 4'b1000;
          default:begin
          operation = 4'b0000;
          $display("---------------------------------->Undfined_ALU_controller_Type\n");
        end 
        endcase
        default:begin
          operation = 4'b0000;
          $display("---------------------------------->Undfined_ALU_controller_Type\n");
        end 
          
      endcase

      2'b11: //IType、Utype
        case (opcode) 
          7'b0010011:
            case (funct3)
              3'b000: operation = 4'b0010;//addi
              3'b111: operation = 4'b0000;//andi
              3'b110: operation = 4'b0001;//ori
              3'b101: operation = 4'b0011;//muli
            default: operation = 4'b0000;
            endcase
          default:operation = 4'b0010;//jalr
        endcase
    endcase
  end

endmodule

alu
`timescale 1ns / 1ps
// 算数逻辑单元
module alu #(
    parameter DATA_WIDTH    = 32,
    parameter OPCODE_LENGTH = 4
) (
    input  logic signed [   DATA_WIDTH - 1 : 0] operand_a,
    input  logic signed [   DATA_WIDTH - 1 : 0] operand_b,
    input  logic [OPCODE_LENGTH - 1 : 0] alu_ctrl,    // Operation
    output logic signed [   DATA_WIDTH - 1 : 0] alu_result,
    output logic                         zero
);

  logic [31:0] s;
  logic [31:0] signed_s, unsigned_operand_a, unsigned_operand_b;

  // modify this
  //这里面没有考虑无符号整数和有符号整数
  assign unsigned_operand_a = operand_a;
  assign unsigned_operand_b = operand_b;
  
  always_comb begin
    // modify this
    case (alu_ctrl)
      4'b0000: //and
        alu_result = operand_a & operand_b;
      4'b0001: //or
        alu_result = operand_a | operand_b;
      4'b0010: //add
        alu_result = operand_a + operand_b;
      4'b0110: //sub include beq
        alu_result = operand_a - operand_b;
      4'b0111: //xor
        alu_result = operand_a ^ operand_b;
      4'b0011: //mul、muli
        alu_result = operand_a * operand_b;
      4'b1000: //slt
        alu_result = (operand_a < operand_b) ? 1'b1 : 1'b0;
      4'b1001: //bne
        alu_result = (operand_a != operand_b) ? 1'b0 : 1'b1;
      4'b1010: //blt
        alu_result = (operand_a < operand_b) ? 1'b0 : 1'b1;
      4'b1011: //bge
        alu_result = (operand_a >= operand_b) ? 1'b0 : 1'b1;
      4'b1100: //bltu
        alu_result = (unsigned_operand_a < unsigned_operand_b) ? 1'b0 : 1'b1;
      4'b1101: //bgeu
        alu_result = (unsigned_operand_a >= unsigned_operand_b) ? 1'b0 : 1'b1;
      default: begin
        alu_result = 32'b0;
        $display("---------------------------------->Undfined_ALU_Type\n");
      end    
    endcase
    assign zero = (alu_result == 0) ? 1'b1 : 1'b0;
    //$display("this operation: oper_a:%x oper_b:%x ctrl:%x result:%x", operand_a, operand_b, alu_ctrl, alu_result);
  end

endmodule


branch_unit
`timescale 1ns / 1ps
// 跳转单元
module BranchUnit #(
    parameter PC_W = 9
) (
    input  logic [PC_W - 1:0] cur_pc,
    input  logic [      31:0] imm,
    input  logic              jalr_sel,
    input  logic              jal_sel,
    input  logic              branch_taken,   // Branch
    input  logic [      31:0] alu_result,
    output logic [      31:0] pc_plus_imm,    // PC + imm
    output logic [      31:0] pc_plus_4,      // PC + 4
    output logic [      31:0] branch_target,  // BrPC
    output logic              pc_sel
);

  logic [31:0] pc;
  assign pc = {23'b0, cur_pc};
  always_comb begin
    pc_plus_4 = pc + 4;//3'b100;
    pc_plus_imm = (jalr_sel) //jal与jalr进行的操作不一样,根本原因在于jalr有用到寄存器rs1,而jal只用到了imm
                             ?  (alu_result >> 1) << 1/*这里将alu_reslut的最低为置为0,alu_result的值为rs1+imm*/ 
                             :   pc + imm ;//{double_pc_plus_imm[31:0],1'b0}//将sum最低位置0的另外一种方法
    pc_sel = ((branch_taken && alu_result == 0) || jalr_sel || jal_sel) ? 1'b1 : 1'b0;
    branch_target = pc_plus_imm;
  end

endmodule

data_mem
`timescale 1ns / 1ps
// 数据存储器
module datamemory #(
    parameter ADDR_WIDTH = 12,
    parameter DATA_WIDTH = 32
) (
    input  logic                     clock,
    input  logic                     read_en,
    input  logic                     write_en,
    input  logic [ADDR_WIDTH -1 : 0] address,   // read/write address
    input  logic [DATA_WIDTH -1 : 0] data_in,   // write Data
    input  logic [              2:0] funct3,    // insn[14:12]
    output logic [DATA_WIDTH -1 : 0] data_out   // read data
);

  logic [7 : 0] MEM[(2**12) - 1 : 0] ;//一开始没考虑读或写一字节2字节4字节的区别
  logic sin01, sin00;//没用上》-《
  always @(posedge clock)
    if (write_en) begin
      // modify this. Note the data width
      case(funct3)
        3'b000://sb
          begin
            MEM[address] <= data_in[7:0];
          end
        3'b001://sh
          begin
            MEM[address] <= data_in[7:0];
            MEM[address + 1] <= data_in[15:8];
          end
        3'b010://sw
          begin
            MEM[address] <= data_in[7:0];
            MEM[address + 1] <= data_in[15:8];
            MEM[address + 2] <= data_in[23:16];
            MEM[address + 3] <= data_in[31:24];
        end
      endcase
    end

  // maybe some extra code here
  always_comb begin
    if (read_en) begin
      // modify this
      data_out = 32'b0;
      case(funct3)
        3'b000://lb
          begin
            data_out = {{(24){MEM[address][7]}},MEM[address]};
          end
        3'b001://lh
          begin
            data_out = {{(16){MEM[address+1][7]}}, MEM[address + 1],MEM[address]};
          end
        3'b010://lw
          begin
            //data_out = {MEM[address],MEM[address + 1],MEM[address + 2],MEM[address + 3]};
            data_out[31:24] = MEM[address + 3];//8'b00000001;
            data_out[23:16] = MEM[address + 2];//8'b00110010;
            data_out[15:8] = MEM[address + 1];//8'b01010100;
            data_out[7:0] = MEM[address];//8'b01110110;
        end
        3'b100://lhu
          begin
            data_out[7:0] = MEM[address];//8'b01110110;
          end
        3'b101://lbu
          begin
            data_out[15:8] = MEM[address + 1];//8'b01010100;
            data_out[7:0] = MEM[address];//8'b01110110;
          end
      endcase
    end
  end

endmodule


flipflop
`timescale 1ns / 1ps

//
// Module Name: flipflop
// Description:  An edge-triggered register
//  When reset is `1`, the value of the register is set to 0.
//  当reset被置为1时,重置该寄存器的信号为全0
//  Otherwise:
//  否则
//    - if stall is set, the register preserves its original data
//    - else, it is updated by `d`.
//  如果stall被置为1,寄存器保留原来的值,stall被置为0,将d的值写入寄存器
//

// 边沿触发寄存器
module flipflop # (
    parameter WIDTH = 8
)(
    input  logic clock,
    input  logic reset,
    input  logic [WIDTH-1:0] d,
    input  logic stall,
    output logic [WIDTH-1:0] q
);

    always_ff @(posedge clock, posedge reset)
    begin
        if (reset)
            q <= 0;
        else if (!stall)
            q <= d;
    end


endmodule

forwarding_unit
`timescale 1ns / 1ps
// 数据定向处理单元
module ForwardingUnit (
    input logic [4:0] rs1,
    input logic [4:0] rs2,
    input logic [4:0] ex_mem_rd,
    input logic [4:0] mem_wb_rd,
    input logic ex_mem_regwrite,
    input logic mem_wb_regwrite,
    output logic [1:0] forward_a,
    output logic [1:0] forward_b
);

  // define your forwarding logic here.
  // forward_a & forward_b 见书P300
  assign forward_a = 
                    ((ex_mem_regwrite) && (ex_mem_rd != 0) && (ex_mem_rd == rs1))     ? 2'b10 :
                    ((mem_wb_regwrite) && (mem_wb_rd != 5'b0) && (rs1 == mem_wb_rd))  ? 2'b01 : 
                                                                                        2'b00 ;
  assign forward_b = 
                    ((ex_mem_regwrite) && (ex_mem_rd != 0) && (rs2 == ex_mem_rd))     ?  2'b10 : 
                    ((mem_wb_regwrite) && (mem_wb_rd != 5'b0) && (rs2 == mem_wb_rd))  ?  2'b01 : 
                                                                                         2'b00 ;

endmodule

hazard_detector
`timescale 1ns / 1ps
// 冒险探测器(阻塞生成器)
module Hazard_detector (
    input logic clock,
    input logic reset,
    input logic [4:0] if_id_rs1,
    input logic [4:0] if_id_rs2,
    input logic [4:0] id_ex_rd,
    input logic id_ex_memread,
    output logic stall
);

  // define your hazard detection logic here
  logic [1:0] counter;
  always @(negedge clock) begin
    stall <= (id_ex_memread && ((id_ex_rd == if_id_rs1) || (id_ex_rd == if_id_rs2)))? 1'b1 : 1'b0;
  end

endmodule

imm_gen
`timescale 1ns / 1ps
// 立即数扩展
module Imm_gen (
    input  logic [31:0] inst_code,
    output logic [31:0] imm_out
);
  
  logic [6:0] test;
  assign test = inst_code[6:0];
  always_comb begin
    imm_out = 32'b0;//init but seem not been used
    case (test)
    7'b0110011://Rtype 用不到imm
      imm_out = 32'b0;
    7'b0010011://addi ori 等都是零扩展lw是符号拓展?//the answer is signed extend
        imm_out =  {{20{inst_code[31]}},inst_code[31:20]};
    7'b0000011,7'b1100111://lw//jalr
        imm_out = {{(20){inst_code[31]}},inst_code[31:20]};
    7'b0110111://lui
        imm_out =  inst_code[31:12] << 12;
    7'b1101111://jal
        imm_out = {{10{inst_code[31]}},inst_code[31],inst_code[19:12],inst_code[20],inst_code[30:21],1'b0};
    7'b0100011://35://sw
      begin
        imm_out[11:5] = inst_code[31:25];
        imm_out[4:0] = inst_code[11:7];
      end
    7'b1100011://beq
      imm_out = {{(19){inst_code[31]}},inst_code[31],inst_code[7],inst_code[30:25],inst_code[11:8],1'b0} ;//
    default: imm_out = 32'b0;
    endcase
  end
endmodule
insn_mem
`timescale 1ns / 1ps
//
// 指令存储器?
module Insn_mem #(
    parameter ADDR_WIDTH = 9,
    parameter INSN_WIDTH = 32
)(
    input  logic [ADDR_WIDTH - 1 : 0] read_address,
    output logic [INSN_WIDTH - 1 : 0] insn
);

    logic [INSN_WIDTH-1 :0] insn_array [(2**(ADDR_WIDTH - 2))-1:0];

    assign insn = insn_array[read_address[ADDR_WIDTH - 1 : 2]];

endmodule

mux2
`timescale 1ns / 1ps
// 二端口多路选择器
module mux2 #(
    parameter WIDTH = 32
) (
    input logic [WIDTH-1:0] d0,
    d1,
    input logic s,
    output logic [WIDTH-1:0] y
);

  assign y = s ? d1 : d0;

endmodule

mux4
`timescale 1ns / 1ps
// 四端口多路选择器
module mux4 #(
    parameter WIDTH = 32
) (
    input logic [WIDTH-1:0] d00,
    input logic [WIDTH-1:0] d01,
    input logic [WIDTH-1:0] d10,
    input logic [WIDTH-1:0] d11,
    input logic [1:0] s,
    output logic [WIDTH-1:0] y
);

  // add your logic here
  assign y = (s == 2'b00) ? d00 :
             (s == 2'b01) ? d01 :
             (s == 2'b10) ? d10 : 
                            d11 ;

endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值