1*6c106319Sxu_zh/*************************************************************************************** 2*6c106319Sxu_zh* Copyright (c) 2024 Beijing Institute of Open Source Chip (BOSC) 3*6c106319Sxu_zh* Copyright (c) 2020-2024 Institute of Computing Technology, Chinese Academy of Sciences 4*6c106319Sxu_zh* Copyright (c) 2020-2021 Peng Cheng Laboratory 5*6c106319Sxu_zh* 6*6c106319Sxu_zh* XiangShan is licensed under Mulan PSL v2. 7*6c106319Sxu_zh* You can use this software according to the terms and conditions of the Mulan PSL v2. 8*6c106319Sxu_zh* You may obtain a copy of Mulan PSL v2 at: 9*6c106319Sxu_zh* http://license.coscl.org.cn/MulanPSL2 10*6c106319Sxu_zh* 11*6c106319Sxu_zh* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 12*6c106319Sxu_zh* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 13*6c106319Sxu_zh* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 14*6c106319Sxu_zh* 15*6c106319Sxu_zh* See the Mulan PSL v2 for more details. 16*6c106319Sxu_zh***************************************************************************************/ 17*6c106319Sxu_zh 18*6c106319Sxu_zhpackage xiangshan.frontend.icache 19*6c106319Sxu_zh 20*6c106319Sxu_zhimport annotation.unused 21*6c106319Sxu_zhimport chisel3._ 22*6c106319Sxu_zhimport chisel3.util._ 23*6c106319Sxu_zhimport freechips.rocketchip.diplomacy._ 24*6c106319Sxu_zhimport freechips.rocketchip.regmapper._ 25*6c106319Sxu_zhimport freechips.rocketchip.tilelink._ 26*6c106319Sxu_zhimport org.chipsalliance.cde.config.Parameters 27*6c106319Sxu_zhimport utils._ 28*6c106319Sxu_zh 29*6c106319Sxu_zhcase class L1ICacheCtrlParams( 30*6c106319Sxu_zh address: AddressSet, 31*6c106319Sxu_zh regWidth: Int, 32*6c106319Sxu_zh beatBytes: Int = 8 33*6c106319Sxu_zh) { 34*6c106319Sxu_zh def regBytes: Int = regWidth / 8 35*6c106319Sxu_zh 36*6c106319Sxu_zh def eccctrlOffset: Int = 0 37*6c106319Sxu_zh def ecciaddrOffset: Int = eccctrlOffset + regBytes 38*6c106319Sxu_zh} 39*6c106319Sxu_zh 40*6c106319Sxu_zhclass ICacheCtrlUnitIO(implicit p: Parameters) extends ICacheBundle { 41*6c106319Sxu_zh // ecc control 42*6c106319Sxu_zh val ecc_enable: Bool = Output(Bool()) 43*6c106319Sxu_zh // ecc inject 44*6c106319Sxu_zh val injecting: Bool = Output(Bool()) 45*6c106319Sxu_zh val metaRead: DecoupledIO[ICacheReadBundle] = DecoupledIO(new ICacheReadBundle) 46*6c106319Sxu_zh val metaReadResp: ICacheMetaRespBundle = Input(new ICacheMetaRespBundle) 47*6c106319Sxu_zh val metaWrite: DecoupledIO[ICacheMetaWriteBundle] = DecoupledIO(new ICacheMetaWriteBundle) 48*6c106319Sxu_zh val dataWrite: DecoupledIO[ICacheDataWriteBundle] = DecoupledIO(new ICacheDataWriteBundle) 49*6c106319Sxu_zh} 50*6c106319Sxu_zh 51*6c106319Sxu_zh// currently for ECC control only 52*6c106319Sxu_zhclass ICacheCtrlUnit(params: L1ICacheCtrlParams)(implicit p: Parameters) extends LazyModule { 53*6c106319Sxu_zh lazy val module = new ICacheCtrlUnitImp(this) 54*6c106319Sxu_zh 55*6c106319Sxu_zh // register tilelink node 56*6c106319Sxu_zh val device: SimpleDevice = new SimpleDevice("L1ICacheCtrl", Seq("xiangshan,l1icache_ctrl")) 57*6c106319Sxu_zh 58*6c106319Sxu_zh val node: TLRegisterNode = TLRegisterNode( 59*6c106319Sxu_zh address = Seq(params.address), 60*6c106319Sxu_zh device = device, 61*6c106319Sxu_zh beatBytes = params.beatBytes, 62*6c106319Sxu_zh concurrency = 1 63*6c106319Sxu_zh ) 64*6c106319Sxu_zh 65*6c106319Sxu_zh class ICacheCtrlUnitImp(wrapper: LazyModule) extends LazyModuleImp(wrapper) with HasICacheParameters { 66*6c106319Sxu_zh val io: ICacheCtrlUnitIO = IO(new ICacheCtrlUnitIO) 67*6c106319Sxu_zh 68*6c106319Sxu_zh // eccctrl.ierror: inject error code 69*6c106319Sxu_zh private def nInjError: Int = 8 70*6c106319Sxu_zh private object eccctrlInjError extends NamedUInt(log2Up(nInjError)) { 71*6c106319Sxu_zh def notEnabled: UInt = 0.U(width.W) // try to inject when ECC check is not enabled 72*6c106319Sxu_zh def targetInvalid: UInt = 1.U(width.W) // try to inject to invalid(rsvd) eccctrl.itarget 73*6c106319Sxu_zh def notFound: UInt = 2.U(width.W) // try to inject to ecciaddr.paddr does not exist in ICache 74*6c106319Sxu_zh @unused 75*6c106319Sxu_zh def rsvd3: UInt = 3.U(width.W) 76*6c106319Sxu_zh @unused 77*6c106319Sxu_zh def rsvd4: UInt = 4.U(width.W) 78*6c106319Sxu_zh @unused 79*6c106319Sxu_zh def rsvd5: UInt = 5.U(width.W) 80*6c106319Sxu_zh @unused 81*6c106319Sxu_zh def rsvd6: UInt = 6.U(width.W) 82*6c106319Sxu_zh @unused 83*6c106319Sxu_zh def rsvd7: UInt = 7.U(width.W) 84*6c106319Sxu_zh } 85*6c106319Sxu_zh // eccctrl.istatus: inject status 86*6c106319Sxu_zh private def nInjStatus: Int = 8 87*6c106319Sxu_zh private object eccctrlInjStatus extends NamedUInt(log2Up(nInjStatus)) { 88*6c106319Sxu_zh def idle: UInt = 0.U(width.W) 89*6c106319Sxu_zh def working: UInt = 1.U(width.W) 90*6c106319Sxu_zh def injected: UInt = 2.U(width.W) 91*6c106319Sxu_zh def error: UInt = 7.U(width.W) 92*6c106319Sxu_zh @unused 93*6c106319Sxu_zh def rsvd3: UInt = 3.U(width.W) 94*6c106319Sxu_zh @unused 95*6c106319Sxu_zh def rsvd4: UInt = 4.U(width.W) 96*6c106319Sxu_zh @unused 97*6c106319Sxu_zh def rsvd5: UInt = 5.U(width.W) 98*6c106319Sxu_zh @unused 99*6c106319Sxu_zh def rsvd6: UInt = 6.U(width.W) 100*6c106319Sxu_zh } 101*6c106319Sxu_zh // eccctrl.itarget: inject target 102*6c106319Sxu_zh private def nInjTarget: Int = 4 103*6c106319Sxu_zh private object eccctrlInjTarget extends NamedUInt(log2Up(nInjTarget)) { 104*6c106319Sxu_zh def metaArray: UInt = 0.U(width.W) 105*6c106319Sxu_zh def dataArray: UInt = 2.U(width.W) 106*6c106319Sxu_zh @unused 107*6c106319Sxu_zh def rsvd1: UInt = 1.U(width.W) 108*6c106319Sxu_zh @unused 109*6c106319Sxu_zh def rsvd3: UInt = 3.U(width.W) 110*6c106319Sxu_zh } 111*6c106319Sxu_zh private class eccctrlBundle extends Bundle { 112*6c106319Sxu_zh val ierror: UInt = eccctrlInjError() // inject error code, read-only, valid only when istatus === error 113*6c106319Sxu_zh val istatus: UInt = eccctrlInjStatus() // inject status, read-only 114*6c106319Sxu_zh val itarget: UInt = eccctrlInjTarget() // inject target 115*6c106319Sxu_zh val inject: Bool = Bool() // request to inject, write-only, read 0 116*6c106319Sxu_zh val enable: Bool = Bool() // enable ECC 117*6c106319Sxu_zh } 118*6c106319Sxu_zh private object eccctrlBundle { 119*6c106319Sxu_zh def default: eccctrlBundle = { 120*6c106319Sxu_zh val x = Wire(new eccctrlBundle) 121*6c106319Sxu_zh x.ierror := eccctrlInjError.notEnabled 122*6c106319Sxu_zh x.istatus := eccctrlInjStatus.idle 123*6c106319Sxu_zh x.itarget := eccctrlInjTarget.metaArray 124*6c106319Sxu_zh x.inject := false.B 125*6c106319Sxu_zh x.enable := true.B 126*6c106319Sxu_zh x 127*6c106319Sxu_zh } 128*6c106319Sxu_zh } 129*6c106319Sxu_zh 130*6c106319Sxu_zh private class ecciaddrBundle extends Bundle { 131*6c106319Sxu_zh val paddr: UInt = UInt(PAddrBits.W) // inject position physical address 132*6c106319Sxu_zh } 133*6c106319Sxu_zh private object ecciaddrBundle { 134*6c106319Sxu_zh def default: ecciaddrBundle = { 135*6c106319Sxu_zh val x = Wire(new ecciaddrBundle) 136*6c106319Sxu_zh x.paddr := 0.U 137*6c106319Sxu_zh x 138*6c106319Sxu_zh } 139*6c106319Sxu_zh } 140*6c106319Sxu_zh 141*6c106319Sxu_zh private val eccctrl = RegInit(eccctrlBundle.default) 142*6c106319Sxu_zh private val ecciaddr = RegInit(ecciaddrBundle.default) 143*6c106319Sxu_zh 144*6c106319Sxu_zh // sanity check 145*6c106319Sxu_zh require(params.regWidth >= eccctrl.asUInt.getWidth) 146*6c106319Sxu_zh require(params.regWidth >= ecciaddr.asUInt.getWidth) 147*6c106319Sxu_zh 148*6c106319Sxu_zh // control signal 149*6c106319Sxu_zh io.ecc_enable := eccctrl.enable 150*6c106319Sxu_zh io.injecting := eccctrl.istatus === eccctrlInjStatus.working 151*6c106319Sxu_zh 152*6c106319Sxu_zh // inject position 153*6c106319Sxu_zh private val ivirIdx = get_idx(ecciaddr.paddr) 154*6c106319Sxu_zh private val iphyTag = get_tag(ecciaddr.paddr) 155*6c106319Sxu_zh private val iwaymask = RegInit(0.U(nWays.W)) // read from metaArray, valid after istate === is_readMetaResp 156*6c106319Sxu_zh 157*6c106319Sxu_zh // inject FSM 158*6c106319Sxu_zh private val is_idle :: is_readMetaReq :: is_readMetaResp :: is_writeMeta :: is_writeData :: Nil = 159*6c106319Sxu_zh Enum(5) 160*6c106319Sxu_zh private val istate = RegInit(is_idle) 161*6c106319Sxu_zh 162*6c106319Sxu_zh io.metaRead.valid := istate === is_readMetaReq 163*6c106319Sxu_zh io.metaRead.bits.isDoubleLine := false.B // we inject into first cacheline and ignore the rest port 164*6c106319Sxu_zh io.metaRead.bits.vSetIdx := VecInit(Seq.fill(PortNumber)(ivirIdx)) 165*6c106319Sxu_zh io.metaRead.bits.waymask := VecInit(Seq.fill(PortNumber)(VecInit(Seq.fill(nWays)(false.B)))) // dontcare 166*6c106319Sxu_zh io.metaRead.bits.blkOffset := 0.U(blockBits.W) // dontcare 167*6c106319Sxu_zh 168*6c106319Sxu_zh io.metaWrite.valid := istate === is_writeMeta 169*6c106319Sxu_zh io.metaWrite.bits.generate( 170*6c106319Sxu_zh tag = iphyTag, 171*6c106319Sxu_zh idx = ivirIdx, 172*6c106319Sxu_zh waymask = iwaymask, 173*6c106319Sxu_zh bankIdx = ivirIdx(0), 174*6c106319Sxu_zh poison = true.B 175*6c106319Sxu_zh ) 176*6c106319Sxu_zh 177*6c106319Sxu_zh io.dataWrite.valid := istate === is_writeData 178*6c106319Sxu_zh io.dataWrite.bits.generate( 179*6c106319Sxu_zh data = 0.U, // inject poisoned data, don't care actual data 180*6c106319Sxu_zh idx = ivirIdx, 181*6c106319Sxu_zh waymask = iwaymask, 182*6c106319Sxu_zh bankIdx = ivirIdx(0), 183*6c106319Sxu_zh poison = true.B 184*6c106319Sxu_zh ) 185*6c106319Sxu_zh 186*6c106319Sxu_zh switch(istate) { 187*6c106319Sxu_zh is(is_idle) { 188*6c106319Sxu_zh when(eccctrl.istatus === eccctrlInjStatus.working) { 189*6c106319Sxu_zh // we need to read meta first to get waymask, whether itarget is metaArray or dataArray 190*6c106319Sxu_zh istate := is_readMetaReq 191*6c106319Sxu_zh } 192*6c106319Sxu_zh } 193*6c106319Sxu_zh is(is_readMetaReq) { 194*6c106319Sxu_zh when(io.metaRead.fire) { 195*6c106319Sxu_zh istate := is_readMetaResp 196*6c106319Sxu_zh } 197*6c106319Sxu_zh } 198*6c106319Sxu_zh is(is_readMetaResp) { 199*6c106319Sxu_zh // metaArray ensures resp is valid one cycle after req 200*6c106319Sxu_zh val waymask = VecInit((0 until nWays).map { w => 201*6c106319Sxu_zh io.metaReadResp.entryValid.head(w) && io.metaReadResp.tags.head(w) === iphyTag 202*6c106319Sxu_zh }).asUInt 203*6c106319Sxu_zh iwaymask := waymask 204*6c106319Sxu_zh when(!waymask.orR) { 205*6c106319Sxu_zh // not hit, refuse to inject 206*6c106319Sxu_zh istate := is_idle 207*6c106319Sxu_zh eccctrl.istatus := eccctrlInjStatus.error 208*6c106319Sxu_zh eccctrl.ierror := eccctrlInjError.notFound 209*6c106319Sxu_zh }.otherwise { 210*6c106319Sxu_zh istate := Mux(eccctrl.itarget === eccctrlInjTarget.metaArray, is_writeMeta, is_writeData) 211*6c106319Sxu_zh } 212*6c106319Sxu_zh } 213*6c106319Sxu_zh is(is_writeMeta) { 214*6c106319Sxu_zh when(io.metaWrite.fire) { 215*6c106319Sxu_zh istate := is_idle 216*6c106319Sxu_zh eccctrl.istatus := eccctrlInjStatus.injected 217*6c106319Sxu_zh } 218*6c106319Sxu_zh } 219*6c106319Sxu_zh is(is_writeData) { 220*6c106319Sxu_zh when(io.dataWrite.fire) { 221*6c106319Sxu_zh istate := is_idle 222*6c106319Sxu_zh eccctrl.istatus := eccctrlInjStatus.injected 223*6c106319Sxu_zh } 224*6c106319Sxu_zh } 225*6c106319Sxu_zh } 226*6c106319Sxu_zh 227*6c106319Sxu_zh private def eccctrlRegDesc: RegFieldDesc = 228*6c106319Sxu_zh RegFieldDesc( 229*6c106319Sxu_zh name = s"ecc_control", 230*6c106319Sxu_zh desc = s"ECC control", 231*6c106319Sxu_zh group = Option(s"ecc_control"), 232*6c106319Sxu_zh groupDesc = Option(s"ECC Control"), 233*6c106319Sxu_zh reset = Option(0) 234*6c106319Sxu_zh ) 235*6c106319Sxu_zh 236*6c106319Sxu_zh private def ecciaddrRegDesc: RegFieldDesc = 237*6c106319Sxu_zh RegFieldDesc( 238*6c106319Sxu_zh name = s"ecc_iaddr", 239*6c106319Sxu_zh desc = s"ECC Inject Address", 240*6c106319Sxu_zh group = Option(s"ecc_iaddr"), 241*6c106319Sxu_zh groupDesc = Option(s"ECC Inject Address"), 242*6c106319Sxu_zh reset = Option(0) 243*6c106319Sxu_zh ) 244*6c106319Sxu_zh 245*6c106319Sxu_zh private def eccctrlRegField(x: eccctrlBundle): RegField = 246*6c106319Sxu_zh RegField( 247*6c106319Sxu_zh params.regWidth, 248*6c106319Sxu_zh RegReadFn { ready => 249*6c106319Sxu_zh val res = WireInit(x) 250*6c106319Sxu_zh res.inject := false.B // read always 0 251*6c106319Sxu_zh when(ready) { 252*6c106319Sxu_zh // if istatus is injected or error, clear it after read 253*6c106319Sxu_zh when(x.istatus === eccctrlInjStatus.injected || x.istatus === eccctrlInjStatus.error) { 254*6c106319Sxu_zh x.istatus := eccctrlInjStatus.idle 255*6c106319Sxu_zh x.ierror := eccctrlInjError.notEnabled 256*6c106319Sxu_zh } 257*6c106319Sxu_zh } 258*6c106319Sxu_zh // always read valid 259*6c106319Sxu_zh (true.B, res.asUInt) 260*6c106319Sxu_zh }, 261*6c106319Sxu_zh RegWriteFn { (valid, data) => 262*6c106319Sxu_zh when(valid) { 263*6c106319Sxu_zh val req = data.asTypeOf(new eccctrlBundle) 264*6c106319Sxu_zh x.enable := req.enable 265*6c106319Sxu_zh when(req.inject && x.istatus === eccctrlInjStatus.idle) { 266*6c106319Sxu_zh // if istatus is not idle, ignore the inject request 267*6c106319Sxu_zh when(req.enable === false.B) { 268*6c106319Sxu_zh // check if enable is not valid 269*6c106319Sxu_zh x.istatus := eccctrlInjStatus.error 270*6c106319Sxu_zh x.ierror := eccctrlInjError.notEnabled 271*6c106319Sxu_zh }.elsewhen(req.itarget =/= eccctrlInjTarget.metaArray && req.itarget =/= eccctrlInjTarget.dataArray) { 272*6c106319Sxu_zh // check if itarget is not valid 273*6c106319Sxu_zh x.istatus := eccctrlInjStatus.error 274*6c106319Sxu_zh x.ierror := eccctrlInjError.targetInvalid 275*6c106319Sxu_zh }.otherwise { 276*6c106319Sxu_zh x.istatus := eccctrlInjStatus.working 277*6c106319Sxu_zh } 278*6c106319Sxu_zh } 279*6c106319Sxu_zh x.itarget := req.itarget 280*6c106319Sxu_zh // istatus is read-only, ignore req.istatus 281*6c106319Sxu_zh // ierror is read-only, ignore req.ierror 282*6c106319Sxu_zh } 283*6c106319Sxu_zh // always ready to write 284*6c106319Sxu_zh true.B 285*6c106319Sxu_zh }, 286*6c106319Sxu_zh eccctrlRegDesc 287*6c106319Sxu_zh ) 288*6c106319Sxu_zh 289*6c106319Sxu_zh private def ecciaddrRegField(x: ecciaddrBundle): RegField = 290*6c106319Sxu_zh RegField(params.regWidth, x.asUInt, ecciaddrRegDesc) 291*6c106319Sxu_zh 292*6c106319Sxu_zh node.regmap( 293*6c106319Sxu_zh params.eccctrlOffset -> Seq(eccctrlRegField(eccctrl)), 294*6c106319Sxu_zh params.ecciaddrOffset -> Seq(ecciaddrRegField(ecciaddr)) 295*6c106319Sxu_zh ) 296*6c106319Sxu_zh } 297*6c106319Sxu_zh} 298