xref: /XiangShan/src/main/scala/xiangshan/frontend/icache/ICacheCtrlUnit.scala (revision 6c106319588f5988a282dc2e7c687a9d44e9c209)
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