实现23条指令的简单流水线
实验任务
1. 实现23条指令的简单流水线
实现支持23条指令的五级流水线RISC-V。23条指令是前面单周期已经实现的,包括:I型运算类指令、R型运算类指令、sw指令、lw指令、分支指令。“简单”流水线是指没有处理冲突。 参考数据通路如图 1。
和单周期数据通路相比,最明显的变化是增加了流水线寄存器。流水线寄存器可以使用DataReg模块实例化,例 1给出了IF-ID流水线寄存器的示例代码,Load端口固定连接“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条指令。
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;