一、数据通路接口定义
// See LICENSE for license details.
package mini
import chisel3._
import chisel3.util._
import freechips.rocketchip.config.Parameters
object Const {
val PC_START = 0x200
val PC_EVEC = 0x100
}
class DatapathIO(implicit p: Parameters) extends CoreBundle()(p) {
val host = new HostIO
val icache = Flipped(new CacheIO)
val dcache = Flipped(new CacheIO)
val ctrl = Flipped(new ControlSignals)
}
单例对象Const 定义两个常量,PC_START定义为0x200 定义为cpu上电运行的第一条指令地址。
PC_EVEC 0x100定义为 cpu异常运行的指令起始地址。
接着是接口定义,可以看到数据通路有host接口,icache dcache以及control模块接口定义。
二、数据通路流水线寄存器定义
class Datapath(implicit val p: Parameters) extends Module with CoreParams {
val io = IO(new DatapathIO)
val csr = Module(new CSR)
val regFile = Module(new RegFile)
val alu = p(BuildALU)(p)
val immGen = p(BuildImmGen)(p)
val brCond = p(BuildBrCond)(p)
import Control._
/***** Fetch / Execute Registers *****/
val fe_inst = RegInit(Instructions.NOP)
val fe_pc = Reg(UInt())
/***** Execute / Write Back Registers *****/
val ew_inst = RegInit(Instructions.NOP)
val ew_pc = Reg(UInt())
val ew_alu = Reg(UInt())
val csr_in = Reg(UInt())
/****** Control signals *****/
val st_type = Reg(io.ctrl.st_type.cloneType)
val ld_type = Reg(io.ctrl.ld_type.cloneType)
val wb_sel = Reg(io.ctrl.wb_sel.cloneType)
val wb_en = Reg(Bool())
val csr_cmd = Reg(io.ctrl.csr_cmd.cloneType)
val illegal = Reg(Bool())
val pc_check = Reg(Bool())
数据通路首先例化接口,接着例化csr immgen 寄存器文件等模块
下面是取值单元寄存器定义,定义了inst 指令和 pc指针寄存器。
执行阶段寄存器定义,定义执行指令 pc指针 ,ALU计算输出ew_alu, csr模块输入csr_in
控制模块输出的寄存器定义等。
三、取值阶段实现
/****** Fetch *****/
val started = RegNext(reset.toBool)
val stall = !io.icache.resp.valid || !io.dcache.resp.valid
val pc = RegInit(Const.PC_START.U(xlen.W) - 4.U(xlen.W))
val npc = Mux(stall, pc, Mux(csr.io.expt, csr.io.evec,
Mux(io.ctrl.pc_sel === PC_EPC, csr.io.epc,
Mux(io.ctrl.pc_sel === PC_ALU || brCond.io.taken, alu.io.sum >> 1.U << 1.U,
Mux(io.ctrl.pc_sel === PC_0, pc, pc + 4.U)))))
val inst = Mux(started || io.ctrl.inst_kill || brCond.io.taken || csr.io.expt, Instructions.NOP, io.icache.resp.bits.data)
pc := npc
io.icache.req.bits.addr := npc
io.icache.req.bits.data := 0.U
io.icache.req.bits.mask := 0.U
io.icache.req.valid := !stall
io.icache.abort := false.B
// Pipelining
when (!stall) {
fe_pc := pc
fe_inst := inst
}
首先started 表示cpu复位,复位为0的时候cpu开始运行。
stall表示 没有读到cache数据,流水线等待。
pc寄存器定义为 cpu运行起始地址PC_START
npc是下一条指令的计算,如果流水线stall,下一条指令地址不变为pc,异常发生,下一条指令为csr.io_evec,decode 如果是返回指令,下一个是返回地址,如果是跳转指令,下一个是alu计算输出的跳转指令,默认是pc+4
接着npc赋值给pc,同时赋值给icache地址,如果cpu处于stall状态 pc和 inst都不变。
四、数据通路的译码和执行
/****** Execute *****/
// Decode
io.ctrl.inst := fe_inst
// regFile read
val rd_addr = fe_inst(11, 7)
val rs1_addr = fe_inst(19, 15)
val rs2_addr = fe_inst(24, 20)
regFile.io.raddr1 := rs1_addr
regFile.io.raddr2 := rs2_addr
// gen immdeates
immGen.io.inst := fe_inst
immGen.io.sel := io.ctrl.imm_sel
// bypass
val wb_rd_addr = ew_inst(11, 7)
val rs1hazard = wb_en && rs1_addr.orR && (rs1_addr === wb_rd_addr)
val rs2hazard = wb_en && rs2_addr.orR && (rs2_addr === wb_rd_addr)
val rs1 = Mux(wb_sel === WB_ALU && rs1hazard, ew_alu, regFile.io.rdata1)
val rs2 = Mux(wb_sel === WB_ALU && rs2hazard, ew_alu, regFile.io.rdata2)
// ALU operations
alu.io.A := Mux(io.ctrl.A_sel === A_RS1, rs1, fe_pc)
alu.io.B := Mux(io.ctrl.B_sel === B_RS2, rs2, immGen.io.out)
alu.io.alu_op := io.ctrl.alu_op
// Branch condition calc
brCond.io.rs1 := rs1
brCond.io.rs2 := rs2
brCond.io.br_type := io.ctrl.br_type
// D$ access
val daddr = Mux(stall, ew_alu, alu.io.sum) >> 2.U << 2.U
val woffset = alu.io.sum(1) << 4.U | alu.io.sum(0) << 3.U
io.dcache.req.valid := !stall && (io.ctrl.st_type.orR || io.ctrl.ld_type.orR)
io.dcache.req.bits.addr := daddr
io.dcache.req.bits.data := rs2 << woffset
io.dcache.req.bits.mask := MuxLookup(Mux(stall, st_type, io.ctrl.st_type),
"b0000".U, Seq(
ST_SW -> "b1111".U,
ST_SH -> ("b11".U << alu.io.sum(1,0)),
ST_SB -> ("b1".U << alu.io.sum(1,0))))
// Pipelining
when(reset.toBool || !stall && csr.io.expt) {
st_type := 0.U
ld_type := 0.U
wb_en := false.B
csr_cmd := 0.U
illegal := false.B
pc_check := false.B
}.elsewhen(!stall && !csr.io.expt) {
ew_pc := fe_pc
ew_inst := fe_inst
ew_alu := alu.io.out
csr_in := Mux(io.ctrl.imm_sel === IMM_Z, immGen.io.out, rs1)
st_type := io.ctrl.st_type
ld_type := io.ctrl.ld_type
wb_sel := io.ctrl.wb_sel
wb_en := io.ctrl.wb_en
csr_cmd := io.ctrl.csr_cmd
illegal := io.ctrl.illegal
pc_check := io.ctrl.pc_sel === PC_ALU
}
首先把fe_inst 输入给control模块获得控制信号,这一块是指令的decode
接着是赋值rs1 rs2 rd寄存器的地址,读出寄存器
然后根据指令产生imm立即数
在把操作数输入到alu运算之前,通过bypass解决掉寄存器读数据冒险。三级流水线通过bypass可以解决数据冒险,ew阶段,即上条指令要写的寄存器地址与ex阶段指令访问的地址重合,存在读寄存器冒险,需要把ew写寄存器数据ew_alu 短路到 alu输入。
接着alu进行运算,这里把alu中的条件指令判断部分单独拿出来,brcond模块产生。
接着是store指令和load指令赋值地址dcache。
如果出现异常和stall 则需要清空流水线,通常所说的冒泡。
其他情况进入指令写回结果的极端。即ew阶段。
五、指令的结果的写回和数据加载
// Load
val loffset = ew_alu(1) << 4.U | ew_alu(0) << 3.U
val lshift = io.dcache.resp.bits.data >> loffset
val load = MuxLookup(ld_type, io.dcache.resp.bits.data.zext, Seq(
LD_LH -> lshift(15, 0).asSInt, LD_LB -> lshift(7, 0).asSInt,
LD_LHU -> lshift(15, 0).zext, LD_LBU -> lshift(7, 0).zext) )
// CSR access
csr.io.stall := stall
csr.io.in := csr_in
csr.io.cmd := csr_cmd
csr.io.inst := ew_inst
csr.io.pc := ew_pc
csr.io.addr := ew_alu
csr.io.illegal := illegal
csr.io.pc_check := pc_check
csr.io.ld_type := ld_type
csr.io.st_type := st_type
io.host <> csr.io.host
// Regfile Write
val regWrite = MuxLookup(wb_sel, ew_alu.zext, Seq(
WB_MEM -> load,
WB_PC4 -> (ew_pc + 4.U).zext,
WB_CSR -> csr.io.out.zext) ).asUInt
regFile.io.wen := wb_en && !stall && !csr.io.expt
regFile.io.waddr := wb_rd_addr
regFile.io.wdata := regWrite
// Abort store when there's an excpetion
io.dcache.abort := csr.io.expt
if (p(Trace)) {
printf("PC: %x, INST: %x, REG[%d] <- %x\n", ew_pc, ew_inst,
Mux(regFile.io.wen, wb_rd_addr, 0.U),
Mux(regFile.io.wen, regFile.io.wdata, 0.U))
}
}
接着load指令获得从dcache得到的内存数据
csr状态寄存器模块赋值。
寄存器的数据的选择regWrite。
寄存器的写回。
接着跟踪调试打印信息。