xref: /XiangShan/src/main/scala/xiangshan/XSCore.scala (revision 1ca0e4f33f402f31daec0e57d270079d2db13562)
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
18
19import chipsalliance.rocketchip.config
20import chipsalliance.rocketchip.config.Parameters
21import chisel3._
22import chisel3.util._
23import freechips.rocketchip.diplomacy.{BundleBridgeSource, LazyModule, LazyModuleImp}
24import freechips.rocketchip.interrupts.{IntSinkNode, IntSinkPortSimple}
25import freechips.rocketchip.tile.HasFPUParameters
26import system.HasSoCParameter
27import utils._
28import xiangshan.backend._
29import xiangshan.backend.exu.{ExuConfig, Wb2Ctrl, WbArbiterWrapper}
30import xiangshan.cache.mmu._
31import xiangshan.frontend._
32
33import scala.collection.mutable.ListBuffer
34
35abstract class XSModule(implicit val p: Parameters) extends MultiIOModule
36  with HasXSParameter
37  with HasFPUParameters {
38  def io: Record
39}
40
41//remove this trait after impl module logic
42trait NeedImpl {
43  this: RawModule =>
44  override protected def IO[T <: Data](iodef: T): T = {
45    println(s"[Warn]: (${this.name}) please reomve 'NeedImpl' after implement this module")
46    val io = chisel3.experimental.IO(iodef)
47    io <> DontCare
48    io
49  }
50}
51
52class WritebackSourceParams(
53  var exuConfigs: Seq[Seq[ExuConfig]] = Seq()
54 ) {
55  def length: Int = exuConfigs.length
56  def ++(that: WritebackSourceParams): WritebackSourceParams = {
57    new WritebackSourceParams(exuConfigs ++ that.exuConfigs)
58  }
59}
60
61trait HasWritebackSource {
62  val writebackSourceParams: Seq[WritebackSourceParams]
63  final def writebackSource(sourceMod: HasWritebackSourceImp): Seq[Seq[Valid[ExuOutput]]] = {
64    require(sourceMod.writebackSource.isDefined, "should not use Valid[ExuOutput]")
65    val source = sourceMod.writebackSource.get
66    require(source.length == writebackSourceParams.length, "length mismatch between sources")
67    for ((s, p) <- source.zip(writebackSourceParams)) {
68      require(s.length == p.length, "params do not match with the exuOutput")
69    }
70    source
71  }
72  final def writebackSource1(sourceMod: HasWritebackSourceImp): Seq[Seq[DecoupledIO[ExuOutput]]] = {
73    require(sourceMod.writebackSource1.isDefined, "should not use DecoupledIO[ExuOutput]")
74    val source = sourceMod.writebackSource1.get
75    require(source.length == writebackSourceParams.length, "length mismatch between sources")
76    for ((s, p) <- source.zip(writebackSourceParams)) {
77      require(s.length == p.length, "params do not match with the exuOutput")
78    }
79    source
80  }
81  val writebackSourceImp: HasWritebackSourceImp
82}
83
84trait HasWritebackSourceImp {
85  def writebackSource: Option[Seq[Seq[Valid[ExuOutput]]]] = None
86  def writebackSource1: Option[Seq[Seq[DecoupledIO[ExuOutput]]]] = None
87}
88
89trait HasWritebackSink {
90  // Caches all sources. The selected source will be the one with smallest length.
91  var writebackSinks = ListBuffer.empty[(Seq[HasWritebackSource], Seq[Int])]
92  def addWritebackSink(source: Seq[HasWritebackSource], index: Option[Seq[Int]] = None): HasWritebackSink = {
93    val realIndex = if (index.isDefined) index.get else Seq.fill(source.length)(0)
94    writebackSinks += ((source, realIndex))
95    this
96  }
97
98  def writebackSinksParams: Seq[WritebackSourceParams] = {
99    writebackSinks.map{ case (s, i) => s.zip(i).map(x => x._1.writebackSourceParams(x._2)).reduce(_ ++ _) }
100  }
101  final def writebackSinksMod(
102     thisMod: Option[HasWritebackSource] = None,
103     thisModImp: Option[HasWritebackSourceImp] = None
104   ): Seq[Seq[HasWritebackSourceImp]] = {
105    require(thisMod.isDefined == thisModImp.isDefined)
106    writebackSinks.map(_._1.map(source =>
107      if (thisMod.isDefined && source == thisMod.get) thisModImp.get else source.writebackSourceImp)
108    )
109  }
110  final def writebackSinksImp(
111    thisMod: Option[HasWritebackSource] = None,
112    thisModImp: Option[HasWritebackSourceImp] = None
113  ): Seq[Seq[ValidIO[ExuOutput]]] = {
114    val sourceMod = writebackSinksMod(thisMod, thisModImp)
115    writebackSinks.zip(sourceMod).map{ case ((s, i), m) =>
116      s.zip(i).zip(m).flatMap(x => x._1._1.writebackSource(x._2)(x._1._2))
117    }
118  }
119  def selWritebackSinks(func: WritebackSourceParams => Int): Int = {
120    writebackSinksParams.zipWithIndex.minBy(params => func(params._1))._2
121  }
122  def generateWritebackIO(
123    thisMod: Option[HasWritebackSource] = None,
124    thisModImp: Option[HasWritebackSourceImp] = None
125   ): Unit
126}
127
128abstract class XSBundle(implicit val p: Parameters) extends Bundle
129  with HasXSParameter
130
131abstract class XSCoreBase()(implicit p: config.Parameters) extends LazyModule
132  with HasXSParameter with HasExuWbHelper
133{
134  // interrupt sinks
135  val clint_int_sink = IntSinkNode(IntSinkPortSimple(1, 2))
136  val debug_int_sink = IntSinkNode(IntSinkPortSimple(1, 1))
137  val plic_int_sink = IntSinkNode(IntSinkPortSimple(2, 1))
138  // outer facing nodes
139  val frontend = LazyModule(new Frontend())
140  val ptw = LazyModule(new PTWWrapper())
141  val csrOut = BundleBridgeSource(Some(() => new DistributedCSRIO()))
142
143  val wbArbiter = LazyModule(new WbArbiterWrapper(exuConfigs, NRIntWritePorts, NRFpWritePorts))
144  val intWbPorts = wbArbiter.intWbPorts
145  val fpWbPorts = wbArbiter.fpWbPorts
146
147  // TODO: better RS organization
148  // generate rs according to number of function units
149  require(exuParameters.JmpCnt == 1)
150  require(exuParameters.MduCnt <= exuParameters.AluCnt && exuParameters.MduCnt > 0)
151  require(exuParameters.FmiscCnt <= exuParameters.FmacCnt && exuParameters.FmiscCnt > 0)
152  require(exuParameters.LduCnt == 2 && exuParameters.StuCnt == 2)
153
154  // one RS every 2 MDUs
155  val schedulePorts = Seq(
156    // exuCfg, numDeq, intFastWakeupTarget, fpFastWakeupTarget
157    Seq(
158      (AluExeUnitCfg, exuParameters.AluCnt, Seq(AluExeUnitCfg, MulDivExeUnitCfg, JumpCSRExeUnitCfg, LdExeUnitCfg, StaExeUnitCfg), Seq()),
159      (MulDivExeUnitCfg, exuParameters.MduCnt, Seq(AluExeUnitCfg, MulDivExeUnitCfg), Seq()),
160      (JumpCSRExeUnitCfg, 1, Seq(), Seq()),
161      (LdExeUnitCfg, exuParameters.LduCnt, Seq(AluExeUnitCfg, LdExeUnitCfg), Seq()),
162      (StaExeUnitCfg, exuParameters.StuCnt, Seq(), Seq()),
163      (StdExeUnitCfg, exuParameters.StuCnt, Seq(), Seq())
164    ),
165    Seq(
166      (FmacExeUnitCfg, exuParameters.FmacCnt, Seq(), Seq(FmacExeUnitCfg, FmiscExeUnitCfg)),
167      (FmiscExeUnitCfg, exuParameters.FmiscCnt, Seq(), Seq())
168    )
169  )
170
171  // should do outer fast wakeup ports here
172  val otherFastPorts = schedulePorts.zipWithIndex.map { case (sche, i) =>
173    val otherCfg = schedulePorts.zipWithIndex.filter(_._2 != i).map(_._1).reduce(_ ++ _)
174    val outerPorts = sche.map(cfg => {
175      // exe units from this scheduler need fastUops from exeunits
176      val outerWakeupInSche = sche.filter(_._1.wakeupFromExu)
177      val intraIntScheOuter = outerWakeupInSche.filter(_._3.contains(cfg._1)).map(_._1)
178      val intraFpScheOuter = outerWakeupInSche.filter(_._4.contains(cfg._1)).map(_._1)
179      // exe units from other schedulers need fastUop from outside
180      val otherIntSource = otherCfg.filter(_._3.contains(cfg._1)).map(_._1)
181      val otherFpSource = otherCfg.filter(_._4.contains(cfg._1)).map(_._1)
182      val intSource = findInWbPorts(intWbPorts, intraIntScheOuter ++ otherIntSource)
183      val fpSource = findInWbPorts(fpWbPorts, intraFpScheOuter ++ otherFpSource)
184      getFastWakeupIndex(cfg._1, intSource, fpSource, intWbPorts.length).sorted
185    })
186    println(s"inter-scheduler wakeup sources for $i: $outerPorts")
187    outerPorts
188  }
189
190  // allow mdu and fmisc to have 2*numDeq enqueue ports
191  val intDpPorts = (0 until exuParameters.AluCnt).map(i => {
192    if (i < exuParameters.JmpCnt) Seq((0, i), (1, i), (2, i))
193    else if (i < 2 * exuParameters.MduCnt) Seq((0, i), (1, i))
194    else Seq((0, i))
195  })
196  val lsDpPorts = Seq(
197    Seq((3, 0)),
198    Seq((3, 1)),
199    Seq((4, 0)),
200    Seq((4, 1))
201  ) ++ (0 until exuParameters.StuCnt).map(i => Seq((5, i)))
202  val fpDpPorts = (0 until exuParameters.FmacCnt).map(i => {
203    if (i < 2 * exuParameters.FmiscCnt) Seq((0, i), (1, i))
204    else Seq((0, i))
205  })
206
207  val dispatchPorts = Seq(intDpPorts ++ lsDpPorts, fpDpPorts)
208
209  val outIntRfReadPorts = Seq(0, 0)
210  val outFpRfReadPorts = Seq(0, 2)
211  val hasIntRf = Seq(true, false)
212  val hasFpRf = Seq(false, true)
213  val exuBlocks = schedulePorts.zip(dispatchPorts).zip(otherFastPorts).zipWithIndex.map {
214    case (((sche, disp), other), i) =>
215      LazyModule(new ExuBlock(sche, disp, intWbPorts, fpWbPorts, other, outIntRfReadPorts(i), outFpRfReadPorts(i), hasIntRf(i), hasFpRf(i)))
216  }
217
218  val memBlock = LazyModule(new MemBlock()(p.alter((site, here, up) => {
219    case XSCoreParamsKey => up(XSCoreParamsKey).copy(
220      IssQueSize = exuBlocks.head.scheduler.memRsEntries.max
221    )
222  })))
223
224  val wb2Ctrl = LazyModule(new Wb2Ctrl(exuConfigs))
225  wb2Ctrl.addWritebackSink(exuBlocks :+ memBlock)
226  val ctrlBlock = LazyModule(new CtrlBlock)
227  val writebackSources = Seq(Seq(wb2Ctrl), Seq(wbArbiter))
228  writebackSources.foreach(s => ctrlBlock.addWritebackSink(s))
229}
230
231class XSCore()(implicit p: config.Parameters) extends XSCoreBase
232  with HasXSDts
233{
234  lazy val module = new XSCoreImp(this)
235}
236
237class XSCoreImp(outer: XSCoreBase) extends LazyModuleImp(outer)
238  with HasXSParameter
239  with HasSoCParameter {
240  val io = IO(new Bundle {
241    val hartId = Input(UInt(64.W))
242    val l2_pf_enable = Output(Bool())
243    val perfEvents = Input(Vec(numPCntHc * coreParams.L2NBanks, new PerfEvent))
244    val beu_errors = Output(new XSL1BusErrors())
245  })
246
247  println(s"FPGAPlatform:${env.FPGAPlatform} EnableDebug:${env.EnableDebug}")
248
249  val frontend = outer.frontend.module
250  val ctrlBlock = outer.ctrlBlock.module
251  val wb2Ctrl = outer.wb2Ctrl.module
252  val memBlock = outer.memBlock.module
253  val ptw = outer.ptw.module
254  val exuBlocks = outer.exuBlocks.map(_.module)
255
256  ctrlBlock.io.hartId := io.hartId
257  exuBlocks.foreach(_.io.hartId := io.hartId)
258  memBlock.io.hartId := io.hartId
259  outer.wbArbiter.module.io.hartId := io.hartId
260
261  outer.wbArbiter.module.io.redirect <> ctrlBlock.io.redirect
262  val allWriteback = exuBlocks.flatMap(_.io.fuWriteback) ++ memBlock.io.writeback
263  require(exuConfigs.length == allWriteback.length, s"${exuConfigs.length} != ${allWriteback.length}")
264  outer.wbArbiter.module.io.in <> allWriteback
265  val rfWriteback = outer.wbArbiter.module.io.out
266
267  wb2Ctrl.io.redirect <> ctrlBlock.io.redirect
268  outer.wb2Ctrl.generateWritebackIO()
269
270  io.beu_errors.icache <> frontend.io.error
271  io.beu_errors.dcache <> memBlock.io.error
272
273  require(exuBlocks.count(_.fuConfigs.map(_._1).contains(JumpCSRExeUnitCfg)) == 1)
274  val csrFenceMod = exuBlocks.filter(_.fuConfigs.map(_._1).contains(JumpCSRExeUnitCfg)).head
275  val csrioIn = csrFenceMod.io.fuExtra.csrio.get
276  val fenceio = csrFenceMod.io.fuExtra.fenceio.get
277
278  frontend.io.backend <> ctrlBlock.io.frontend
279  frontend.io.sfence <> fenceio.sfence
280  frontend.io.tlbCsr <> csrioIn.tlb
281  frontend.io.csrCtrl <> csrioIn.customCtrl
282  frontend.io.fencei := fenceio.fencei
283
284  ctrlBlock.io.csrCtrl <> csrioIn.customCtrl
285  val redirectBlocks = exuBlocks.reverse.filter(_.fuConfigs.map(_._1).map(_.hasRedirect).reduce(_ || _))
286  ctrlBlock.io.exuRedirect <> redirectBlocks.flatMap(_.io.fuExtra.exuRedirect)
287  ctrlBlock.io.stIn <> memBlock.io.stIn
288  ctrlBlock.io.memoryViolation <> memBlock.io.memoryViolation
289  exuBlocks.head.io.scheExtra.enqLsq.get <> memBlock.io.enqLsq
290  val sourceModules = outer.writebackSources.map(_.map(_.module.asInstanceOf[HasWritebackSourceImp]))
291  outer.ctrlBlock.generateWritebackIO()
292
293  val allFastUop = exuBlocks.flatMap(b => b.io.fastUopOut.dropRight(b.numOutFu)) ++ memBlock.io.otherFastWakeup
294  require(allFastUop.length == exuConfigs.length, s"${allFastUop.length} != ${exuConfigs.length}")
295  val intFastUop = allFastUop.zip(exuConfigs).filter(_._2.writeIntRf).map(_._1)
296  val fpFastUop = allFastUop.zip(exuConfigs).filter(_._2.writeFpRf).map(_._1)
297  val intFastUop1 = outer.wbArbiter.intConnections.map(c => intFastUop(c.head))
298  val fpFastUop1 = outer.wbArbiter.fpConnections.map(c => fpFastUop(c.head))
299  val allFastUop1 = intFastUop1 ++ fpFastUop1
300
301  ctrlBlock.io.dispatch <> exuBlocks.flatMap(_.io.in)
302
303  exuBlocks(0).io.scheExtra.fpRfReadIn.get <> exuBlocks(1).io.scheExtra.fpRfReadOut.get
304  exuBlocks(0).io.scheExtra.fpStateReadIn.get <> exuBlocks(1).io.scheExtra.fpStateReadOut.get
305
306  memBlock.io.issue <> exuBlocks(0).io.issue.get
307  // By default, instructions do not have exceptions when they enter the function units.
308  memBlock.io.issue.map(_.bits.uop.clearExceptions())
309  exuBlocks(0).io.scheExtra.loadFastMatch.get <> memBlock.io.loadFastMatch
310
311  val stdIssue = exuBlocks(0).io.issue.get.takeRight(exuParameters.StuCnt)
312  exuBlocks.map(_.io).foreach { exu =>
313    exu.redirect <> ctrlBlock.io.redirect
314    exu.allocPregs <> ctrlBlock.io.allocPregs
315    exu.rfWriteback <> rfWriteback
316    exu.fastUopIn <> allFastUop1
317    exu.scheExtra.jumpPc <> ctrlBlock.io.jumpPc
318    exu.scheExtra.jalr_target <> ctrlBlock.io.jalr_target
319    exu.scheExtra.stIssuePtr <> memBlock.io.stIssuePtr
320    exu.scheExtra.debug_fp_rat <> ctrlBlock.io.debug_fp_rat
321    exu.scheExtra.debug_int_rat <> ctrlBlock.io.debug_int_rat
322    exu.scheExtra.memWaitUpdateReq.staIssue.zip(memBlock.io.stIn).foreach{case (sink, src) => {
323      sink.bits := src.bits
324      sink.valid := src.valid
325    }}
326    exu.scheExtra.memWaitUpdateReq.stdIssue.zip(stdIssue).foreach{case (sink, src) => {
327      sink.valid := src.valid
328      sink.bits := src.bits
329    }}
330  }
331  XSPerfHistogram("fastIn_count", PopCount(allFastUop1.map(_.valid)), true.B, 0, allFastUop1.length, 1)
332  XSPerfHistogram("wakeup_count", PopCount(rfWriteback.map(_.valid)), true.B, 0, rfWriteback.length, 1)
333
334  ctrlBlock.perfinfo.perfEventsEu0 := exuBlocks(0).getPerf.dropRight(outer.exuBlocks(0).scheduler.numRs)
335  ctrlBlock.perfinfo.perfEventsEu1 := exuBlocks(1).getPerf.dropRight(outer.exuBlocks(1).scheduler.numRs)
336  memBlock.io.perfEventsPTW  := ptw.getPerf
337  ctrlBlock.perfinfo.perfEventsRs  := outer.exuBlocks.flatMap(b => b.module.getPerf.takeRight(b.scheduler.numRs))
338
339  csrioIn.hartId <> io.hartId
340  csrioIn.perf <> DontCare
341  csrioIn.perf.retiredInstr <> ctrlBlock.io.robio.toCSR.perfinfo.retiredInstr
342  csrioIn.perf.ctrlInfo <> ctrlBlock.io.perfInfo.ctrlInfo
343  csrioIn.perf.memInfo <> memBlock.io.memInfo
344  csrioIn.perf.frontendInfo <> frontend.io.frontendInfo
345
346  csrioIn.perf.perfEventsFrontend <> frontend.getPerf
347  csrioIn.perf.perfEventsCtrl     <> ctrlBlock.getPerf
348  csrioIn.perf.perfEventsLsu      <> memBlock.getPerf
349  csrioIn.perf.perfEventsHc       <> io.perfEvents
350
351  csrioIn.fpu.fflags <> ctrlBlock.io.robio.toCSR.fflags
352  csrioIn.fpu.isIllegal := false.B
353  csrioIn.fpu.dirty_fs <> ctrlBlock.io.robio.toCSR.dirty_fs
354  csrioIn.fpu.frm <> exuBlocks(1).io.fuExtra.frm.get
355  csrioIn.exception <> ctrlBlock.io.robio.exception
356  csrioIn.isXRet <> ctrlBlock.io.robio.toCSR.isXRet
357  csrioIn.trapTarget <> ctrlBlock.io.robio.toCSR.trapTarget
358  csrioIn.interrupt <> ctrlBlock.io.robio.toCSR.intrBitSet
359  csrioIn.memExceptionVAddr <> memBlock.io.lsqio.exceptionAddr.vaddr
360
361  csrioIn.externalInterrupt.msip := outer.clint_int_sink.in.head._1(0)
362  csrioIn.externalInterrupt.mtip := outer.clint_int_sink.in.head._1(1)
363  csrioIn.externalInterrupt.meip := outer.plic_int_sink.in.head._1(0)
364  csrioIn.externalInterrupt.seip := outer.plic_int_sink.in.last._1(0)
365  csrioIn.externalInterrupt.debug := outer.debug_int_sink.in.head._1(0)
366
367  csrioIn.distributedUpdate <> memBlock.io.csrUpdate // TODO
368
369  fenceio.sfence <> memBlock.io.sfence
370  fenceio.sbuffer <> memBlock.io.fenceToSbuffer
371
372  memBlock.io.redirect <> ctrlBlock.io.redirect
373  memBlock.io.rsfeedback <> exuBlocks(0).io.scheExtra.feedback.get
374  memBlock.io.csrCtrl <> csrioIn.customCtrl
375  memBlock.io.tlbCsr <> csrioIn.tlb
376  memBlock.io.lsqio.rob <> ctrlBlock.io.robio.lsq
377  memBlock.io.lsqio.exceptionAddr.isStore := CommitType.lsInstIsStore(ctrlBlock.io.robio.exception.bits.uop.ctrl.commitType)
378
379  val itlbRepeater1 = PTWRepeater(frontend.io.ptw, fenceio.sfence, csrioIn.tlb)
380  val itlbRepeater2 = PTWRepeater(itlbRepeater1.io.ptw, ptw.io.tlb(0), fenceio.sfence, csrioIn.tlb)
381  val dtlbRepeater1  = PTWFilter(memBlock.io.ptw, fenceio.sfence, csrioIn.tlb, l2tlbParams.filterSize)
382  val dtlbRepeater2  = PTWRepeaterNB(passReady = false, dtlbRepeater1.io.ptw, ptw.io.tlb(1), fenceio.sfence, csrioIn.tlb)
383  ptw.io.sfence <> fenceio.sfence
384  ptw.io.csr.tlb <> csrioIn.tlb
385  ptw.io.csr.distribute_csr <> csrioIn.customCtrl.distribute_csr
386
387  // if l2 prefetcher use stream prefetch, it should be placed in XSCore
388  io.l2_pf_enable := csrioIn.customCtrl.l2_pf_enable
389
390  // Modules are reset one by one
391  // reset --> SYNC ----> SYNC ------> SYNC -----> SYNC -----> SYNC ---
392  //                  |          |            |           |           |
393  //                  v          v            v           v           v
394  //                 PTW  {MemBlock, dtlb}  ExuBlocks  CtrlBlock  {Frontend, itlb}
395  val resetChain = Seq(
396    Seq(memBlock, dtlbRepeater1, dtlbRepeater2),
397    Seq(exuBlocks.head),
398    // Note: arbiters don't actually have reset ports
399    exuBlocks.tail ++ Seq(outer.wbArbiter.module),
400    Seq(ctrlBlock),
401    Seq(ptw),
402    Seq(frontend, itlbRepeater1, itlbRepeater2)
403  )
404  ResetGen(resetChain, reset.asBool, !debugOpts.FPGAPlatform)
405}
406