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 = Vec(numPCntHc * coreParams.L2NBanks,(Input(UInt(6.W)))) 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 // TODO: connect rsPerf 335 val rsPerf = VecInit(exuBlocks.flatMap(_.io.scheExtra.perf)) 336 val rs_perf = Wire(new PerfEventsBundle(rsPerf.length)) 337 val rs_cnt = rs_perf.length 338 for (i <- 0 until rs_cnt){ 339 rs_perf.perf_events(i).incr_step := rsPerf(i).asUInt 340 } 341 dontTouch(rsPerf) 342 exuBlocks(0).perfinfo.perfEvents <> ctrlBlock.perfinfo.perfEventsEu0 343 exuBlocks(1).perfinfo.perfEvents <> ctrlBlock.perfinfo.perfEventsEu1 344 memBlock.perfinfo.perfEventsPTW <> ptw.perfinfo.perfEvents 345 ctrlBlock.perfinfo.perfEventsRs := rs_perf 346 347 csrioIn.hartId <> io.hartId 348 csrioIn.perf <> DontCare 349 csrioIn.perf.retiredInstr <> ctrlBlock.io.robio.toCSR.perfinfo.retiredInstr 350 csrioIn.perf.ctrlInfo <> ctrlBlock.io.perfInfo.ctrlInfo 351 csrioIn.perf.memInfo <> memBlock.io.memInfo 352 csrioIn.perf.frontendInfo <> frontend.io.frontendInfo 353 354 csrioIn.perf.perfEventsFrontend <> frontend.perfinfo.perfEvents 355 csrioIn.perf.perfEventsCtrl <> ctrlBlock.perfinfo.perfEvents 356 csrioIn.perf.perfEventsLsu <> memBlock.perfinfo.perfEvents 357 csrioIn.perf.perfEventsHc <> io.perfEvents 358 359 csrioIn.fpu.fflags <> ctrlBlock.io.robio.toCSR.fflags 360 csrioIn.fpu.isIllegal := false.B 361 csrioIn.fpu.dirty_fs <> ctrlBlock.io.robio.toCSR.dirty_fs 362 csrioIn.fpu.frm <> exuBlocks(1).io.fuExtra.frm.get 363 csrioIn.exception <> ctrlBlock.io.robio.exception 364 csrioIn.isXRet <> ctrlBlock.io.robio.toCSR.isXRet 365 csrioIn.trapTarget <> ctrlBlock.io.robio.toCSR.trapTarget 366 csrioIn.interrupt <> ctrlBlock.io.robio.toCSR.intrBitSet 367 csrioIn.memExceptionVAddr <> memBlock.io.lsqio.exceptionAddr.vaddr 368 369 csrioIn.externalInterrupt.msip := outer.clint_int_sink.in.head._1(0) 370 csrioIn.externalInterrupt.mtip := outer.clint_int_sink.in.head._1(1) 371 csrioIn.externalInterrupt.meip := outer.plic_int_sink.in.head._1(0) 372 csrioIn.externalInterrupt.seip := outer.plic_int_sink.in.last._1(0) 373 csrioIn.externalInterrupt.debug := outer.debug_int_sink.in.head._1(0) 374 375 csrioIn.distributedUpdate <> memBlock.io.csrUpdate // TODO 376 377 fenceio.sfence <> memBlock.io.sfence 378 fenceio.sbuffer <> memBlock.io.fenceToSbuffer 379 380 memBlock.io.redirect <> ctrlBlock.io.redirect 381 memBlock.io.rsfeedback <> exuBlocks(0).io.scheExtra.feedback.get 382 memBlock.io.csrCtrl <> csrioIn.customCtrl 383 memBlock.io.tlbCsr <> csrioIn.tlb 384 memBlock.io.lsqio.rob <> ctrlBlock.io.robio.lsq 385 memBlock.io.lsqio.exceptionAddr.isStore := CommitType.lsInstIsStore(ctrlBlock.io.robio.exception.bits.uop.ctrl.commitType) 386 387 val itlbRepeater1 = PTWRepeater(frontend.io.ptw, fenceio.sfence, csrioIn.tlb) 388 val itlbRepeater2 = PTWRepeater(itlbRepeater1.io.ptw, ptw.io.tlb(0), fenceio.sfence, csrioIn.tlb) 389 val dtlbRepeater1 = PTWFilter(memBlock.io.ptw, fenceio.sfence, csrioIn.tlb, l2tlbParams.filterSize) 390 val dtlbRepeater2 = PTWRepeaterNB(passReady = false, dtlbRepeater1.io.ptw, ptw.io.tlb(1), fenceio.sfence, csrioIn.tlb) 391 ptw.io.sfence <> fenceio.sfence 392 ptw.io.csr.tlb <> csrioIn.tlb 393 ptw.io.csr.distribute_csr <> csrioIn.customCtrl.distribute_csr 394 395 // if l2 prefetcher use stream prefetch, it should be placed in XSCore 396 io.l2_pf_enable := csrioIn.customCtrl.l2_pf_enable 397 398 // Modules are reset one by one 399 // reset --> SYNC ----> SYNC ------> SYNC -----> SYNC -----> SYNC --- 400 // | | | | | 401 // v v v v v 402 // PTW {MemBlock, dtlb} ExuBlocks CtrlBlock {Frontend, itlb} 403 val resetChain = Seq( 404 Seq(memBlock, dtlbRepeater1, dtlbRepeater2), 405 Seq(exuBlocks.head), 406 // Note: arbiters don't actually have reset ports 407 exuBlocks.tail ++ Seq(outer.wbArbiter.module), 408 Seq(ctrlBlock), 409 Seq(ptw), 410 Seq(frontend, itlbRepeater1, itlbRepeater2) 411 ) 412 ResetGen(resetChain, reset.asBool, !debugOpts.FPGAPlatform) 413} 414