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(0).w.valid := memBlock.io.csrUpdate.w.valid 368 csrioIn.distributedUpdate(0).w.bits := memBlock.io.csrUpdate.w.bits 369 csrioIn.distributedUpdate(1).w.valid := frontend.io.csrUpdate.w.valid 370 csrioIn.distributedUpdate(1).w.bits := frontend.io.csrUpdate.w.bits 371 372 fenceio.sfence <> memBlock.io.sfence 373 fenceio.sbuffer <> memBlock.io.fenceToSbuffer 374 375 memBlock.io.redirect <> ctrlBlock.io.redirect 376 memBlock.io.rsfeedback <> exuBlocks(0).io.scheExtra.feedback.get 377 memBlock.io.csrCtrl <> csrioIn.customCtrl 378 memBlock.io.tlbCsr <> csrioIn.tlb 379 memBlock.io.lsqio.rob <> ctrlBlock.io.robio.lsq 380 memBlock.io.lsqio.exceptionAddr.isStore := CommitType.lsInstIsStore(ctrlBlock.io.robio.exception.bits.uop.ctrl.commitType) 381 382 val itlbRepeater1 = PTWRepeater(frontend.io.ptw, fenceio.sfence, csrioIn.tlb) 383 val itlbRepeater2 = PTWRepeater(itlbRepeater1.io.ptw, ptw.io.tlb(0), fenceio.sfence, csrioIn.tlb) 384 val dtlbRepeater1 = PTWFilter(memBlock.io.ptw, fenceio.sfence, csrioIn.tlb, l2tlbParams.filterSize) 385 val dtlbRepeater2 = PTWRepeaterNB(passReady = false, dtlbRepeater1.io.ptw, ptw.io.tlb(1), fenceio.sfence, csrioIn.tlb) 386 ptw.io.sfence <> fenceio.sfence 387 ptw.io.csr.tlb <> csrioIn.tlb 388 ptw.io.csr.distribute_csr <> csrioIn.customCtrl.distribute_csr 389 390 // if l2 prefetcher use stream prefetch, it should be placed in XSCore 391 io.l2_pf_enable := csrioIn.customCtrl.l2_pf_enable 392 393 // Modules are reset one by one 394 // reset --> SYNC ----> SYNC ------> SYNC -----> SYNC -----> SYNC --- 395 // | | | | | 396 // v v v v v 397 // PTW {MemBlock, dtlb} ExuBlocks CtrlBlock {Frontend, itlb} 398 val resetChain = Seq( 399 Seq(memBlock, dtlbRepeater1, dtlbRepeater2), 400 Seq(exuBlocks.head), 401 // Note: arbiters don't actually have reset ports 402 exuBlocks.tail ++ Seq(outer.wbArbiter.module), 403 Seq(ctrlBlock), 404 Seq(ptw), 405 Seq(frontend, itlbRepeater1, itlbRepeater2) 406 ) 407 ResetGen(resetChain, reset.asBool, !debugOpts.FPGAPlatform) 408} 409