实现23条指令的简单流水线

开始之前务必将前面单周期的代码保存!

别怪我没提醒你哦

本项目将前面单周期的代码改为流水线,在开始之前请先保存单周期的代码!因为下一个项目扩充到27条指令,仍然是在前面单周期的代码基础上扩充,并非在本项目的流水线基础上扩充。

如果你已经使用Git管理自己的代码,建议创建一个分支,在新的分支上实现流水线。

也可征得指导教师同意,跳过本项目,扩充到27条指令后再实现流水线。

实验目的

(1)理解流水线与单周期数据通路的关系;

(2)掌握流水线微架构的实现方法。

实验任务

1. 实现23条指令的简单流水线

实现支持23条指令的五级流水线RISC-V。23条指令是前面单周期已经实现的,包括:I型运算类指令、R型运算类指令、sw指令、lw指令、分支指令。“简单”流水线是指没有处理冲突。 参考数据通路如图 1

RISCV_Pipeline_6e
图 1. 支持23指令的五级流水线数据通路

和单周期数据通路相比,最明显的变化是增加了流水线寄存器。流水线寄存器可以使用DataReg模块实例化,例 1给出了IF-ID流水线寄存器的示例代码,Load端口固定连接“1”,即每个时钟上升沿都会更新流水线寄存器。

例 1. 流水线寄存器示例
  // IF-ID pipeline regisetr
  wire [31:0] instr_id, pc_id;
  DataReg #(32) pr_instr_id(.iD(instr_if), .oQ(instr_id), .Clk(clk), .Load(1'b1), .Reset(reset));
  DataReg #(32) pr_pc_id(.iD(pc_if), .oQ(pc_id), .Clk(clk), .Load(1'b1), .Reset(reset));

和单周期数据通路相比,另一个较大的不同在分支控制上。在单周期数据通路中,指令译码、比较运算和转移地址计算均在一个指令周期内。而在流水线通路中,指令译码在译码阶段,如果比较运算在EX阶段完成,和指令译码不在同一个周期,那么在译码阶段控制器无法产生是否转移的PCjump信号。所以图 1数据通路增加了一个Branch信号,表示该指令为分支指令,传递到EX阶段后再根据比较结果产生PCjump信号,因此funct3也要传递到EX阶段。

实现分支指令的数据通路并不唯一。比如上面提到的比较运算和指令译码不在同一个周期的问题,如果将比较运算安排在译码阶段,就不存在这个问题,可以直接在译码阶段产生PCjump信号,甚至在译码阶段计算转移地址。但是这样一来,译码阶段的工作增加了,传输延时增大,如果成为关键路径,有可能影响到流水线的最高时钟频率;当然在目前的实验阶段,时钟信号手动产生,并不能直观地体会系统的频率特性,在后面进行到实速运行实验时,可以改变时钟频率,衡量系统性能。

数据通路不唯一还可以体现在转移地址计算上。图 1数据通路增加了一个加法器计算转移地址,ALU可用于产生标志位。教学视频中提到的另一种方案是比较运算独立于ALU,将ALU用于转移地址计算,实际上这种方案可能有利于后面扩充到27条指令。

读者可以按照自己的想法进行设计,图 1只是提供一个参考,实验平台的电路测试并不限定必须采用图 1结构,但如果发生转移则必须在EX阶段,不能在译码阶段或访存阶段发生转移,否则测试成绩会受影响。

2. 调试支持

在设计代码的调试支持部分需要增加与虚拟面板对应的观察信号和数据。因为观察变量比较多,从虚拟面板上逐一查找比较费时,为节省读者时间,下面给出与给定虚拟面板对应的ws和wd结构体定义。流水线各个阶段的控制信号几乎全部添加到了虚拟面板,但观察数据只添加了一部分,可以视需要自行添加。

    //送给调试器的观察信号,需要与虚拟面板的信号框相对应
    struct packed{
        logic       WS16;   //ImmToALU_id;   
        logic [4:0] WS15;   //ImmType[4:0]; 
        logic [3:0] WS14;   //ALUop_id[3:0];
        logic       WS13;   //MemWrite_id;   
        logic       WS12;   //MemToReg_id;   
        logic       WS11;   //RegWrite_id;   
        logic       WS10;   //ImmToALU_ex;   
        logic [3:0] WS9 ;   //ALUop_ex[3:0];
        logic       WS8 ;   //MemWrite_ex;   
        logic       WS7 ;   //MemToReg_ex;   
        logic       WS6 ;   //RegWrite_ex;   
        logic       WS5 ;   //MemToReg_mem;  
        logic       WS4 ;   //RegWrite_mem;  
        logic       WS3 ;   //MemToReg_wb;   
        logic       WS2 ;   //PCjump;        
        //以下观察信号用于电路测试,请勿修改!
        logic       WS1 ;   //MemWrite_mem;  
        logic       WS0 ;   //RegWrite_wb;   
    }ws;

    //送给调试器的观察数据,需要与虚拟面板的数据框相对应
    struct packed{
        logic [31:0] WD10;  //aluOut_ex;        
        logic [31:0] WD9 ;  //nextPC;
        logic [31:0] WD8 ;  //regReadData2_id;  
        logic [4:0]  WD7 ;  //ra2;             
        logic [31:0] WD6 ;  //immData_id;       
        logic [31:0] WD5 ;  //regWriteData_wb;
        logic [4:0]  WD4 ;  //wa_wb;
        //以下观察数据用于电路测试,请勿修改!
        logic [31:0] WD3;   //regReadData1_id;  
        logic [4:0]  WD2;   //ra1;             
        logic [31:0] WD1;   //instr_if;
        logic [31:0] WD0;   //pc_if;
    }wd;

3. 在实验平台验证设计

(1)编写测试程序,测试所实现的流水线微结构。

重点测试分支指令。因为从单周期变为流水线的过程中,分支控制是比较容易出错的地方。

(2)执行计算斐波那契数列的程序。

如果存在数据冲突或控制冲突,优先采用用静态指令调度(调整指令顺序)的方法解决;如果调整指令顺序无法解决,插入空操作指令避免冲突,但应仔细分析,仅在必要时插入最少的空操作指令。