提交 3cd542e6 编写于 作者: A Allen

Added ldu and stu.

上级 228d8adf
......@@ -95,6 +95,6 @@ class DCacheResp extends MemBundle
class LSUDMemIO extends MemBundle
{
val req = new DecoupledIO(Vec(memWidth, Valid(new DCacheReq)))
val resp = Flipped(Vec(memWidth, new ValidIO(new DCacheResp)))
val req = new DecoupledIO(new DCacheReq)
val resp = Flipped(new ValidIO(new DCacheResp))
}
......@@ -107,67 +107,16 @@ class L1MetaWriteReq extends L1MetaReadReq {
}
class L1DataReadReq extends DCacheBundle {
// you can choose which beat to read to save power
val rmask = Bits(refillCycles.W))
val way_en = Bits(nWays.W)
val addr = Bits(untagBits.W)
}
// Now, we can write a cache-block in a single cycle
class L1DataWriteReq extends L1DataReadReq {
val wmask = Bits(rowWords.W)
val data = Bits(encRowBits.W)
}
class L1MetadataArray[T <: L1Metadata](onReset: () => T) extends DCacheModule {
val rstVal = onReset()
val io = IO(new Bundle {
val read = Flipped(Decoupled(new L1MetaReadReq))
val write = Flipped(Decoupled(new L1MetaWriteReq))
val resp = Output(Vec(nWays, rstVal.cloneType))
})
val rst_cnt = RegInit(0.U(log2Up(nSets+1).W))
val rst = rst_cnt < nSets.U
val waddr = Mux(rst, rst_cnt, io.write.bits.idx)
val wdata = Mux(rst, rstVal, io.write.bits.data).asUInt
val wmask = Mux(rst || (nWays == 1).B, (-1).asSInt, io.write.bits.way_en.asSInt).asBools
val rmask = Mux(rst || (nWays == 1).B, (-1).asSInt, io.read.bits.way_en.asSInt).asBools
when (rst) { rst_cnt := rst_cnt + 1.U }
val metabits = rstVal.getWidth
val tag_array = SyncReadMem(nSets, Vec(nWays, UInt(metabits.W)))
val wen = rst || io.write.valid
when (wen) {
tag_array.write(waddr, VecInit(Array.fill(nWays)(wdata)), wmask)
}
io.resp := tag_array.read(io.read.bits.idx, io.read.fire()).map(_.asTypeOf(rstVal))
io.read.ready := !wen // so really this could be a 6T RAM
io.write.ready := !rst
def dumpRead() = {
when (io.read.fire()) {
XSDebug("MetaArray Read: idx: %d way_en: %x tag: %x\n",
io.read.bits.idx, io.read.bits.way_en, io.read.bits.tag)
}
}
def dumpWrite() = {
when (io.write.fire()) {
XSDebug("MetaArray Write: idx: %d way_en: %x tag: %x new_tag: %x new_coh: %x\n",
io.write.bits.idx, io.write.bits.way_en, io.write.bits.tag, io.write.bits.data.tag, io.write.bits.data.coh.state)
}
}
def dumpResp() = {
(0 until nWays) map { i =>
XSDebug(s"MetaArray Resp: way: $i tag: %x coh: %x\n",
io.resp(i).tag, io.resp(i).coh.state)
}
}
def dump() = {
dumpRead
dumpWrite
dumpResp
}
val wmask = Vec(refillCycles, Bits(rowWords.W))
val data = Vec(refillCycles, Bits(encRowBits.W))
}
// argument general L1 DCache bundles with memWidth
......@@ -184,7 +133,7 @@ abstract class AbstractDataArray extends DCacheModule {
val io = IO(new DCacheBundle {
val read = Input(Vec(memWidth, Valid(new L1DataReadReq)))
val write = Input(Valid(new L1DataWriteReq))
val resp = Output(Vec(memWidth, Vec(nWays, Bits(encRowBits.W))))
val resp = Output(Vec(memWidth, Vec(nWays, Vec(refillCycles, Bits(encRowBits.W)))))
val nacks = Output(Vec(memWidth, Bool()))
})
......@@ -234,17 +183,19 @@ abstract class AbstractDataArray extends DCacheModule {
class DuplicatedDataArray extends AbstractDataArray
{
val waddr = io.write.bits.addr >> rowOffBits
val waddr = io.write.bits.addr >> blockOffBits
for (j <- 0 until memWidth) {
val raddr = io.read(j).bits.addr >> rowOffBits
val raddr = io.read(j).bits.addr >> blockOffBits
for (w <- 0 until nWays) {
val array = SyncReadMem(nSets * refillCycles, Vec(rowWords, Bits(encDataBits.W)))
when (io.write.bits.way_en(w) && io.write.valid) {
val data = VecInit((0 until rowWords) map (i => io.write.bits.data(encDataBits*(i+1)-1,encDataBits*i)))
array.write(waddr, data, io.write.bits.wmask.asBools)
for (r <- 0 until refillCycles) {
val array = SyncReadMem(nSets, Vec(rowWords, Bits(encDataBits.W)))
when (io.write.bits.way_en(w) && io.write.valid) {
val data = VecInit((0 until rowWords) map (i => io.write.bits.data(encDataBits*(i+1)-1,encDataBits*i)))
array.write(waddr, data, io.write.bits.wmask(r).asBools)
}
io.resp(j)(w)(r) := RegNext(array.read(raddr, io.read(j).bits.way_en(w)
&& io.read(j).bits.rmask(r) && io.read(j).valid).asUInt)
}
io.resp(j)(w) := RegNext(array.read(raddr, io.read(j).bits.way_en(w) && io.read(j).valid).asUInt)
}
io.nacks(j) := false.B
}
......@@ -323,6 +274,85 @@ class BankedDataArray extends AbstractDataArray {
io.nacks := s2_nacks
}
class L1MetadataArray[T <: L1Metadata](onReset: () => T) extends DCacheModule {
val rstVal = onReset()
val io = IO(new Bundle {
val read = Flipped(Decoupled(new L1MetaReadReq))
val write = Flipped(Decoupled(new L1MetaWriteReq))
val resp = Output(Vec(nWays, rstVal.cloneType))
})
val rst_cnt = RegInit(0.U(log2Up(nSets+1).W))
val rst = rst_cnt < nSets.U
val waddr = Mux(rst, rst_cnt, io.write.bits.idx)
val wdata = Mux(rst, rstVal, io.write.bits.data).asUInt
val wmask = Mux(rst || (nWays == 1).B, (-1).asSInt, io.write.bits.way_en.asSInt).asBools
val rmask = Mux(rst || (nWays == 1).B, (-1).asSInt, io.read.bits.way_en.asSInt).asBools
when (rst) { rst_cnt := rst_cnt + 1.U }
val metabits = rstVal.getWidth
val tag_array = SyncReadMem(nSets, Vec(nWays, UInt(metabits.W)))
val wen = rst || io.write.valid
when (wen) {
tag_array.write(waddr, VecInit(Array.fill(nWays)(wdata)), wmask)
}
io.resp := tag_array.read(io.read.bits.idx, io.read.fire()).map(_.asTypeOf(rstVal))
io.read.ready := !wen // so really this could be a 6T RAM
io.write.ready := !rst
def dumpRead() = {
when (io.read.fire()) {
XSDebug("MetaArray Read: idx: %d way_en: %x tag: %x\n",
io.read.bits.idx, io.read.bits.way_en, io.read.bits.tag)
}
}
def dumpWrite() = {
when (io.write.fire()) {
XSDebug("MetaArray Write: idx: %d way_en: %x tag: %x new_tag: %x new_coh: %x\n",
io.write.bits.idx, io.write.bits.way_en, io.write.bits.tag, io.write.bits.data.tag, io.write.bits.data.coh.state)
}
}
def dumpResp() = {
(0 until nWays) map { i =>
XSDebug(s"MetaArray Resp: way: $i tag: %x coh: %x\n",
io.resp(i).tag, io.resp(i).coh.state)
}
}
def dump() = {
dumpRead
dumpWrite
dumpResp
}
}
class DuplicatedMetaArray extends DCacheModule {
val io = IO(new DCacheBundle {
val read = Input(Vec(memWidth, Valid(new L1MetaReadReq)))
val write = Input(Valid(new L1MetaWriteReq))
val resp = Output(Vec(memWidth, Vec(nWays, Vec(refillCycles, Bits(encRowBits.W)))))
val nacks = Output(Vec(memWidth, Bool()))
})
def onReset = L1Metadata(0.U, ClientMetadata.onReset)
val meta = Seq.fill(memWidth) { Module(new L1MetadataArray(onReset _)) }
for (w <- 0 until memWidth) {
meta(w).io.write <> io.write
meta(w).io.read <> io.read(w)
meta(w).io.resp <> io.resp(w)
}
def dump() = {
(0 until memWidth) map { w =>
XSDebug(s"MetaArray $w\n")
meta(w).dump
}
}
}
class DCache extends DCacheModule
......
package xiangshan.mem.cache
import chisel3._
import chisel3.util._
import chisel3.util.experimental.BoringUtils
import xiangshan.mem.{DCacheReq, DCacheResp, LSUDMemIO}
import xiangshan.utils.XSDebug
import bus.tilelink._
import _root_.utils.{Code, RandomReplacement, Transpose}
import xiangshan.mem.MemoryOpConstants
class LoadPipe extends DCacheModule
{
val io = IO(new DCacheBundle{
val lsu = Flipped(new LSUDMemIO)
val data_read = Output(Valid(new L1DataReadReq))
val data_resp = Output(Vec(nWays, Vec(refillCycles, Bits(encRowBits.W))))
val meta_read = Decoupled(new L1MetaReadReq)
val meta_resp = Output(Vec(nWays, rstVal.cloneType))
})
// LSU requests
io.lsu.req.ready := io.meta_read.ready && io.data_read.ready
io.meta_read.bits.valid := io.lsu.req.valid
io.data_read.bits.valid := io.lsu.req.valid
val meta_read = io.meta_read.bits
val data_read = io.data_read.bits
for (w <- 0 until memWidth) {
// Tag read for new requests
meta_read.idx := io.lsu.req.bits(w).bits.addr >> blockOffBits
meta_read.way_en := ~0.U(nWays.W)
meta_read.tag := DontCare
// Data read for new requests
data_read.addr := io.lsu.req.bits(w).bits.addr
data_read.way_en := ~0.U(nWays.W)
}
// Pipeline
// stage 0
val s0_valid = io.lsu.req.fire()
val s0_req = io.lsu.req.bits
assert(!(s0_valid && s0_req.cmd =/= MemoryOpConstants.M_XRD), "LoadPipe only accepts load req")
dump_pipeline_reqs("LoadPipe s0", s0_valid, s0_req, s0_type)
// stage 1
val s1_req = RegNext(s0_req)
val s1_valid = RegNext(s0_valid, init = false.B)
val s1_addr = s1_req.addr
val s1_nack = false.B
dump_pipeline_reqs("LoadPipe s1", s1_valid, s1_req, s1_type)
// tag check
def wayMap[T <: Data](f: Int => T) = VecInit((0 until nWays).map(f))
val s1_tag_eq_way = wayMap((w: Int) => meta_resp(w).tag === (s1_addr >> untagBits)).asUInt
val s1_tag_match_way = wayMap((w: Int) => s1_tag_eq_way(i)(w) && meta(i).io.resp(w).coh.isValid()).asUInt
// stage 2
val s2_req = RegNext(s1_req)
val s2_valid = RegNext(s1_valid(w), init = false.B))
dump_pipeline_reqs("LoadPipe s2", s2_valid, s2_req, s2_type)
val s2_tag_match_way = RegNext(s1_tag_match_way)
val s2_tag_match = s2_tag_match_way.orR
val s2_hit_state = Mux1H(s2_tag_match_way(i), wayMap((w: Int) => RegNext(meta_resp(w).coh)))
val s2_has_permission = s2_hit_state.onAccess(s2_req.cmd)._1
val s2_new_hit_state = s2_hit_state.onAccess(s2_req.cmd)._3
// we not only need permissions
// we also require that state does not change on hit
// thus we require new_hit_state === old_hit_state
//
// If state changes on hit,
// we should treat it as not hit, and let mshr deal with it,
// since we can not write meta data on the main pipeline.
// It's possible that we had permission but state changes on hit:
// eg: write to exclusive but clean block
val s2_hit = s2_tag_match && s2_has_permission && s2_hit_state === s2_new_hit_state && !mshrs.io.block_hit
val s2_nack = Wire(Bool())
val s2_data = Wire(Vec(nWays, UInt(encRowBits.W)))
for (w <- 0 until nWays) {
s2_data(w) := data_resp(w)
}
val s2_data_muxed = Mux1H(s2_tag_match_way, s2_data)
// the index of word in a row, in case rowBits != wordBits
val s2_word_idx = if (rowWords == 1) 0.U else s2_req.addr(log2Up(rowWords*wordBytes)-1, log2Up(wordBytes))
val s2_nack_hit = RegNext(s1_nack)
// Can't allocate MSHR for same set currently being written back
// the same set is busy
val s2_nack_set_busy = s2_valid && mshrs.io.block_hit
// Bank conflict on data arrays
val s2_nack_data = data_resp.nacks
s2_nack := s2_nack_hit || s2_nack_set_busy || s2_nack_data
dump_pipeline_valids("LoadPipe s2", "s2_hit", s2_hit)
dump_pipeline_valids("LoadPipe s2", "s2_nack", s2_nack)
dump_pipeline_valids("LoadPipe s2", "s2_nack_hit", s2_nack_hit)
dump_pipeline_valids("LoadPipe s2", "s2_nack_set_busy", s2_nack_set_busy)
// load data gen
val s2_data_word = s2_data_muxed >> Cat(s2_word_idx, 0.U(log2Ceil(wordBits).W))
val resp = Wire(Valid(new DCacheResp))
for (w <- 0 until memWidth) {
resp.valid := s2_valid
resp.bits.data := s2_data_word
resp.bits.meta := s2_req.meta
resp.bits.nack := s2_nack
}
io.lsu.resp(w) <> resp(w)
when (resp.valid) {
XSDebug(s"DCache resp: data: %x meta: %d nack: %b\n",
resp.data, resp.meta, resp.nack)
}
// -------
// Debug logging functions
def dump_pipeline_reqs(pipeline_stage_name: String, valid: Vec[Bool],
reqs: Vec[DCacheReq], req_type: UInt) = {
val anyValid = valid.reduce(_||_)
when (anyValid) {
(0 until memWidth) map { w =>
when (valid(w)) {
XSDebug(s"$pipeline_stage_name\n")
XSDebug("channel %d: valid: %b \n", w.U, valid(w))
when (req_type === t_replay) {
XSDebug("req_type: replay ")
} .elsewhen (req_type === t_lsu) {
XSDebug("req_type: lsu ")
} .otherwise {
XSDebug("req_type: unknown ")
}
XSDebug("cmd: %x addr: %x data: %x mask: %x meta: %x\n",
reqs(w).cmd, reqs(w).addr, reqs(w).data, reqs(w).mask, reqs(w).meta)
}
}
}
}
def dump_pipeline_valids(pipeline_stage_name: String, signal_name: String, valid: Vec[Bool]) = {
val anyValid = valid.reduce(_||_)
when (anyValid) {
(0 until memWidth) map { w =>
when (valid(w)) {
XSDebug(s"$pipeline_stage_name channel %d: $signal_name\n", w.U)
}
}
}
}
}
//******************************************************************************
// Ported from Rocket-Chip
// See LICENSE.Berkeley and LICENSE.SiFive in Rocket-Chip for license details.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
package xiangshan.mem.cache
import chisel3._
import chisel3.util._
import xiangshan.mem.DCacheReq
import xiangshan.utils.XSDebug
import bus.tilelink._
class DCacheReqInternal extends DCacheReq
with HasDCacheParameters
{
// miss info
val tag_match = Bool()
val old_meta = new L1Metadata
val way_en = UInt(nWays.W)
val sdq_id = UInt(log2Up(cfg.nSDQ).W)
}
class MSHR extends DCacheModule
{
val io = IO(new Bundle {
val id = Input(UInt())
val req_pri_val = Input(Bool())
val req_pri_rdy = Output(Bool())
val req = Input(new DCacheReqInternal)
val idx = Output(Valid(UInt()))
val way = Output(Valid(UInt()))
val tag = Output(Valid(UInt()))
val mem_acquire = Decoupled(new TLBundleA(cfg.busParams))
val mem_grant = Flipped(Decoupled(new TLBundleD(cfg.busParams)))
val mem_finish = Decoupled(new TLBundleE(cfg.busParams))
val refill = Decoupled(new L1DataWriteReq)
val meta_write = Decoupled(new L1MetaWriteReq)
val wb_req = Decoupled(new WritebackReq)
val wb_resp = Input(Bool())
// Replays go through the cache pipeline again
val replay = Decoupled(new DCacheReqInternal)
})
// TODO: Optimize this. We don't want to mess with cache during speculation
// s_refill_req : Make a request for a new cache line
// s_refill_resp : Store the refill response into our buffer
// s_drain_rpq_loads : Drain out loads from the rpq
// : If miss was misspeculated, go to s_invalid
// s_wb_req : Write back the evicted cache line
// s_wb_resp : Finish writing back the evicted cache line
// s_meta_write_req : Write the metadata for new cache lne
// s_meta_write_resp :
val s_invalid :: s_refill_req :: s_refill_resp :: s_wb_req :: s_wb_resp :: s_drain_rpq :: s_meta_write_req :: s_mem_finish :: Nil = Enum(8)
val state = RegInit(s_invalid)
val req = Reg(new DCacheReqInternal)
val req_idx = req.addr(untagBits-1, blockOffBits)
val req_tag = req.addr >> untagBits
val req_block_addr = (req.addr >> blockOffBits) << blockOffBits
val new_coh = RegInit(ClientMetadata.onReset)
val (_, shrink_param, coh_on_clear) = req.old_meta.coh.onCacheControl(M_FLUSH)
val grow_param = new_coh.onAccess(req.cmd)._2
val coh_on_grant = new_coh.onGrant(req.cmd, io.mem_grant.bits.param)
val (_, _, refill_done, refill_address_inc) = TLUtilities.addr_inc(io.mem_grant)
val rpq = Module(new Queue(new DCacheReqInternal, cfg.nRPQ))
rpq.io.enq.valid := io.req_pri_val && io.req_pri_rdy
rpq.io.enq.bits := io.req
rpq.io.deq.ready := false.B
val grantack = Reg(Valid(new TLBundleE(cfg.busParams)))
val refill_ctr = Reg(UInt(log2Up(cacheDataBeats).W))
io.idx.valid := state =/= s_invalid
io.tag.valid := state =/= s_invalid
io.way.valid := state =/= s_invalid
io.idx.bits := req_idx
io.tag.bits := req_tag
io.way.bits := req.way_en
XSDebug("mshr: %d state: %d idx_valid: %b\n", io.id, state, io.idx.valid)
// assign default values to output signals
io.req_pri_rdy := false.B
io.mem_acquire.valid := false.B
io.mem_acquire.bits := DontCare
io.mem_grant.ready := false.B
io.mem_finish.valid := false.B
io.mem_finish.bits := DontCare
io.refill.valid := false.B
io.refill.bits := DontCare
io.meta_write.valid := false.B
io.meta_write.bits := DontCare
io.wb_req.valid := false.B
io.wb_req.bits := DontCare
io.replay.valid := false.B
io.replay.bits := DontCare
def handle_pri_req(old_state: UInt): UInt = {
val new_state = WireInit(old_state)
grantack.valid := false.B
refill_ctr := 0.U
assert(rpq.io.enq.ready)
req := io.req
val old_coh = io.req.old_meta.coh
val needs_wb = old_coh.onCacheControl(M_FLUSH)._1 // does the line we are evicting need to be written back
when (io.req.tag_match) {
val (is_hit, _, coh_on_hit) = old_coh.onAccess(io.req.cmd)
when (is_hit) { // set dirty bit
assert(isWrite(io.req.cmd))
new_coh := coh_on_hit
new_state := s_drain_rpq
} .otherwise { // upgrade permissions
new_coh := old_coh
new_state := s_refill_req
}
} .otherwise { // refill and writeback if necessary
new_coh := ClientMetadata.onReset
when (needs_wb) {
new_state := s_wb_req
} .otherwise {
new_state := s_refill_req
}
}
new_state
}
// --------------------------------------------
// s_invalid: receive requests
when (state === s_invalid) {
io.req_pri_rdy := true.B
when (io.req_pri_val && io.req_pri_rdy) {
state := handle_pri_req(state)
}
}
// --------------------------------------------
// write back
when (state === s_wb_req) {
io.wb_req.valid := true.B
io.wb_req.bits.tag := req.old_meta.tag
io.wb_req.bits.idx := req_idx
io.wb_req.bits.param := shrink_param
io.wb_req.bits.way_en := req.way_en
io.wb_req.bits.source := io.id
io.wb_req.bits.voluntary := true.B
when (io.wb_req.fire()) {
state := s_wb_resp
}
}
when (state === s_wb_resp) {
when (io.wb_resp) {
state := s_refill_req
}
}
// --------------------------------------------
// refill
when (state === s_refill_req) {
io.mem_acquire.valid := true.B
// TODO: Use AcquirePerm if just doing permissions acquire
io.mem_acquire.bits := TLMasterUtilities.AcquireBlock(
params = cfg.busParams,
fromSource = io.id,
toAddress = Cat(req_tag, req_idx) << blockOffBits,
lgSize = (log2Up(cfg.blockBytes)).U,
growPermissions = grow_param)._2
when (io.mem_acquire.fire()) {
state := s_refill_resp
}
}
when (state === s_refill_resp) {
when (TLUtilities.hasData(io.mem_grant.bits)) {
io.mem_grant.ready := io.refill.ready
io.refill.valid := io.mem_grant.valid
io.refill.bits.addr := req_block_addr | (refill_ctr << rowOffBits)
io.refill.bits.way_en := req.way_en
io.refill.bits.wmask := ~(0.U(rowWords.W))
io.refill.bits.data := io.mem_grant.bits.data
when (io.refill.fire()) {
refill_ctr := refill_ctr + 1.U
when (refill_ctr === (cacheDataBeats - 1).U) {
assert(refill_done, "refill not done!")
}
}
} .otherwise {
io.mem_grant.ready := true.B
}
when (refill_done) {
grantack.valid := TLUtilities.isRequest(io.mem_grant.bits)
grantack.bits := TLMasterUtilities.GrantAck(io.mem_grant.bits)
state := s_mem_finish
new_coh := coh_on_grant
}
}
when (state === s_mem_finish) {
io.mem_finish.valid := grantack.valid
io.mem_finish.bits := grantack.bits
when (io.mem_finish.fire()) {
grantack.valid := false.B
state := s_drain_rpq
}
}
// --------------------------------------------
// meta write
when (state === s_meta_write_req) {
io.meta_write.valid := true.B
io.meta_write.bits.idx := req_idx
io.meta_write.bits.data.coh := new_coh
io.meta_write.bits.data.tag := req_tag
io.meta_write.bits.way_en := req.way_en
when (io.meta_write.fire()) {
state := s_invalid
}
}
// --------------------------------------------
// replay
when (state === s_drain_rpq) {
io.replay <> rpq.io.deq
io.replay.bits.way_en := req.way_en
io.replay.bits.addr := Cat(req_tag, req_idx, rpq.io.deq.bits.addr(blockOffBits-1,0))
when (io.replay.fire() && isWrite(rpq.io.deq.bits.cmd)) {
// Set dirty bit
val (is_hit, _, coh_on_hit) = new_coh.onAccess(rpq.io.deq.bits.cmd)
assert(is_hit, "We still don't have permissions for this store")
new_coh := coh_on_hit
}
when (rpq.io.count === 0.U) {
state := s_meta_write_req
}
}
}
class MSHRFile extends DCacheModule
{
val io = IO(new Bundle {
val req = Flipped(Vec(memWidth, Decoupled(new DCacheReqInternal))) // Req from s2 of DCache pipe
val block_hit = Output(Vec(memWidth, Bool()))
val mem_acquire = Decoupled(new TLBundleA(cfg.busParams))
val mem_grant = Flipped(Decoupled(new TLBundleD(cfg.busParams)))
val mem_finish = Decoupled(new TLBundleE(cfg.busParams))
val refill = Decoupled(new L1DataWriteReq)
val meta_write = Decoupled(new L1MetaWriteReq)
val replay = Decoupled(new DCacheReqInternal)
val wb_req = Decoupled(new WritebackReq)
val wb_resp = Input(Bool())
})
val req_idx = OHToUInt(io.req.map(_.valid))
val req = io.req(req_idx)
for (w <- 0 until memWidth)
io.req(w).ready := false.B
val cacheable = true.B
// --------------------
// The MSHR SDQ
val sdq_val = RegInit(0.U(cfg.nSDQ.W))
val sdq_alloc_id = PriorityEncoder(~sdq_val(cfg.nSDQ-1,0))
val sdq_rdy = !sdq_val.andR
val sdq_enq = req.fire() && cacheable && isWrite(req.bits.cmd)
val sdq = Mem(cfg.nSDQ, UInt(wordBits.W))
when (sdq_enq) {
sdq(sdq_alloc_id) := req.bits.data
}
// --------------------
// The LineBuffer Data
def widthMap[T <: Data](f: Int => T) = VecInit((0 until memWidth).map(f))
val idx_matches = Wire(Vec(memWidth, Vec(cfg.nMSHRs, Bool())))
val tag_matches = Wire(Vec(memWidth, Vec(cfg.nMSHRs, Bool())))
val way_matches = Wire(Vec(memWidth, Vec(cfg.nMSHRs, Bool())))
val tag_match = widthMap(w => Mux1H(idx_matches(w), tag_matches(w)))
val idx_match = widthMap(w => idx_matches(w).reduce(_||_))
val way_match = widthMap(w => Mux1H(idx_matches(w), way_matches(w)))
val wb_tag_list = Wire(Vec(cfg.nMSHRs, UInt(tagBits.W)))
val meta_write_arb = Module(new Arbiter(new L1MetaWriteReq, cfg.nMSHRs))
val wb_req_arb = Module(new Arbiter(new WritebackReq, cfg.nMSHRs))
val replay_arb = Module(new Arbiter(new DCacheReqInternal, cfg.nMSHRs))
val refill_arb = Module(new Arbiter(new L1DataWriteReq, cfg.nMSHRs))
io.mem_grant.ready := false.B
val mshr_alloc_idx = Wire(UInt())
val pri_rdy = WireInit(false.B)
val pri_val = req.valid && sdq_rdy && cacheable && !idx_match(req_idx)
val mshrs = (0 until cfg.nMSHRs) map { i =>
val mshr = Module(new MSHR)
mshr.io.id := i.U(log2Up(cfg.nMSHRs).W)
for (w <- 0 until memWidth) {
idx_matches(w)(i) := mshr.io.idx.valid && mshr.io.idx.bits === io.req(w).bits.addr(untagBits-1,blockOffBits)
tag_matches(w)(i) := mshr.io.tag.valid && mshr.io.tag.bits === io.req(w).bits.addr >> untagBits
way_matches(w)(i) := mshr.io.way.valid && mshr.io.way.bits === io.req(w).bits.way_en
when (idx_matches(w)(i)) {
XSDebug(s"mshr: $i channel: $w idx_match\n")
}
when (tag_matches(w)(i)) {
XSDebug(s"mshr: $i channel: $w tag_match\n")
}
when (way_matches(w)(i)) {
XSDebug(s"mshr: $i channel: $w way_match\n")
}
}
wb_tag_list(i) := mshr.io.wb_req.bits.tag
mshr.io.req_pri_val := (i.U === mshr_alloc_idx) && pri_val
when (i.U === mshr_alloc_idx) {
pri_rdy := mshr.io.req_pri_rdy
}
mshr.io.req := req.bits
mshr.io.req.sdq_id := sdq_alloc_id
mshr.io.wb_resp := io.wb_resp
meta_write_arb.io.in(i) <> mshr.io.meta_write
wb_req_arb.io.in(i) <> mshr.io.wb_req
replay_arb.io.in(i) <> mshr.io.replay
refill_arb.io.in(i) <> mshr.io.refill
mshr.io.mem_grant.valid := false.B
mshr.io.mem_grant.bits := DontCare
when (io.mem_grant.bits.source === i.U) {
mshr.io.mem_grant <> io.mem_grant
}
mshr
}
mshr_alloc_idx := RegNext(PriorityEncoder(mshrs.map(m=>m.io.req_pri_rdy)))
io.meta_write <> meta_write_arb.io.out
io.wb_req <> wb_req_arb.io.out
TLArbiter.lowestFromSeq(io.mem_acquire, mshrs.map(_.io.mem_acquire))
TLArbiter.lowestFromSeq(io.mem_finish, mshrs.map(_.io.mem_finish))
val mmio_rdy = true.B
for (w <- 0 until memWidth) {
io.req(w).ready := (w.U === req_idx) &&
Mux(!cacheable, mmio_rdy, sdq_rdy && pri_rdy)
io.block_hit(w) := idx_match(w)
}
io.refill <> refill_arb.io.out
val free_sdq = io.replay.fire() && isWrite(io.replay.bits.cmd)
io.replay <> replay_arb.io.out
io.replay.bits.data := sdq(replay_arb.io.out.bits.sdq_id)
when (io.replay.valid || sdq_enq) {
sdq_val := sdq_val & ~(UIntToOH(replay_arb.io.out.bits.sdq_id) & Fill(cfg.nSDQ, free_sdq)) |
PriorityEncoderOH(~sdq_val(cfg.nSDQ-1,0)) & Fill(cfg.nSDQ, sdq_enq)
}
// print all input/output requests for debug purpose
// print req
XSDebug(req.fire(), "req cmd: %x addr: %x data: %x mask: %x meta: %x tag_match: %b old_coh: %d old_tag: %x way_en: %x\n",
req.bits.cmd, req.bits.addr, req.bits.data, req.bits.mask, req.bits.meta,
req.bits.tag_match, req.bits.old_meta.coh.state, req.bits.old_meta.tag, req.bits.way_en)
// block hit
(0 until memWidth) map { w =>
XSDebug(io.block_hit(w), "channel %d req block hit\n", w.U)
}
// print refill
XSDebug(io.refill.fire(), "refill addr %x data: %x wmask: %x way_en: %x\n",
io.refill.bits.addr, io.refill.bits.data,
io.refill.bits.wmask, io.refill.bits.way_en)
// print meta_write
XSDebug(io.meta_write.fire(), "meta_write idx %x way_en: %x old_tag: %x new_coh: %d new_tag: %x\n",
io.meta_write.bits.idx, io.meta_write.bits.way_en,
io.meta_write.bits.data.coh.state, io.meta_write.bits.data.tag,
io.meta_write.bits.tag)
// print replay
XSDebug(io.replay.fire(), "replay cmd: %x addr: %x data: %x mask: %x meta: %x tag_match: %b old_coh: %d old_tag: %x way_en: %x\n",
io.replay.bits.cmd, io.replay.bits.addr, io.replay.bits.data, io.replay.bits.mask, io.replay.bits.meta,
io.replay.bits.tag_match, io.replay.bits.old_meta.coh.state, io.replay.bits.old_meta.tag, io.replay.bits.way_en)
// print wb_req
XSDebug(io.wb_req.fire(), "wb_req idx %x tag: %x source: %d param: %x way_en: %x voluntary: %b\n",
io.wb_req.bits.idx, io.wb_req.bits.tag,
io.wb_req.bits.source, io.wb_req.bits.param,
io.wb_req.bits.way_en, io.wb_req.bits.voluntary)
// print tilelink messages
/*
XSDebug.exec(io.mem_acquire.fire(), io.mem_acquire.bits.dump)
XSDebug.exec(io.mem_grant.fire(), io.mem_grant.bits.dump)
XSDebug.exec(io.mem_finish.fire(), io.mem_finish.bits.dump)
*/
}
package xiangshan.mem.cache
import chisel3._
import chisel3.util._
import chisel3.util.experimental.BoringUtils
import xiangshan.mem.{DCacheReq, DCacheResp, LSUDMemIO}
import xiangshan.utils.XSDebug
import bus.tilelink._
import _root_.utils.{Code, RandomReplacement, Transpose}
import xiangshan.mem.MemoryOpConstants
class StorePipe extends DCacheModule
{
val io = IO(new DCacheBundle{
val lsu = Flipped(new LSUDMemIO)
val data_write = Output(Valid(new L1DataWriteReq))
val data_resp = Output(Vec(nWays, Vec(refillCycles, Bits(encRowBits.W))))
val meta_read = Decoupled(new L1MetaReadReq)
val meta_resp = Output(Vec(nWays, rstVal.cloneType))
})
// LSU requests
io.lsu.req.ready := io.meta_read.ready
io.meta_read.bits.valid := io.lsu.req.valid
val meta_read = io.meta_read.bits
val data_read = io.data_read.bits
for (w <- 0 until memWidth) {
// Tag read for new requests
meta_read.idx := io.lsu.req.bits(w).bits.addr >> blockOffBits
meta_read.way_en := ~0.U(nWays.W)
meta_read.tag := DontCare
}
// Pipeline
// stage 0
val s0_valid = io.lsu.req.fire()
val s0_req = io.lsu.req.bits
assert(!(s0_valid && s0_req.cmd =/= MemoryOpConstants.M_XWR), "StorePipe only accepts store req")
dump_pipeline_reqs("StorePipe s0", s0_valid, s0_req, s0_type)
// stage 1
val s1_req = RegNext(s0_req)
val s1_valid = RegNext(s0_valid, init = false.B)
val s1_addr = s1_req.addr
val s1_nack = false.B
dump_pipeline_reqs("StorePipe s1", s1_valid, s1_req, s1_type)
// tag check
def wayMap[T <: Data](f: Int => T) = VecInit((0 until nWays).map(f))
val s1_tag_eq_way = wayMap((w: Int) => meta_resp(w).tag === (s1_addr >> untagBits)).asUInt
val s1_tag_match_way = wayMap((w: Int) => s1_tag_eq_way(i)(w) && meta(i).io.resp(w).coh.isValid()).asUInt
// stage 2
val s2_req = RegNext(s1_req)
val s2_valid = RegNext(s1_valid(w), init = false.B))
dump_pipeline_reqs("StorePipe s2", s2_valid, s2_req, s2_type)
val s2_tag_match_way = RegNext(s1_tag_match_way)
val s2_tag_match = s2_tag_match_way.orR
val s2_hit_state = Mux1H(s2_tag_match_way(i), wayMap((w: Int) => RegNext(meta_resp(w).coh)))
val s2_has_permission = s2_hit_state.onAccess(s2_req.cmd)._1
val s2_new_hit_state = s2_hit_state.onAccess(s2_req.cmd)._3
// we not only need permissions
// we also require that state does not change on hit
// thus we require new_hit_state === old_hit_state
//
// If state changes on hit,
// we should treat it as not hit, and let mshr deal with it,
// since we can not write meta data on the main pipeline.
// It's possible that we had permission but state changes on hit:
// eg: write to exclusive but clean block
val s2_hit = s2_tag_match && s2_has_permission && s2_hit_state === s2_new_hit_state && !mshrs.io.block_hit
val s2_nack = Wire(Bool())
val s2_nack_hit = RegNext(s1_nack)
// Can't allocate MSHR for same set currently being written back
// the same set is busy
val s2_nack_set_busy = s2_valid && mshrs.io.block_hit
s2_nack := s2_nack_hit || s2_nack_set_busy
// write dcache if hit
io.meta_write.valid := s2_valid && s2_hit
io.meta_write.bits := s2_req
dump_pipeline_valids("StorePipe s2", "s2_hit", s2_hit)
dump_pipeline_valids("StorePipe s2", "s2_nack", s2_nack)
dump_pipeline_valids("StorePipe s2", "s2_nack_hit", s2_nack_hit)
dump_pipeline_valids("StorePipe s2", "s2_nack_set_busy", s2_nack_set_busy)
val resp = Wire(Valid(new DCacheResp))
for (w <- 0 until memWidth) {
resp.valid := s2_valid
resp.bits.data := DontCare
resp.bits.meta := s2_req.meta
resp.bits.nack := s2_nack
}
io.lsu.resp(w) <> resp(w)
when (resp.valid) {
XSDebug(s"StorePipe resp: meta: %d nack: %b\n",
resp.meta, resp.nack)
}
// -------
// Debug logging functions
def dump_pipeline_reqs(pipeline_stage_name: String, valid: Vec[Bool],
reqs: Vec[DCacheReq], req_type: UInt) = {
val anyValid = valid.reduce(_||_)
when (anyValid) {
(0 until memWidth) map { w =>
when (valid(w)) {
XSDebug(s"$pipeline_stage_name\n")
XSDebug("channel %d: valid: %b \n", w.U, valid(w))
when (req_type === t_replay) {
XSDebug("req_type: replay ")
} .elsewhen (req_type === t_lsu) {
XSDebug("req_type: lsu ")
} .otherwise {
XSDebug("req_type: unknown ")
}
XSDebug("cmd: %x addr: %x data: %x mask: %x meta: %x\n",
reqs(w).cmd, reqs(w).addr, reqs(w).data, reqs(w).mask, reqs(w).meta)
}
}
}
}
def dump_pipeline_valids(pipeline_stage_name: String, signal_name: String, valid: Vec[Bool]) = {
val anyValid = valid.reduce(_||_)
when (anyValid) {
(0 until memWidth) map { w =>
when (valid(w)) {
XSDebug(s"$pipeline_stage_name channel %d: $signal_name\n", w.U)
}
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册