现在分析的是arbiter.scala。因为instruction 和data共用一个memory,所以需要用到arbiter来进行仲裁:
package Sodor
{
import chisel3._
import chisel3.util._
import Common._
// arbitrates memory access
class SodorMemArbiter(implicit val conf: SodorConfiguration) extends Module
{
val io = IO(new Bundle
{
// TODO I need to come up with better names... this is too confusing
// from the point of view of the other modules
//instruction memory的端口,是MemPortIo的反向
val imem = Flipped(new MemPortIo(conf.xprlen)) // instruction fetch
//data memory的端口,是MemPortIo的反向
val dmem = Flipped(new MemPortIo(conf.xprlen)) // load/store
//memory的端口,单端,是MemPortIo的正向
val mem = new MemPortIo(conf.xprlen) // the single-ported memory
})
//***************************
//定义一个i1reg的寄存器
val i1reg = Reg(UInt(conf.xprlen.W))
//定义一个d1reg的寄存器
val d1reg = Reg(UInt(conf.xprlen.W))
//定义一个nextdreq的寄存器,初始值为true
val nextdreq = Reg(init = true.B)
//data memory一直ready
io.dmem.req.ready := true.B
//d_fire : when true data request will be put on bus
//当data进行数据请求时,d_fire会为1
val d_fire = Wire(Bool())
//instruction memory的ready看d_fire信号
//我个人觉得这里有错,应该是io.imem.req.ready := !d_fire
//d_fire正确memory此时是data在用,instruction的ready应该为0才对
io.imem.req.ready := d_fire
//***************************
// hook up requests
// 3 cycle FSM on LW , SW , FENCE in exe stage
// HAZ since contention for MEM PORT so next cycle STALL
// CYC 1 : Store inst in reg requested in prev CYC
// make data addr available on MEM PORT
// CYC 2 : Store data in reg to be used in next CYC
// CYC 3 : Default State with data addr on MEM PORT
// nextdreq ensures that data req gets access to bus only
// for one cycle
// alternate between data and instr to avoid starvation
//指令和数据交替提前或写入,防止单方面阻塞太久
//当此时数据请求,且nextdreq为1时,将d_fire置1,并将nextdreq置0,下周期将data的控制信号输
//入到memory中,同时instruction memory的resp_valid由memory的resp_valid决定
when (io.dmem.req.valid && nextdreq)
{
d_fire := true.B
nextdreq := false.B // allow only instr in next cycle
io.imem.resp.valid := io.mem.resp.valid
}
//当此时数据请求,且且nextdreq不为1时,将d_fire置0,并将将nextdreq置1,下周期将inst的控制
//信号输入到memory中,同时instruction memory的resp_valid为0
.elsewhen(io.dmem.req.valid && !nextdreq)
{
d_fire := false.B
nextdreq := true.B // allow any future data request
io.imem.resp.valid := false.B
}
//其他情况是d_fire为0,io.imem.resp.valid 由io.mem.resp.valid决定,是指令取指的情况
.otherwise
{
d_fire := false.B
io.imem.resp.valid := io.mem.resp.valid
}
//输出给memory的控制信号,当d_fire为1时,给data的,当d_fire为0时,给inst的
// SwITCH BET DATA AND INST REQ FOR SINGLE PORT
when (d_fire)
{
io.mem.req.valid := io.dmem.req.valid
io.mem.req.bits.addr := io.dmem.req.bits.addr
io.mem.req.bits.fcn := io.dmem.req.bits.fcn
io.mem.req.bits.typ := io.dmem.req.bits.typ
}
.otherwise
{
io.mem.req.valid := io.imem.req.valid
io.mem.req.bits.addr := io.imem.req.bits.addr
io.mem.req.bits.fcn := io.imem.req.bits.fcn
io.mem.req.bits.typ := io.imem.req.bits.typ
}
//data写给mem的数据
io.mem.req.bits.data := io.dmem.req.bits.data
//data的resp valid由io.mem.resp.valid和io.imem.resp.valid决定,当不是指令回复,同时memory回复
//有效时就是data数据的回复
io.dmem.resp.valid := io.mem.resp.valid && !io.imem.resp.valid
//当nextdreq为0,将data memory请求的数据存入到d1reg中,数据比控制信号慢一个周期
when (!nextdreq){
d1reg := io.mem.resp.bits.data
}
//当指令回复有效,同时数据请求有效,且下一次是数据请求时,将指令回复数据存入i1reg中
when (io.imem.resp.valid && io.dmem.req.valid && nextdreq){
i1reg := io.mem.resp.bits.data
}
//instruction memory的数据需要选择,当instruction回复无效,且数据请求有效,下一个cycle不为数据
//请求时,将i1reg回复,不然将读到的memory data回复,也就是没有指令请求时,保持这个就的值
io.imem.resp.bits.data := Mux( !io.imem.resp.valid && io.dmem.req.valid && !nextdreq , i1reg , io.mem.resp.bits.data )
//data memory的数据请求直接从d1reg中取
io.dmem.resp.bits.data := d1reg
}
}