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 exuBlocks.foreach(b => { 291 b.io.scheExtra.lcommit := ctrlBlock.io.robio.lsq.lcommit 292 b.io.scheExtra.scommit := memBlock.io.sqDeq 293 b.io.scheExtra.lqCancelCnt := memBlock.io.lqCancelCnt 294 b.io.scheExtra.sqCancelCnt := memBlock.io.sqCancelCnt 295 }) 296 val sourceModules = outer.writebackSources.map(_.map(_.module.asInstanceOf[HasWritebackSourceImp])) 297 outer.ctrlBlock.generateWritebackIO() 298 299 val allFastUop = exuBlocks.flatMap(b => b.io.fastUopOut.dropRight(b.numOutFu)) ++ memBlock.io.otherFastWakeup 300 require(allFastUop.length == exuConfigs.length, s"${allFastUop.length} != ${exuConfigs.length}") 301 val intFastUop = allFastUop.zip(exuConfigs).filter(_._2.writeIntRf).map(_._1) 302 val fpFastUop = allFastUop.zip(exuConfigs).filter(_._2.writeFpRf).map(_._1) 303 val intFastUop1 = outer.wbArbiter.intConnections.map(c => intFastUop(c.head)) 304 val fpFastUop1 = outer.wbArbiter.fpConnections.map(c => fpFastUop(c.head)) 305 val allFastUop1 = intFastUop1 ++ fpFastUop1 306 307 ctrlBlock.io.dispatch <> exuBlocks.flatMap(_.io.in) 308 309 exuBlocks(0).io.scheExtra.fpRfReadIn.get <> exuBlocks(1).io.scheExtra.fpRfReadOut.get 310 exuBlocks(0).io.scheExtra.fpStateReadIn.get <> exuBlocks(1).io.scheExtra.fpStateReadOut.get 311 312 memBlock.io.issue <> exuBlocks(0).io.issue.get 313 // By default, instructions do not have exceptions when they enter the function units. 314 memBlock.io.issue.map(_.bits.uop.clearExceptions()) 315 exuBlocks(0).io.scheExtra.loadFastMatch.get <> memBlock.io.loadFastMatch 316 317 val stdIssue = exuBlocks(0).io.issue.get.takeRight(exuParameters.StuCnt) 318 exuBlocks.map(_.io).foreach { exu => 319 exu.redirect <> ctrlBlock.io.redirect 320 exu.allocPregs <> ctrlBlock.io.allocPregs 321 exu.rfWriteback <> rfWriteback 322 exu.fastUopIn <> allFastUop1 323 exu.scheExtra.jumpPc <> ctrlBlock.io.jumpPc 324 exu.scheExtra.jalr_target <> ctrlBlock.io.jalr_target 325 exu.scheExtra.stIssuePtr <> memBlock.io.stIssuePtr 326 exu.scheExtra.debug_fp_rat <> ctrlBlock.io.debug_fp_rat 327 exu.scheExtra.debug_int_rat <> ctrlBlock.io.debug_int_rat 328 exu.scheExtra.memWaitUpdateReq.staIssue.zip(memBlock.io.stIn).foreach{case (sink, src) => { 329 sink.bits := src.bits 330 sink.valid := src.valid 331 }} 332 exu.scheExtra.memWaitUpdateReq.stdIssue.zip(stdIssue).foreach{case (sink, src) => { 333 sink.valid := src.valid 334 sink.bits := src.bits 335 }} 336 } 337 XSPerfHistogram("fastIn_count", PopCount(allFastUop1.map(_.valid)), true.B, 0, allFastUop1.length, 1) 338 XSPerfHistogram("wakeup_count", PopCount(rfWriteback.map(_.valid)), true.B, 0, rfWriteback.length, 1) 339 340 ctrlBlock.perfinfo.perfEventsEu0 := exuBlocks(0).getPerf.dropRight(outer.exuBlocks(0).scheduler.numRs) 341 ctrlBlock.perfinfo.perfEventsEu1 := exuBlocks(1).getPerf.dropRight(outer.exuBlocks(1).scheduler.numRs) 342 memBlock.io.perfEventsPTW := ptw.getPerf 343 ctrlBlock.perfinfo.perfEventsRs := outer.exuBlocks.flatMap(b => b.module.getPerf.takeRight(b.scheduler.numRs)) 344 345 csrioIn.hartId <> io.hartId 346 csrioIn.perf <> DontCare 347 csrioIn.perf.retiredInstr <> ctrlBlock.io.robio.toCSR.perfinfo.retiredInstr 348 csrioIn.perf.ctrlInfo <> ctrlBlock.io.perfInfo.ctrlInfo 349 csrioIn.perf.memInfo <> memBlock.io.memInfo 350 csrioIn.perf.frontendInfo <> frontend.io.frontendInfo 351 352 csrioIn.perf.perfEventsFrontend <> frontend.getPerf 353 csrioIn.perf.perfEventsCtrl <> ctrlBlock.getPerf 354 csrioIn.perf.perfEventsLsu <> memBlock.getPerf 355 csrioIn.perf.perfEventsHc <> io.perfEvents 356 357 csrioIn.fpu.fflags <> ctrlBlock.io.robio.toCSR.fflags 358 csrioIn.fpu.isIllegal := false.B 359 csrioIn.fpu.dirty_fs <> ctrlBlock.io.robio.toCSR.dirty_fs 360 csrioIn.fpu.frm <> exuBlocks(1).io.fuExtra.frm.get 361 csrioIn.exception <> ctrlBlock.io.robio.exception 362 csrioIn.isXRet <> ctrlBlock.io.robio.toCSR.isXRet 363 csrioIn.trapTarget <> ctrlBlock.io.robio.toCSR.trapTarget 364 csrioIn.interrupt <> ctrlBlock.io.robio.toCSR.intrBitSet 365 csrioIn.memExceptionVAddr <> memBlock.io.lsqio.exceptionAddr.vaddr 366 367 csrioIn.externalInterrupt.msip := outer.clint_int_sink.in.head._1(0) 368 csrioIn.externalInterrupt.mtip := outer.clint_int_sink.in.head._1(1) 369 csrioIn.externalInterrupt.meip := outer.plic_int_sink.in.head._1(0) 370 csrioIn.externalInterrupt.seip := outer.plic_int_sink.in.last._1(0) 371 csrioIn.externalInterrupt.debug := outer.debug_int_sink.in.head._1(0) 372 373 csrioIn.distributedUpdate(0).w.valid := memBlock.io.csrUpdate.w.valid 374 csrioIn.distributedUpdate(0).w.bits := memBlock.io.csrUpdate.w.bits 375 csrioIn.distributedUpdate(1).w.valid := frontend.io.csrUpdate.w.valid 376 csrioIn.distributedUpdate(1).w.bits := frontend.io.csrUpdate.w.bits 377 378 fenceio.sfence <> memBlock.io.sfence 379 fenceio.sbuffer <> memBlock.io.fenceToSbuffer 380 381 memBlock.io.redirect <> ctrlBlock.io.redirect 382 memBlock.io.rsfeedback <> exuBlocks(0).io.scheExtra.feedback.get 383 memBlock.io.csrCtrl <> csrioIn.customCtrl 384 memBlock.io.tlbCsr <> csrioIn.tlb 385 memBlock.io.lsqio.rob <> ctrlBlock.io.robio.lsq 386 memBlock.io.lsqio.exceptionAddr.isStore := CommitType.lsInstIsStore(ctrlBlock.io.robio.exception.bits.uop.ctrl.commitType) 387 388 val itlbRepeater1 = PTWRepeater(frontend.io.ptw, fenceio.sfence, csrioIn.tlb) 389 val itlbRepeater2 = PTWRepeater(itlbRepeater1.io.ptw, ptw.io.tlb(0), fenceio.sfence, csrioIn.tlb) 390 val dtlbRepeater1 = PTWFilter(memBlock.io.ptw, fenceio.sfence, csrioIn.tlb, l2tlbParams.filterSize) 391 val dtlbRepeater2 = PTWRepeaterNB(passReady = false, dtlbRepeater1.io.ptw, ptw.io.tlb(1), fenceio.sfence, csrioIn.tlb) 392 ptw.io.sfence <> fenceio.sfence 393 ptw.io.csr.tlb <> csrioIn.tlb 394 ptw.io.csr.distribute_csr <> csrioIn.customCtrl.distribute_csr 395 396 // if l2 prefetcher use stream prefetch, it should be placed in XSCore 397 io.l2_pf_enable := csrioIn.customCtrl.l2_pf_enable 398 399 // Modules are reset one by one 400 // reset --> SYNC ----> SYNC ------> SYNC -----> SYNC -----> SYNC --- 401 // | | | | | 402 // v v v v v 403 // PTW {MemBlock, dtlb} ExuBlocks CtrlBlock {Frontend, itlb} 404 val resetChain = Seq( 405 Seq(memBlock, dtlbRepeater1, dtlbRepeater2), 406 Seq(exuBlocks.head), 407 // Note: arbiters don't actually have reset ports 408 exuBlocks.tail ++ Seq(outer.wbArbiter.module), 409 Seq(ctrlBlock), 410 Seq(ptw), 411 Seq(frontend, itlbRepeater1, itlbRepeater2) 412 ) 413 ResetGen(resetChain, reset.asBool, !debugOpts.FPGAPlatform) 414} 415