1/*************************************************************************************** 2* Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences 3* Copyright (c) 2020-2021 Peng Cheng Laboratory 4* 5* XiangShan is licensed under Mulan PSL v2. 6* You can use this software according to the terms and conditions of the Mulan PSL v2. 7* You may obtain a copy of Mulan PSL v2 at: 8* http://license.coscl.org.cn/MulanPSL2 9* 10* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 11* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 12* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 13* 14* See the Mulan PSL v2 for more details. 15***************************************************************************************/ 16 17package xiangshan.cache 18 19import chipsalliance.rocketchip.config.Parameters 20import chisel3._ 21import chisel3.util._ 22 23import freechips.rocketchip.tilelink.{TLEdgeOut, TLBundleB, TLMessages, TLPermissions} 24 25import utils.{HasTLDump, XSDebug, XSPerfAccumulate, PerfEventsBundle} 26 27class ProbeReq(implicit p: Parameters) extends DCacheBundle 28{ 29 val source = UInt() 30 val opcode = UInt() 31 val addr = UInt(PAddrBits.W) 32 // TODO: l2 should use vaddr index to probe l1 33 val vaddr = UInt(VAddrBits.W) 34 val param = UInt(TLPermissions.bdWidth.W) 35 val needData = Bool() 36 37 def dump() = { 38 XSDebug("ProbeReq source: %d opcode: %d addr: %x param: %d\n", 39 source, opcode, addr, param) 40 } 41} 42 43class ProbeEntry(implicit p: Parameters) extends DCacheModule { 44 val io = IO(new Bundle { 45 val req = Flipped(Decoupled(new ProbeReq)) 46 val pipe_req = DecoupledIO(new MainPipeReq) 47 val lrsc_locked_block = Input(Valid(UInt())) 48 49 // the block we are probing 50 val block_addr = Output(Valid(UInt())) 51 }) 52 53 val s_invalid :: s_pipe_req :: Nil = Enum(2) 54 55 val state = RegInit(s_invalid) 56 57 val req = Reg(new ProbeReq) 58 59 // assign default values to signals 60 io.req.ready := false.B 61 io.pipe_req.valid := false.B 62 io.pipe_req.bits := DontCare 63 64 io.block_addr.valid := state =/= s_invalid 65 io.block_addr.bits := req.addr 66 67 when (state =/= s_invalid) { 68 XSDebug("state: %d\n", state) 69 } 70 71 when (state =/= s_invalid) { 72 XSDebug("ProbeEntry: state: %d block_addr: %x\n", state, io.block_addr.bits) 73 } 74 75 when (state === s_invalid) { 76 io.req.ready := true.B 77 when (io.req.fire()) { 78 req := io.req.bits 79 state := s_pipe_req 80 } 81 } 82 83 when (state === s_pipe_req) { 84 val lrsc_blocked = io.lrsc_locked_block.valid && io.lrsc_locked_block.bits === req.addr 85 io.pipe_req.valid := !lrsc_blocked 86 87 val pipe_req = io.pipe_req.bits 88 pipe_req := DontCare 89 pipe_req.miss := false.B 90 pipe_req.probe := true.B 91 pipe_req.probe_param := req.param 92 pipe_req.addr := req.addr 93 pipe_req.vaddr := req.vaddr 94 pipe_req.probe_need_data := req.needData 95 96 when (io.pipe_req.fire()) { 97 state := s_invalid 98 } 99 } 100 101 // perfoemance counters 102 XSPerfAccumulate("probe_req", state === s_invalid && io.req.fire()) 103 XSPerfAccumulate("probe_penalty", state =/= s_invalid) 104 XSPerfAccumulate("probe_penalty_blocked_by_lrsc", state === s_pipe_req && io.lrsc_locked_block.valid && io.lrsc_locked_block.bits === req.addr) 105 XSPerfAccumulate("probe_penalty_blocked_by_pipeline", state === s_pipe_req && io.pipe_req.valid && !io.pipe_req.ready) 106} 107 108class ProbeQueue(edge: TLEdgeOut)(implicit p: Parameters) extends DCacheModule with HasTLDump 109{ 110 val io = IO(new Bundle { 111 val mem_probe = Flipped(Decoupled(new TLBundleB(edge.bundle))) 112 val pipe_req = DecoupledIO(new MainPipeReq) 113 val lrsc_locked_block = Input(Valid(UInt())) 114 }) 115 116 val pipe_req_arb = Module(new RRArbiter(new MainPipeReq, cfg.nProbeEntries)) 117 118 // allocate a free entry for incoming request 119 val primary_ready = Wire(Vec(cfg.nProbeEntries, Bool())) 120 val allocate = primary_ready.asUInt.orR 121 val alloc_idx = PriorityEncoder(primary_ready) 122 123 // translate to inner req 124 val req = Wire(new ProbeReq) 125 val alias_addr_frag = io.mem_probe.bits.data(2, 1) // add extra 2 bits from vaddr to get vindex 126 req.source := io.mem_probe.bits.source 127 req.opcode := io.mem_probe.bits.opcode 128 req.addr := io.mem_probe.bits.address 129 if(DCacheAboveIndexOffset > DCacheTagOffset) { 130 // have alias problem, extra alias bits needed for index 131 req.vaddr := Cat( 132 io.mem_probe.bits.address(VAddrBits - 1, DCacheAboveIndexOffset), // dontcare 133 alias_addr_frag(DCacheAboveIndexOffset - DCacheTagOffset - 1, 0), // index 134 io.mem_probe.bits.address(DCacheTagOffset - 1, 0) // index & others 135 ) 136 } else { // no alias problem 137 req.vaddr := io.mem_probe.bits.address 138 } 139 req.param := io.mem_probe.bits.param 140 req.needData := io.mem_probe.bits.data(0) 141 142 io.mem_probe.ready := allocate 143 144 val entries = (0 until cfg.nProbeEntries) map { i => 145 val entry = Module(new ProbeEntry) 146 147 // entry req 148 entry.io.req.valid := (i.U === alloc_idx) && allocate && io.mem_probe.valid 149 primary_ready(i) := entry.io.req.ready 150 entry.io.req.bits := req 151 152 // pipe_req 153 pipe_req_arb.io.in(i) <> entry.io.pipe_req 154 155 entry.io.lrsc_locked_block := io.lrsc_locked_block 156 157 entry 158 } 159 160 io.pipe_req <> pipe_req_arb.io.out 161 162 // print all input/output requests for debug purpose 163 when (io.mem_probe.valid) { 164 // before a probe finishes, L2 should not further issue probes on this block 165 val probe_conflict = VecInit(entries.map(e => e.io.block_addr.valid && e.io.block_addr.bits === io.mem_probe.bits.address)).asUInt.orR 166 assert (!probe_conflict) 167 // for now, we can only deal with ProbeBlock 168 assert (io.mem_probe.bits.opcode === TLMessages.Probe) 169 } 170 171 // debug output 172 when (io.mem_probe.fire()) { 173 XSDebug("mem_probe: ") 174 io.mem_probe.bits.dump 175 } 176 177// when (io.pipe_req.fire()) { 178// io.pipe_req.bits.dump() 179// } 180 181 when (io.lrsc_locked_block.valid) { 182 XSDebug("lrsc_locked_block: %x\n", io.lrsc_locked_block.bits) 183 } 184 val perfinfo = IO(new Bundle(){ 185 val perfEvents = Output(new PerfEventsBundle(5)) 186 }) 187 val perfEvents = Seq( 188 ("dcache_probq_req ", io.pipe_req.fire() ), 189 ("dcache_probq_1/4_valid ", (PopCount(entries.map(e => e.io.block_addr.valid)) < (cfg.nProbeEntries.U/4.U)) ), 190 ("dcache_probq_2/4_valid ", (PopCount(entries.map(e => e.io.block_addr.valid)) > (cfg.nProbeEntries.U/4.U)) & (PopCount(entries.map(e => e.io.block_addr.valid)) <= (cfg.nProbeEntries.U/2.U)) ), 191 ("dcache_probq_3/4_valid ", (PopCount(entries.map(e => e.io.block_addr.valid)) > (cfg.nProbeEntries.U/2.U)) & (PopCount(entries.map(e => e.io.block_addr.valid)) <= (cfg.nProbeEntries.U*3.U/4.U))), 192 ("dcache_probq_4/4_valid ", (PopCount(entries.map(e => e.io.block_addr.valid)) > (cfg.nProbeEntries.U*3.U/4.U)) ), 193 ) 194 195 for (((perf_out,(perf_name,perf)),i) <- perfinfo.perfEvents.perf_events.zip(perfEvents).zipWithIndex) { 196 perf_out.incr_step := RegNext(perf) 197 } 198} 199