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