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