From 023a54f1225ff2df576affa5f79ba09ec7284469 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 16 Sep 2016 00:27:26 -0700 Subject: [PATCH 01/28] tilelink2 AddressDecoder: improved heuristic --- .../uncore/tilelink2/AddressDecoder.scala | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/AddressDecoder.scala b/src/main/scala/uncore/tilelink2/AddressDecoder.scala index e937d60d..adddc8a5 100644 --- a/src/main/scala/uncore/tilelink2/AddressDecoder.scala +++ b/src/main/scala/uncore/tilelink2/AddressDecoder.scala @@ -25,13 +25,14 @@ object AddressDecoder // Verify the user did not give us an impossible problem ports.combinations(2).foreach { case Seq(x, y) => x.foreach { a => y.foreach { b => - require (!a.overlaps(b)) // it must be possible to disambiguate addresses! + require (!a.overlaps(b)) // it must be possible to disambiguate ports! } } } val maxBits = log2Ceil(ports.map(_.map(_.max).max).max + 1) val bits = (0 until maxBits).map(BigInt(1) << _).toSeq val selected = recurse(Seq(ports.map(_.sorted).sorted(portOrder)), bits) selected.reduceLeft(_ | _) + // port validation via mask expansion } // A simpler version that works for a Seq[Int] @@ -51,11 +52,12 @@ object AddressDecoder // pick the bit which minimizes the number of ports in each partition // as a secondary goal, reduce the number of AddressSets within a partition - val bigValue = 100000 - def bitScore(partitions: Partitions): Int = { + def bitScore(partitions: Partitions): Seq[Int] = { val maxPortsPerPartition = partitions.map(_.size).max + val sumPortsPerPartition = partitions.map(_.size).sum val maxSetsPerPartition = partitions.map(_.map(_.size).sum).max - maxPortsPerPartition * bigValue + maxSetsPerPartition + val sumSetsPerPartition = partitions.map(_.map(_.size).sum).sum + Seq(maxPortsPerPartition, sumPortsPerPartition, maxSetsPerPartition, sumSetsPerPartition) } def partitionPort(port: Port, bit: BigInt): (Port, Port) = { @@ -77,8 +79,8 @@ object AddressDecoder def partitionPartitions(partitions: Partitions, bit: BigInt): Partitions = { val partitioned_partitions = partitions.map(p => partitionPorts(p, bit)) - val case_a_partitions = partitioned_partitions.map(_._1) - val case_b_partitions = partitioned_partitions.map(_._2) + val case_a_partitions = partitioned_partitions.map(_._1).filter(!_.isEmpty) + val case_b_partitions = partitioned_partitions.map(_._2).filter(!_.isEmpty) val new_partitions = (case_a_partitions ++ case_b_partitions).sorted(partitionOrder) // Prevent combinational memory explosion; if two partitions are equal, keep only one // Note: AddressSets in a port are sorted, and ports in a partition are sorted. @@ -106,9 +108,9 @@ object AddressDecoder val score = bitScore(result) (score, bit, result) } - val (bestScore, bestBit, bestPartitions) = candidates.min(Ordering.by[(Int, BigInt, Partitions), Int](_._1)) + val (bestScore, bestBit, bestPartitions) = candidates.min(Ordering.by[(Seq[Int], BigInt, Partitions), Iterable[Int]](_._1.toIterable)) if (debug) println("=> Selected bit 0x%x".format(bestBit)) - if (bestScore < 2*bigValue) { + if (bestScore(0) <= 1) { if (debug) println("---") Seq(bestBit) } else { From 2210e71f429f92887e5913b4fba9474fc25a6318 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 16 Sep 2016 00:49:05 -0700 Subject: [PATCH 02/28] tilelink2 AddressDecoder: validate output of optimization --- .../scala/uncore/tilelink2/AddressDecoder.scala | 15 +++++++++++++-- src/main/scala/uncore/tilelink2/Parameters.scala | 3 +++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/AddressDecoder.scala b/src/main/scala/uncore/tilelink2/AddressDecoder.scala index adddc8a5..ea7cacac 100644 --- a/src/main/scala/uncore/tilelink2/AddressDecoder.scala +++ b/src/main/scala/uncore/tilelink2/AddressDecoder.scala @@ -28,11 +28,22 @@ object AddressDecoder require (!a.overlaps(b)) // it must be possible to disambiguate ports! } } } + val maxBits = log2Ceil(ports.map(_.map(_.max).max).max + 1) val bits = (0 until maxBits).map(BigInt(1) << _).toSeq val selected = recurse(Seq(ports.map(_.sorted).sorted(portOrder)), bits) - selected.reduceLeft(_ | _) - // port validation via mask expansion + val output = selected.reduceLeft(_ | _) + + // Modify the AddressSets to allow the new wider match functions + val widePorts = ports.map { _.map { _.widen(~output) } } + // Verify that it remains possible to disambiguate all ports + widePorts.combinations(2).foreach { case Seq(x, y) => + x.foreach { a => y.foreach { b => + require (!a.overlaps(b)) + } } + } + + output } // A simpler version that works for a Seq[Int] diff --git a/src/main/scala/uncore/tilelink2/Parameters.scala b/src/main/scala/uncore/tilelink2/Parameters.scala index 6423db3c..7c49e792 100644 --- a/src/main/scala/uncore/tilelink2/Parameters.scala +++ b/src/main/scala/uncore/tilelink2/Parameters.scala @@ -98,6 +98,9 @@ case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet] // A strided slave serves discontiguous ranges def strided = alignment1 != mask + // Widen the match function to ignore all bits in imask + def widen(imask: BigInt) = AddressSet(base & ~imask, mask | imask) + // AddressSets have one natural Ordering (the containment order) def compare(x: AddressSet) = { val primary = (this.base - x.base).signum // smallest address first From 3fcc1a44608af28a8728c620c6f63db68b7158e1 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 16 Sep 2016 14:46:37 -0700 Subject: [PATCH 03/28] tilelink2 RegisterRouterTest: don't couple fire into helpers --- .../uncore/tilelink2/RegisterRouterTest.scala | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala index 8c226f01..04d73cf2 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala @@ -26,15 +26,13 @@ class RRTestCombinational(val bits: Int, rvalid: Bool => Bool, wready: Bool => B val wdata = UInt(INPUT, width = bits) } - val rfire = io.rvalid && io.rready - val wfire = io.wvalid && io.wready val reg = Reg(UInt(width = bits)) - io.rvalid := rvalid(rfire) - io.wready := wready(wfire) + io.rvalid := rvalid(io.rready) + io.wready := wready(io.wvalid) io.rdata := reg - when (wfire) { reg := io.wdata } + when (io.wvalid && io.wready) { reg := io.wdata } } object RRTestCombinational @@ -43,19 +41,19 @@ object RRTestCombinational def always: Bool => Bool = _ => Bool(true) - def random: Bool => Bool = { fire => + def random: Bool => Bool = { ready => seed = seed + 1 val lfsr = LFSR16Seed(seed) - val reg = RegInit(Bool(true)) - reg := Mux(reg, !fire, lfsr(0) && lfsr(1)) - reg + val valid = RegInit(Bool(true)) + valid := Mux(valid, !ready, lfsr(0) && lfsr(1)) + valid } - def delay(x: Int): Bool => Bool = { fire => + def delay(x: Int): Bool => Bool = { ready => val reg = RegInit(UInt(0, width = log2Ceil(x+1))) - val ready = reg === UInt(0) - reg := Mux(fire, UInt(x), Mux(ready, UInt(0), reg - UInt(1))) - ready + val valid = reg === UInt(0) + reg := Mux(ready && valid, UInt(x), Mux(valid, UInt(0), reg - UInt(1))) + valid } def combo(bits: Int, rvalid: Bool => Bool, wready: Bool => Bool): RegField = { From f0f553f2271f40d9260dcb7554380bce0537ea27 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 16 Sep 2016 14:49:43 -0700 Subject: [PATCH 04/28] tilelink2 RegisterRouterTest: work around firrtl warning Using io.wready leads to verilog that reads from the output... Lint-[PCTIO-L] Ports coerced to inout /scratch/terpstra/federation/rocket-chip/vsim/generated-src/UnitTestHarness.UnitTestConfig.v, 24860 "io_wready" Port "io_wready" declared as output in module "RRTestCombinational_29" may need to be inout. Coercing to inout. --- src/main/scala/uncore/tilelink2/RegisterRouterTest.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala index 04d73cf2..0c71428e 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala @@ -28,11 +28,13 @@ class RRTestCombinational(val bits: Int, rvalid: Bool => Bool, wready: Bool => B val reg = Reg(UInt(width = bits)) - io.rvalid := rvalid(io.rready) - io.wready := wready(io.wvalid) + val rvalid_s = rvalid(io.rready) + val wready_s = wready(io.wvalid) + io.rvalid := rvalid_s + io.wready := wready_s io.rdata := reg - when (io.wvalid && io.wready) { reg := io.wdata } + when (io.wvalid && wready_s) { reg := io.wdata } } object RRTestCombinational From dae0918c8527b28c92a6c822f34583f578a62102 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 16 Sep 2016 14:54:30 -0700 Subject: [PATCH 05/28] tilelink2 RegisterRouter: support undefZero --- .../scala/uncore/tilelink2/RegMapper.scala | 60 ++++++++++++------- .../uncore/tilelink2/RegisterRouter.scala | 7 +-- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/RegMapper.scala b/src/main/scala/uncore/tilelink2/RegMapper.scala index b20937f8..6678e500 100644 --- a/src/main/scala/uncore/tilelink2/RegMapper.scala +++ b/src/main/scala/uncore/tilelink2/RegMapper.scala @@ -2,8 +2,8 @@ package uncore.tilelink2 -import Chisel._ -import chisel3.util.{Irrevocable, IrrevocableIO} +import chisel3._ +import chisel3.util._ // A bus agnostic register interface to a register-based device @@ -36,6 +36,23 @@ object RegMapper regmap.combinations(2).foreach { case Seq((reg1, _), (reg2, _)) => require (reg1 != reg2) } + // Don't be an asshole... + regmap.foreach { reg => require (reg._1 >= 0) } + // Make sure registers fit + val inParams = in.bits.params + val inBits = inParams.indexBits + assert (regmap.map(_._1).max < (1 << inBits)) + + val out = Wire(Irrevocable(new RegMapperOutput(inParams))) + val front = Wire(Irrevocable(new RegMapperInput(inParams))) + front.bits := in.bits + + // Must this device pipeline the control channel? + val pipelined = regmap.map(_._2.map(_.pipelined)).flatten.reduce(_ || _) + val depth = concurrency.getOrElse(if (pipelined) 1 else 0) + require (depth >= 0) + require (!pipelined || depth > 0) + val back = if (depth > 0) Queue(front, depth, pipe = depth == 1) else front // Convert to and from Bits def toBits(x: Int, tail: List[Boolean] = List.empty): List[Boolean] = @@ -44,36 +61,33 @@ object RegMapper // Find the minimal mask that can decide the register map val mask = AddressDecoder(regmap.map(_._1)) + val maskMatch = ~UInt(mask, width = inBits) val maskFilter = toBits(mask) val maskBits = maskFilter.filter(x => x).size // Calculate size and indexes into the register map - val endIndex = 1 << log2Ceil(regmap.map(_._1).max+1) - val params = RegMapperParams(log2Up(endIndex), bytes, in.bits.params.extraBits) val regSize = 1 << maskBits def regIndexI(x: Int) = ofBits((maskFilter zip toBits(x)).filter(_._1).map(_._2)) def regIndexU(x: UInt) = if (maskBits == 0) UInt(0) else Cat((maskFilter zip x.toBools).filter(_._1).map(_._2).reverse) + // Protection flag for undefined registers + val iRightReg = Array.fill(regSize) { Bool(true) } + val oRightReg = Array.fill(regSize) { Bool(true) } + // Flatten the regmap into (RegIndex:Int, Offset:Int, field:RegField) val flat = regmap.map { case (reg, fields) => val offsets = fields.scanLeft(0)(_ + _.width).init val index = regIndexI(reg) + val uint = UInt(reg, width = inBits) + if (undefZero) { + iRightReg(index) = ((front.bits.index ^ uint) & maskMatch) === UInt(0) + oRightReg(index) = ((back .bits.index ^ uint) & maskMatch) === UInt(0) + } // println("mapping 0x%x -> 0x%x for 0x%x/%d".format(reg, index, mask, maskBits)) (offsets zip fields) map { case (o, f) => (index, o, f) } }.flatten - val out = Wire(Irrevocable(new RegMapperOutput(params))) - val front = Wire(Irrevocable(new RegMapperInput(params))) - front.bits := in.bits - - // Must this device pipeline the control channel? - val pipelined = flat.map(_._3.pipelined).reduce(_ || _) - val depth = concurrency.getOrElse(if (pipelined) 1 else 0) - require (depth >= 0) - require (!pipelined || depth > 0) - val back = if (depth > 0) Queue(front, depth, pipe = depth == 1) else front - // Forward declaration of all flow control signals val rivalid = Wire(Vec(flat.size, Bool())) val wivalid = Wire(Vec(flat.size, Bool())) @@ -122,10 +136,10 @@ object RegMapper } // Is the selected register ready? - val rifireMux = Vec(rifire.map(_.reduce(_ && _))) - val wifireMux = Vec(wifire.map(_.reduce(_ && _))) - val rofireMux = Vec(rofire.map(_.reduce(_ && _))) - val wofireMux = Vec(wofire.map(_.reduce(_ && _))) + val rifireMux = Vec(rifire.zipWithIndex.map { case (seq, i) => !iRightReg(i) || seq.reduce(_ && _)}) + val wifireMux = Vec(wifire.zipWithIndex.map { case (seq, i) => !iRightReg(i) || seq.reduce(_ && _)}) + val rofireMux = Vec(rofire.zipWithIndex.map { case (seq, i) => !oRightReg(i) || seq.reduce(_ && _)}) + val wofireMux = Vec(wofire.zipWithIndex.map { case (seq, i) => !oRightReg(i) || seq.reduce(_ && _)}) val iindex = regIndexU(front.bits.index) val oindex = regIndexU(back .bits.index) val iready = Mux(front.bits.read, rifireMux(iindex), wifireMux(iindex)) @@ -138,8 +152,8 @@ object RegMapper out.valid := back.valid && oready // Which register is touched? - val frontSel = UIntToOH(iindex) - val backSel = UIntToOH(oindex) + val frontSel = UIntToOH(iindex) & Cat(iRightReg.reverse) + val backSel = UIntToOH(oindex) & Cat(oRightReg.reverse) // Include the per-register one-hot selected criteria for (reg <- 0 until regSize) { @@ -159,9 +173,9 @@ object RegMapper } out.bits.read := back.bits.read - out.bits.data := Vec(dataOut)(oindex) + out.bits.data := Mux(Vec(oRightReg)(oindex), Vec(dataOut)(oindex), UInt(0)) out.bits.extra := back.bits.extra - (endIndex, out) + out } } diff --git a/src/main/scala/uncore/tilelink2/RegisterRouter.scala b/src/main/scala/uncore/tilelink2/RegisterRouter.scala index 7cf6184d..9db44546 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouter.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouter.scala @@ -27,7 +27,7 @@ class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatB val (sourceEnd, sourceOff) = (edge.bundle.sourceBits + sizeEnd, sizeEnd) val (addrLoEnd, addrLoOff) = (log2Up(beatBytes) + sourceEnd, sourceEnd) - val params = RegMapperParams(log2Up(address.mask+1), beatBytes, addrLoEnd) + val params = RegMapperParams(log2Up((address.mask+1)/beatBytes), beatBytes, addrLoEnd) val in = Wire(Decoupled(new RegMapperInput(params))) in.bits.read := a.bits.opcode === TLMessages.Get in.bits.index := a.bits.addr_hi @@ -36,10 +36,7 @@ class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatB in.bits.extra := Cat(edge.addr_lo(a.bits), a.bits.source, a.bits.size) // Invoke the register map builder - val (endIndex, out) = RegMapper(beatBytes, concurrency, undefZero, in, mapping:_*) - - // All registers must fit inside the device address space - require (address.mask >= (endIndex-1)*beatBytes) + val out = RegMapper(beatBytes, concurrency, undefZero, in, mapping:_*) // No flow control needed in.valid := a.valid From 915a929af1a1db77bcafe393cd60476aa2c1abab Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 16 Sep 2016 17:25:00 -0700 Subject: [PATCH 06/28] tilelink2: Nodes can now mix context into parameters --- src/main/scala/uncore/tilelink2/Nodes.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/Nodes.scala b/src/main/scala/uncore/tilelink2/Nodes.scala index a0de1a84..c9482e69 100644 --- a/src/main/scala/uncore/tilelink2/Nodes.scala +++ b/src/main/scala/uncore/tilelink2/Nodes.scala @@ -17,6 +17,9 @@ abstract class NodeImp[PO, PI, EO, EI, B <: Data] def bundleO(eo: Seq[EO]): Vec[B] def bundleI(ei: Seq[EI]): Vec[B] def connect(bo: B, eo: EO, bi: B, ei: EI)(implicit sourceInfo: SourceInfo): Unit + // If you want to track parameters as they flow through nodes, overload these: + def mixO(po: PO, node: BaseNode[PO, PI, EO, EI, B]): PO = po + def mixI(pi: PI, node: BaseNode[PO, PI, EO, EI, B]): PI = pi } class RootNode @@ -59,12 +62,12 @@ class BaseNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])( private lazy val oParams : Seq[PO] = { val o = oFn(oPorts.size, iPorts.map{ case (i, n) => n.oParams(i) }) reqE(oPorts.size, o.size) - o + o.map(imp.mixO(_, this)) } private lazy val iParams : Seq[PI] = { val i = iFn(iPorts.size, oPorts.map{ case (o, n) => n.iParams(o) }) reqE(i.size, iPorts.size) - i + i.map(imp.mixI(_, this)) } lazy val edgesOut = (oPorts zip oParams).map { case ((i, n), o) => imp.edgeO(o, n.iParams(i)) } From 258723483864fe8f2faab6715f1e7df59eb98c75 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 16 Sep 2016 17:25:22 -0700 Subject: [PATCH 07/28] tilelink2 TLNodes: capture nodePath in {Client,Manager}Parameters --- src/main/scala/uncore/tilelink2/TLNodes.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/scala/uncore/tilelink2/TLNodes.scala b/src/main/scala/uncore/tilelink2/TLNodes.scala index ea0d2c77..7e1c79b5 100644 --- a/src/main/scala/uncore/tilelink2/TLNodes.scala +++ b/src/main/scala/uncore/tilelink2/TLNodes.scala @@ -24,6 +24,11 @@ object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TL TLMonitor.legalize(bo, eo) bi <> bo } + + override def mixO(po: TLClientPortParameters, node: TLBaseNode): TLClientPortParameters = + po.copy(clients = po.clients.map { c => c.copy (nodePath = node +: c.nodePath) }) + override def mixI(pi: TLManagerPortParameters, node: TLBaseNode): TLManagerPortParameters = + pi.copy(managers = pi.managers.map { m => m.copy (nodePath = node +: m.nodePath) }) } case class TLIdentityNode() extends IdentityNode(TLImp) From a357c1d42e655753ea23aec00db3a4f77fdaf7fa Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 16 Sep 2016 17:26:14 -0700 Subject: [PATCH 08/28] tilelink2: create DTS for devices automagically --- src/main/scala/uncore/tilelink2/Parameters.scala | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/scala/uncore/tilelink2/Parameters.scala b/src/main/scala/uncore/tilelink2/Parameters.scala index 7c49e792..61bdad72 100644 --- a/src/main/scala/uncore/tilelink2/Parameters.scala +++ b/src/main/scala/uncore/tilelink2/Parameters.scala @@ -127,7 +127,8 @@ case class TLManagerParameters( supportsPutPartial: TransferSizes = TransferSizes.none, supportsHint: TransferSizes = TransferSizes.none, // If fifoId=Some, all accesses sent to the same fifoId are executed and ACK'd in FIFO order - fifoId: Option[Int] = None) + fifoId: Option[Int] = None, + customDTS: Option[String]= None) { address.combinations(2).foreach({ case Seq(x,y) => require (!x.overlaps(y)) @@ -143,6 +144,18 @@ case class TLManagerParameters( supportsPutFull.max, supportsPutPartial.max).max + // Generate the config string (in future device tree) + lazy val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") + lazy val dts = customDTS.getOrElse { + val header = s"${name} {\n" + val middle = address.map { a => + require (!a.strided) // Config String does not support this + " addr 0x%x;\n size 0x%x;\n".format(a.base, a.mask+1) + } + val footer = "}\n" + header + middle.reduce(_ + _) + footer + } + // The device had better not support a transfer larger than it's alignment address.foreach({ case a => require (a.alignment1 >= maxTransfer-1) From 8876d83640cb28857528da83460285bc94113962 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 16 Sep 2016 17:26:50 -0700 Subject: [PATCH 09/28] Prci: preserve Andrew's preferred clint name --- src/main/scala/uncore/devices/Prci.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/scala/uncore/devices/Prci.scala b/src/main/scala/uncore/devices/Prci.scala index ff9800dc..1d324005 100644 --- a/src/main/scala/uncore/devices/Prci.scala +++ b/src/main/scala/uncore/devices/Prci.scala @@ -87,3 +87,6 @@ class CoreplexLocalInterrupter(c: CoreplexLocalInterrupterConfig)(implicit val p extends TLRegisterRouter(c.address, 0, c.size, None, c.beatBytes, false)( new TLRegBundle((c, p), _) with CoreplexLocalInterrupterBundle)( new TLRegModule((c, p), _, _) with CoreplexLocalInterrupterModule) +{ + override def name = "clint" // defaul is "CoreplexLocalInterrupter" +} From b5ce6150c7e73004c2d6e3bb8d683f2e16303834 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 16 Sep 2016 17:27:49 -0700 Subject: [PATCH 10/28] Periphery: dynamically create address map + config string for TL2 --- src/main/scala/rocketchip/Periphery.scala | 14 +------------ src/main/scala/rocketchip/Top.scala | 10 ++++++---- src/main/scala/rocketchip/Utils.scala | 24 ++++++++++++++++++----- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/main/scala/rocketchip/Periphery.scala b/src/main/scala/rocketchip/Periphery.scala index f2d867cc..0b2ae458 100644 --- a/src/main/scala/rocketchip/Periphery.scala +++ b/src/main/scala/rocketchip/Periphery.scala @@ -289,10 +289,6 @@ trait PeripheryCoreplexLocalInterrupter extends LazyModule with HasPeripheryPara val clint = LazyModule(new CoreplexLocalInterrupter(clintConfig)(innerMMIOParams)) // The periphery bus is 32-bit, so we may need to adapt its width to XLen clint.node := TLFragmenter(TLWidthWidget(peripheryBus.node, 4), beatBytes, 256) - - // TL1 legacy - val pDevices: ResourceManager[AddrMapEntry] - pDevices.add(AddrMapEntry("clint", MemRange(clintConfig.address, clintConfig.size, MemAttr(AddrMapProt.RW)))) } trait PeripheryCoreplexLocalInterrupterBundle { @@ -315,12 +311,8 @@ trait PeripheryBootROM extends LazyModule { implicit val p: Parameters val peripheryBus: TLXbar - val rom = LazyModule(new TLROM(0x1000, 0x1000, GenerateBootROM(p))) + val rom = LazyModule(new TLROM(0x1000, 0x1000, GenerateBootROM(p)) { override def name = "bootrom" }) rom.node := TLFragmenter(peripheryBus.node, 4, 256) - - // TL1 legacy address map - val pDevices: ResourceManager[AddrMapEntry] - pDevices.add(AddrMapEntry("bootrom", MemRange(0x1000, 4096, MemAttr(AddrMapProt.RX)))) } trait PeripheryBootROMBundle { @@ -344,10 +336,6 @@ trait PeripheryTestRAM extends LazyModule { val sram = LazyModule(new TLRAM(AddressSet(ramBase, ramSize-1))) sram.node := TLFragmenter(peripheryBus.node, 4, 256) - - // TL1 legacy address map - val pDevices: ResourceManager[AddrMapEntry] - pDevices.add(AddrMapEntry("testram", MemRange(ramBase, ramSize, MemAttr(AddrMapProt.RW)))) } trait PeripheryTestRAMBundle { diff --git a/src/main/scala/rocketchip/Top.scala b/src/main/scala/rocketchip/Top.scala index 30bddc3c..6ea9069c 100644 --- a/src/main/scala/rocketchip/Top.scala +++ b/src/main/scala/rocketchip/Top.scala @@ -27,6 +27,10 @@ abstract class BaseTop(q: Parameters) extends LazyModule { val pBusMasters = new RangeManager val pDevices = new ResourceManager[AddrMapEntry] + // Add a peripheral bus + val peripheryBus = LazyModule(new TLXbar) + lazy val peripheryManagers = peripheryBus.node.edgesIn(0).manager.managers + lazy val c = CoreplexConfig( nTiles = q(NTiles), nExtInterrupts = pInterrupts.sum, @@ -36,16 +40,14 @@ abstract class BaseTop(q: Parameters) extends LazyModule { hasExtMMIOPort = true ) - lazy val genGlobalAddrMap = GenerateGlobalAddrMap(q, pDevices.get) + lazy val genGlobalAddrMap = GenerateGlobalAddrMap(q, pDevices.get, peripheryManagers) private val qWithMap = q.alterPartial({case GlobalAddrMap => genGlobalAddrMap}) - lazy val genConfigString = GenerateConfigString(qWithMap, c, pDevices.get) + lazy val genConfigString = GenerateConfigString(qWithMap, c, pDevices.get, peripheryManagers) implicit val p = qWithMap.alterPartial({ case ConfigString => genConfigString case NCoreplexExtClients => pBusMasters.sum}) - // Add a peripheral bus - val peripheryBus = LazyModule(new TLXbar) val legacy = LazyModule(new TLLegacy()(p.alterPartial({ case TLId => "L2toMMIO" }))) peripheryBus.node := TLBuffer(TLWidthWidget(TLHintHandler(legacy.node), legacy.tlDataBytes)) diff --git a/src/main/scala/rocketchip/Utils.scala b/src/main/scala/rocketchip/Utils.scala index bb32006b..b045e8c7 100644 --- a/src/main/scala/rocketchip/Utils.scala +++ b/src/main/scala/rocketchip/Utils.scala @@ -8,6 +8,7 @@ import uncore.devices._ import rocket._ import rocket.Util._ import coreplex._ +import uncore.tilelink2._ import java.nio.file.{Files, Paths} import java.nio.{ByteBuffer, ByteOrder} @@ -51,7 +52,7 @@ class GlobalVariable[T] { } object GenerateGlobalAddrMap { - def apply(p: Parameters, pDevicesEntries: Seq[AddrMapEntry]) = { + def apply(p: Parameters, pDevicesEntries: Seq[AddrMapEntry], peripheryManagers: Seq[TLManagerParameters]) = { lazy val intIOAddrMap: AddrMap = { val entries = collection.mutable.ArrayBuffer[AddrMapEntry]() entries += AddrMapEntry("debug", MemSize(4096, MemAttr(AddrMapProt.RWX))) @@ -64,8 +65,20 @@ object GenerateGlobalAddrMap { new AddrMap(entries) } - lazy val tl2AddrMap = new AddrMap(pDevicesEntries, collapse = true) - lazy val extIOAddrMap = new AddrMap(AddrMapEntry("TL2", tl2AddrMap) +: p(ExtMMIOPorts), collapse = true) + lazy val tl2Devices = peripheryManagers.map { manager => + val attr = MemAttr( + (if (manager.supportsGet) AddrMapProt.R else 0) | + (if (manager.supportsPutFull) AddrMapProt.W else 0) | + (if (manager.executable) AddrMapProt.X else 0)) + val name = manager.nodePath.last.lazyModule.name // !!! + manager.address.zipWithIndex.map { case (address, i) => + require (!address.strided) // TL1 can't do this + AddrMapEntry(s"${name}", MemRange(address.base, address.mask+1, attr)) + } + }.flatten + + lazy val tl2AddrMap = new AddrMap(tl2Devices, collapse = true) + lazy val extIOAddrMap = new AddrMap(AddrMapEntry("TL2", tl2AddrMap) +: (p(ExtMMIOPorts) ++ pDevicesEntries), collapse = true) val memBase = 0x80000000L val memSize = p(ExtMemSize) @@ -80,7 +93,7 @@ object GenerateGlobalAddrMap { } object GenerateConfigString { - def apply(p: Parameters, c: CoreplexConfig, pDevicesEntries: Seq[AddrMapEntry]) = { + def apply(p: Parameters, c: CoreplexConfig, pDevicesEntries: Seq[AddrMapEntry], peripheryManagers: Seq[TLManagerParameters]) = { val addrMap = p(GlobalAddrMap) val plicAddr = addrMap("io:int:plic").start val clint = CoreplexLocalInterrupterConfig(0, addrMap("io:ext:TL2:clint").start) @@ -136,12 +149,13 @@ object GenerateConfigString { } res append "};\n" pDevicesEntries foreach { entry => - val region = addrMap("io:ext:TL2:" + entry.name) + val region = addrMap("io:ext:" + entry.name) res append s"${entry.name} {\n" res append s" addr 0x${region.start.toString(16)};\n" res append s" size 0x${region.size.toString(16)}; \n" res append "}\n" } + peripheryManagers.foreach { manager => res append manager.dts } res append '\u0000' res.toString } From a9382b3116c44158fa2b9f9f39f4687e39bd13c5 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 16 Sep 2016 17:47:20 -0700 Subject: [PATCH 11/28] Periphery: test bench looks for "testram" --- src/main/scala/rocketchip/Periphery.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/rocketchip/Periphery.scala b/src/main/scala/rocketchip/Periphery.scala index 0b2ae458..c69ef131 100644 --- a/src/main/scala/rocketchip/Periphery.scala +++ b/src/main/scala/rocketchip/Periphery.scala @@ -334,7 +334,7 @@ trait PeripheryTestRAM extends LazyModule { val ramBase = 0x52000000 val ramSize = 0x1000 - val sram = LazyModule(new TLRAM(AddressSet(ramBase, ramSize-1))) + val sram = LazyModule(new TLRAM(AddressSet(ramBase, ramSize-1)) { override def name = "testram" }) sram.node := TLFragmenter(peripheryBus.node, 4, 256) } From 5c858685aa1701a1cbb65174521090316bb00047 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 16 Sep 2016 18:03:49 -0700 Subject: [PATCH 12/28] Utils: support managers with multiple addresses --- src/main/scala/rocketchip/Utils.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/scala/rocketchip/Utils.scala b/src/main/scala/rocketchip/Utils.scala index b045e8c7..8f27cc1b 100644 --- a/src/main/scala/rocketchip/Utils.scala +++ b/src/main/scala/rocketchip/Utils.scala @@ -70,10 +70,11 @@ object GenerateGlobalAddrMap { (if (manager.supportsGet) AddrMapProt.R else 0) | (if (manager.supportsPutFull) AddrMapProt.W else 0) | (if (manager.executable) AddrMapProt.X else 0)) - val name = manager.nodePath.last.lazyModule.name // !!! + val multi = manager.address.size > 1 manager.address.zipWithIndex.map { case (address, i) => require (!address.strided) // TL1 can't do this - AddrMapEntry(s"${name}", MemRange(address.base, address.mask+1, attr)) + val name = manager.name + (if (multi) ".%d".format(i) else "") + AddrMapEntry(name, MemRange(address.base, address.mask+1, attr)) } }.flatten From e3d2bd33234a2bae6d02213e6af3e3a4b7f68dc4 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Sat, 17 Sep 2016 00:16:00 -0700 Subject: [PATCH 13/28] Top: print memory region properties, RWX [C] --- src/main/scala/rocketchip/Top.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/scala/rocketchip/Top.scala b/src/main/scala/rocketchip/Top.scala index 6ea9069c..ef4a98b4 100644 --- a/src/main/scala/rocketchip/Top.scala +++ b/src/main/scala/rocketchip/Top.scala @@ -76,7 +76,12 @@ class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle](val p: Parameters, l: L, val name = entry.name val start = entry.region.start val end = entry.region.start + entry.region.size - 1 - println(f"\t$name%s $start%x - $end%x") + val prot = entry.region.attr.prot + val protStr = (if ((prot & AddrMapProt.R) > 0) "R" else "") + + (if ((prot & AddrMapProt.W) > 0) "W" else "") + + (if ((prot & AddrMapProt.X) > 0) "X" else "") + val cacheable = if (entry.region.attr.cacheable) " [C]" else "" + println(f"\t$name%s $start%x - $end%x, $protStr$cacheable") } println("Generated Configuration String") From c70045b8b3020f58fff0e2c3b3869a1e9917d6f7 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Sat, 17 Sep 2016 00:16:40 -0700 Subject: [PATCH 14/28] Utils: express cacheability from TL2 to TL1 --- src/main/scala/rocketchip/Utils.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/scala/rocketchip/Utils.scala b/src/main/scala/rocketchip/Utils.scala index 8f27cc1b..4ece9f38 100644 --- a/src/main/scala/rocketchip/Utils.scala +++ b/src/main/scala/rocketchip/Utils.scala @@ -66,10 +66,16 @@ object GenerateGlobalAddrMap { } lazy val tl2Devices = peripheryManagers.map { manager => + val cacheable = manager.regionType match { + case RegionType.CACHED => true + case RegionType.TRACKED => true + case RegionType.UNCACHED => true + case _ => false + } val attr = MemAttr( (if (manager.supportsGet) AddrMapProt.R else 0) | (if (manager.supportsPutFull) AddrMapProt.W else 0) | - (if (manager.executable) AddrMapProt.X else 0)) + (if (manager.executable) AddrMapProt.X else 0), cacheable) val multi = manager.address.size > 1 manager.address.zipWithIndex.map { case (address, i) => require (!address.strided) // TL1 can't do this From e749558190ce6d373b13d7c2d0e73fb61acd966c Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Sat, 17 Sep 2016 00:19:09 -0700 Subject: [PATCH 15/28] ROM: optionally (default: true) executable --- src/main/scala/uncore/devices/Rom.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/scala/uncore/devices/Rom.scala b/src/main/scala/uncore/devices/Rom.scala index 8e31cbe5..2986f85c 100644 --- a/src/main/scala/uncore/devices/Rom.scala +++ b/src/main/scala/uncore/devices/Rom.scala @@ -8,11 +8,12 @@ import uncore.tilelink2._ import uncore.util._ import cde.{Parameters, Field} -class TLROM(val base: BigInt, val size: Int, contentsDelayed: => Seq[Byte], beatBytes: Int = 4) extends LazyModule +class TLROM(val base: BigInt, val size: Int, contentsDelayed: => Seq[Byte], executable: Boolean = true, beatBytes: Int = 4) extends LazyModule { val node = TLManagerNode(beatBytes, TLManagerParameters( address = List(AddressSet(base, size-1)), regionType = RegionType.UNCACHED, + executable = executable, supportsGet = TransferSizes(1, beatBytes), fifoId = Some(0))) From 6c3269a1d863c0f049f774b75e9e45ffffeae1b5 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Sat, 17 Sep 2016 00:19:37 -0700 Subject: [PATCH 16/28] SRAM: optionally (default: true) executable --- src/main/scala/uncore/tilelink2/SRAM.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/SRAM.scala b/src/main/scala/uncore/tilelink2/SRAM.scala index 2970eb78..84a775f2 100644 --- a/src/main/scala/uncore/tilelink2/SRAM.scala +++ b/src/main/scala/uncore/tilelink2/SRAM.scala @@ -4,12 +4,12 @@ package uncore.tilelink2 import Chisel._ -class TLRAM(address: AddressSet, beatBytes: Int = 4) extends LazyModule +class TLRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4) extends LazyModule { val node = TLManagerNode(beatBytes, TLManagerParameters( address = List(address), regionType = RegionType.UNCACHED, - executable = true, + executable = executable, supportsGet = TransferSizes(1, beatBytes), supportsPutPartial = TransferSizes(1, beatBytes), supportsPutFull = TransferSizes(1, beatBytes), From 01c1886b9d9446b123ae822f909d065e6611b1bf Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Sat, 17 Sep 2016 00:56:21 -0700 Subject: [PATCH 17/28] Utils: cacheable only if there is a cache manager --- src/main/scala/rocketchip/Utils.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/scala/rocketchip/Utils.scala b/src/main/scala/rocketchip/Utils.scala index 4ece9f38..9dec5cb3 100644 --- a/src/main/scala/rocketchip/Utils.scala +++ b/src/main/scala/rocketchip/Utils.scala @@ -69,7 +69,6 @@ object GenerateGlobalAddrMap { val cacheable = manager.regionType match { case RegionType.CACHED => true case RegionType.TRACKED => true - case RegionType.UNCACHED => true case _ => false } val attr = MemAttr( From e43750854879c702d493d10ec63a1841697e97b7 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Sat, 17 Sep 2016 13:56:35 -0700 Subject: [PATCH 18/28] tilelink2: track interrupt connectivity like in TL2 --- .../scala/uncore/tilelink2/IntNodes.scala | 32 +++++++++++++++---- .../uncore/tilelink2/RegisterRouter.scala | 2 +- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/IntNodes.scala b/src/main/scala/uncore/tilelink2/IntNodes.scala index df3e6152..a4acc443 100644 --- a/src/main/scala/uncore/tilelink2/IntNodes.scala +++ b/src/main/scala/uncore/tilelink2/IntNodes.scala @@ -21,9 +21,19 @@ object IntRange implicit def apply(end: Int): IntRange = apply(0, end) } -case class IntSourceParameters(device: String, range: IntRange) +case class IntSourceParameters( + range: IntRange, + nodePath: Seq[IntBaseNode] = Seq()) +{ + val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") +} + +case class IntSinkParameters( + nodePath: Seq[IntBaseNode] = Seq()) +{ + val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") +} -case class IntSinkPortParameters() case class IntSourcePortParameters(sources: Seq[IntSourceParameters]) { val num = sources.map(_.range.size).sum @@ -32,6 +42,9 @@ case class IntSourcePortParameters(sources: Seq[IntSourceParameters]) // The interrupts must perfectly cover the range require (sources.map(_.range.end).max == num) } + +case class IntSinkPortParameters(sinks: Seq[IntSinkParameters]) + case class IntEdge(source: IntSourcePortParameters, sink: IntSinkPortParameters) object IntImp extends NodeImp[IntSourcePortParameters, IntSinkPortParameters, IntEdge, IntEdge, Vec[Bool]] @@ -52,16 +65,21 @@ object IntImp extends NodeImp[IntSourcePortParameters, IntSinkPortParameters, In // Cannot use bulk connect, because the widths could differ (bo zip bi) foreach { case (o, i) => i := o } } + + override def mixO(po: IntSourcePortParameters, node: IntBaseNode): IntSourcePortParameters = + po.copy(sources = po.sources.map { s => s.copy (nodePath = node +: s.nodePath) }) + override def mixI(pi: IntSinkPortParameters, node: IntBaseNode): IntSinkPortParameters = + pi.copy(sinks = pi.sinks.map { s => s.copy (nodePath = node +: s.nodePath) }) } case class IntIdentityNode() extends IdentityNode(IntImp) case class IntOutputNode() extends OutputNode(IntImp) case class IntInputNode() extends InputNode(IntImp) -case class IntSourceNode(device: String, num: Int) extends SourceNode(IntImp)( - IntSourcePortParameters(Seq(IntSourceParameters(device, num))), - (if (num == 0) 0 else 1) to 1) -case class IntSinkNode() extends SinkNode(IntImp)(IntSinkPortParameters()) +case class IntSourceNode(num: Int) extends SourceNode(IntImp)( + IntSourcePortParameters(Seq(IntSourceParameters(num))), (if (num == 0) 0 else 1) to 1) +case class IntSinkNode() extends SinkNode(IntImp)( + IntSinkPortParameters(Seq(IntSinkParameters()))) case class IntAdapterNode( sourceFn: Seq[IntSourcePortParameters] => IntSourcePortParameters, @@ -75,7 +93,7 @@ class IntXbar extends LazyModule val intnode = IntAdapterNode( numSourcePorts = 1 to 1, // does it make sense to have more than one interrupt sink? numSinkPorts = 1 to 128, - sinkFn = { _ => IntSinkPortParameters() }, + sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) }, sourceFn = { seq => IntSourcePortParameters((seq zip seq.map(_.num).scanLeft(0)(_+_).init).map { case (s, o) => s.sources.map(z => z.copy(range = z.range.offset(o))) diff --git a/src/main/scala/uncore/tilelink2/RegisterRouter.scala b/src/main/scala/uncore/tilelink2/RegisterRouter.scala index 9db44546..e7cd9837 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouter.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouter.scala @@ -75,7 +75,7 @@ object TLRegisterNode abstract class TLRegisterRouterBase(address: AddressSet, interrupts: Int, concurrency: Option[Int], beatBytes: Int, undefZero: Boolean) extends LazyModule { val node = TLRegisterNode(address, concurrency, beatBytes, undefZero) - val intnode = IntSourceNode(name + s" @ ${address.base}", interrupts) + val intnode = IntSourceNode(interrupts) } case class TLRegBundleArg(interrupts: Vec[Vec[Bool]], in: Vec[TLBundle]) From fa0f119f3ca1bcf15da30996faf4ef7625531302 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Sat, 17 Sep 2016 15:27:39 -0700 Subject: [PATCH 19/28] tilelink2: consider the implications of negative address mask --- .../scala/uncore/tilelink2/Parameters.scala | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/Parameters.scala b/src/main/scala/uncore/tilelink2/Parameters.scala index 61bdad72..af8343cb 100644 --- a/src/main/scala/uncore/tilelink2/Parameters.scala +++ b/src/main/scala/uncore/tilelink2/Parameters.scala @@ -82,26 +82,31 @@ case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet] { // Forbid misaligned base address (and empty sets) require ((base & mask) == 0) + require (base >= 0) // TL2 address widths are not fixed => negative is ambiguous + // We do allow negative mask (=> ignore all high bits) - def contains(x: BigInt) = ~(~(x ^ base) | mask) == 0 - def contains(x: UInt) = ~(~(x ^ UInt(base)) | UInt(mask)) === UInt(0) + def contains(x: BigInt) = ((x ^ base) & ~mask) == 0 + def contains(x: UInt) = ((x ^ UInt(base)).zext() & SInt(~mask)) === SInt(0) // overlap iff bitwise: both care (~mask0 & ~mask1) => both equal (base0=base1) def overlaps(x: AddressSet) = (~(mask | x.mask) & (base ^ x.base)) == 0 - // contains iff bitwise: x.mask => mask && contains(x.base) def contains(x: AddressSet) = ((x.mask | (base ^ x.base)) & ~mask) == 0 - // 1 less than the number of bytes to which the manager should be aligned - def alignment1 = ((mask + 1) & ~mask) - 1 - def max = base | mask - // A strided slave serves discontiguous ranges - def strided = alignment1 != mask + // The number of bytes to which the manager must be aligned + def alignment = ((mask + 1) & ~mask) + + // Is this a contiguous memory range + def contiguous = alignment == mask+1 + def strided = alignment != mask+1 + + def finite = mask >= 0 + def max = { require (finite); base | mask } // Widen the match function to ignore all bits in imask def widen(imask: BigInt) = AddressSet(base & ~imask, mask | imask) - // AddressSets have one natural Ordering (the containment order) + // AddressSets have one natural Ordering (the containment order, if contiguous) def compare(x: AddressSet) = { val primary = (this.base - x.base).signum // smallest address first val secondary = (x.mask - this.mask).signum // largest mask first @@ -109,7 +114,13 @@ case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet] } // We always want to see things in hex - override def toString() = "AddressSet(0x%x, 0x%x)".format(base, mask) + override def toString() = { + if (mask >= 0) { + "AddressSet(0x%x, 0x%x)".format(base, mask) + } else { + "AddressSet(0x%x, ~0x%x)".format(base, ~mask) + } + } } case class TLManagerParameters( @@ -130,6 +141,7 @@ case class TLManagerParameters( fifoId: Option[Int] = None, customDTS: Option[String]= None) { + address.foreach { a => require (a.finite) } address.combinations(2).foreach({ case Seq(x,y) => require (!x.overlaps(y)) }) @@ -144,8 +156,9 @@ case class TLManagerParameters( supportsPutFull.max, supportsPutPartial.max).max + val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") + // Generate the config string (in future device tree) - lazy val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") lazy val dts = customDTS.getOrElse { val header = s"${name} {\n" val middle = address.map { a => @@ -158,7 +171,7 @@ case class TLManagerParameters( // The device had better not support a transfer larger than it's alignment address.foreach({ case a => - require (a.alignment1 >= maxTransfer-1) + require (a.alignment >= maxTransfer) }) } @@ -256,6 +269,8 @@ case class TLClientParameters( supportsGet.max, supportsPutFull.max, supportsPutPartial.max).max + + val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") } case class TLClientPortParameters(clients: Seq[TLClientParameters]) { From 76d8ed6a69454f6d35ac2ea8cb70733dfa47a859 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Sat, 17 Sep 2016 15:31:35 -0700 Subject: [PATCH 20/28] tilelink2: remove 'strided'; !contiguous is clearer --- src/main/scala/rocketchip/Utils.scala | 2 +- src/main/scala/uncore/tilelink2/Parameters.scala | 4 +--- src/main/scala/uncore/tilelink2/RegisterRouter.scala | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/scala/rocketchip/Utils.scala b/src/main/scala/rocketchip/Utils.scala index 9dec5cb3..1436050a 100644 --- a/src/main/scala/rocketchip/Utils.scala +++ b/src/main/scala/rocketchip/Utils.scala @@ -77,7 +77,7 @@ object GenerateGlobalAddrMap { (if (manager.executable) AddrMapProt.X else 0), cacheable) val multi = manager.address.size > 1 manager.address.zipWithIndex.map { case (address, i) => - require (!address.strided) // TL1 can't do this + require (address.contiguous) // TL1 needs this val name = manager.name + (if (multi) ".%d".format(i) else "") AddrMapEntry(name, MemRange(address.base, address.mask+1, attr)) } diff --git a/src/main/scala/uncore/tilelink2/Parameters.scala b/src/main/scala/uncore/tilelink2/Parameters.scala index af8343cb..4c7d2603 100644 --- a/src/main/scala/uncore/tilelink2/Parameters.scala +++ b/src/main/scala/uncore/tilelink2/Parameters.scala @@ -95,10 +95,8 @@ case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet] // The number of bytes to which the manager must be aligned def alignment = ((mask + 1) & ~mask) - // Is this a contiguous memory range def contiguous = alignment == mask+1 - def strided = alignment != mask+1 def finite = mask >= 0 def max = { require (finite); base | mask } @@ -162,7 +160,7 @@ case class TLManagerParameters( lazy val dts = customDTS.getOrElse { val header = s"${name} {\n" val middle = address.map { a => - require (!a.strided) // Config String does not support this + require (a.contiguous) // Config String is not so flexible " addr 0x%x;\n size 0x%x;\n".format(a.base, a.mask+1) } val footer = "}\n" diff --git a/src/main/scala/uncore/tilelink2/RegisterRouter.scala b/src/main/scala/uncore/tilelink2/RegisterRouter.scala index e7cd9837..7b6bdc21 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouter.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouter.scala @@ -12,7 +12,7 @@ class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatB supportsPutFull = TransferSizes(1, beatBytes), fifoId = Some(0))) // requests are handled in order { - require (!address.strided) + require (address.contiguous) // Calling this method causes the matching TL2 bundle to be // configured to route all requests to the listed RegFields. From b4baae4214910eb9ab4d8f198280acd9a764b8fd Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Sat, 17 Sep 2016 16:13:46 -0700 Subject: [PATCH 21/28] tilelink2: minimize Xbar decode logic --- src/main/scala/uncore/tilelink2/Fuzzer.scala | 8 +++-- src/main/scala/uncore/tilelink2/Xbar.scala | 31 ++++++++++++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/Fuzzer.scala b/src/main/scala/uncore/tilelink2/Fuzzer.scala index deeb936b..30828c61 100644 --- a/src/main/scala/uncore/tilelink2/Fuzzer.scala +++ b/src/main/scala/uncore/tilelink2/Fuzzer.scala @@ -218,14 +218,18 @@ class ClockDivider extends BlackBox { class TLFuzzRAM extends LazyModule { val model = LazyModule(new TLRAMModel) - val ram = LazyModule(new TLRAM(AddressSet(0, 0x3ff))) + val ram = LazyModule(new TLRAM(AddressSet(0x800, 0x7ff))) + val ram2 = LazyModule(new TLRAM(AddressSet(0, 0x3ff), beatBytes = 16)) val gpio = LazyModule(new RRTest1(0x400)) val xbar = LazyModule(new TLXbar) + val xbar2= LazyModule(new TLXbar) val fuzz = LazyModule(new TLFuzzer(5000)) val cross = LazyModule(new TLAsyncCrossing) model.node := fuzz.node - xbar.node := TLWidthWidget(TLHintHandler(model.node), 16) + xbar2.node := model.node + ram2.node := TLFragmenter(xbar2.node, 16, 256) + xbar.node := TLWidthWidget(TLHintHandler(xbar2.node), 16) cross.node := TLFragmenter(TLBuffer(xbar.node), 4, 256) ram.node := cross.node gpio.node := TLFragmenter(TLBuffer(xbar.node), 4, 32) diff --git a/src/main/scala/uncore/tilelink2/Xbar.scala b/src/main/scala/uncore/tilelink2/Xbar.scala index 9302ea10..4a5bbf40 100644 --- a/src/main/scala/uncore/tilelink2/Xbar.scala +++ b/src/main/scala/uncore/tilelink2/Xbar.scala @@ -75,6 +75,23 @@ class TLXbar(policy: (Vec[Bool], Bool) => Seq[Bool] = TLXbar.lowestIndex) extend val inputIdRanges = mapInputIds(node.edgesIn.map(_.client)) val outputIdRanges = mapOutputIds(node.edgesOut.map(_.manager)) + // Find a good mask for address decoding + val port_addrs = node.edgesOut.map(_.manager.managers.map(_.address).flatten) + val routingMask = AddressDecoder(port_addrs) + val route_addrs = port_addrs.map(_.map(_.widen(~routingMask)).distinct) + val outputPorts = route_addrs.map(seq => (addr: UInt) => seq.map(_.contains(addr)).reduce(_ || _)) + + // Print the mapping + if (false) { + println("Xbar mapping:") + route_addrs.foreach { p => + print(" ") + p.foreach { a => print(s" ${a}") } + println("") + } + println("--") + } + // We need an intermediate size of bundle with the widest possible identifiers val wide_bundle = io.in(0).params.union(io.out(0).params) @@ -145,11 +162,15 @@ class TLXbar(policy: (Vec[Bool], Bool) => Seq[Bool] = TLXbar.lowestIndex) extend in(i).e.ready := Mux1C(grantedEIO(i), out.map(_.e.ready)) } - val requestAIO = Vec(in.map { i => Vec(node.edgesOut.map { o => i.a.valid && o.manager.contains(o.address(i.a.bits)) }) }) - val requestBOI = Vec(out.map { o => Vec(inputIdRanges.map { i => o.b.valid && i .contains(o.b.bits.source) }) }) - val requestCIO = Vec(in.map { i => Vec(node.edgesOut.map { o => i.c.valid && o.manager.contains(o.address(i.c.bits)) }) }) - val requestDOI = Vec(out.map { o => Vec(inputIdRanges.map { i => o.d.valid && i .contains(o.d.bits.source) }) }) - val requestEIO = Vec(in.map { i => Vec(outputIdRanges.map { o => i.e.valid && o .contains(i.e.bits.sink) }) }) + val addressA = (in zip node.edgesIn) map { case (i, e) => (i.a.valid, e.address(i.a.bits)) } + val addressC = (in zip node.edgesIn) map { case (i, e) => (i.c.valid, e.address(i.c.bits)) } + + val requestAIO = Vec(addressA.map { i => Vec(outputPorts.map { o => i._1 && o(i._2) }) }) + val requestCIO = Vec(addressC.map { i => Vec(outputPorts.map { o => i._1 && o(i._2) }) }) + + val requestBOI = Vec(out.map { o => Vec(inputIdRanges.map { i => o.b.valid && i.contains(o.b.bits.source) }) }) + val requestDOI = Vec(out.map { o => Vec(inputIdRanges.map { i => o.d.valid && i.contains(o.d.bits.source) }) }) + val requestEIO = Vec(in.map { i => Vec(outputIdRanges.map { o => i.e.valid && o.contains(i.e.bits.sink) }) }) val beatsA = Vec((in zip node.edgesIn) map { case (i, e) => e.numBeats(i.a.bits) }) val beatsB = Vec((out zip node.edgesOut) map { case (o, e) => e.numBeats(o.b.bits) }) From b11839f5a1743452e132fe6e29966e67c0edf500 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Sat, 17 Sep 2016 17:04:18 -0700 Subject: [PATCH 22/28] tilelink2: differentiate fast/safe address lookup cases --- src/main/scala/uncore/tilelink2/Edges.scala | 20 +++---- .../scala/uncore/tilelink2/Fragmenter.scala | 2 +- .../scala/uncore/tilelink2/HintHandler.scala | 4 +- src/main/scala/uncore/tilelink2/Monitor.scala | 22 ++++---- .../scala/uncore/tilelink2/Parameters.scala | 52 +++++++++++++------ .../scala/uncore/tilelink2/RAMModel.scala | 7 +-- 6 files changed, 63 insertions(+), 44 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/Edges.scala b/src/main/scala/uncore/tilelink2/Edges.scala index ceab2ca5..78c354f8 100644 --- a/src/main/scala/uncore/tilelink2/Edges.scala +++ b/src/main/scala/uncore/tilelink2/Edges.scala @@ -231,7 +231,7 @@ class TLEdgeOut( // Transfers def Acquire(fromSource: UInt, toAddress: UInt, lgSize: UInt, growPermissions: UInt) = { require (manager.anySupportAcquire) - val legal = manager.supportsAcquire(toAddress, lgSize) + val legal = manager.supportsAcquireFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.Acquire a.param := growPermissions @@ -245,7 +245,7 @@ class TLEdgeOut( def Release(fromSource: UInt, toAddress: UInt, lgSize: UInt, shrinkPermissions: UInt) = { require (manager.anySupportAcquire) - val legal = manager.supportsAcquire(toAddress, lgSize) + val legal = manager.supportsAcquireFast(toAddress, lgSize) val c = Wire(new TLBundleC(bundle)) c.opcode := TLMessages.Release c.param := shrinkPermissions @@ -260,7 +260,7 @@ class TLEdgeOut( def Release(fromSource: UInt, toAddress: UInt, lgSize: UInt, shrinkPermissions: UInt, data: UInt) = { require (manager.anySupportAcquire) - val legal = manager.supportsAcquire(toAddress, lgSize) + val legal = manager.supportsAcquireFast(toAddress, lgSize) val c = Wire(new TLBundleC(bundle)) c.opcode := TLMessages.ReleaseData c.param := shrinkPermissions @@ -308,7 +308,7 @@ class TLEdgeOut( // Accesses def Get(fromSource: UInt, toAddress: UInt, lgSize: UInt) = { require (manager.anySupportGet) - val legal = manager.supportsGet(toAddress, lgSize) + val legal = manager.supportsGetFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.Get a.param := UInt(0) @@ -322,7 +322,7 @@ class TLEdgeOut( def Put(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt) = { require (manager.anySupportPutFull) - val legal = manager.supportsPutFull(toAddress, lgSize) + val legal = manager.supportsPutFullFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.PutFullData a.param := UInt(0) @@ -336,7 +336,7 @@ class TLEdgeOut( def Put(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, mask : UInt) = { require (manager.anySupportPutPartial) - val legal = manager.supportsPutPartial(toAddress, lgSize) + val legal = manager.supportsPutPartialFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.PutPartialData a.param := UInt(0) @@ -350,7 +350,7 @@ class TLEdgeOut( def Arithmetic(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, atomic: UInt) = { require (manager.anySupportArithmetic) - val legal = manager.supportsArithmetic(toAddress, lgSize) + val legal = manager.supportsArithmeticFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.ArithmeticData a.param := atomic @@ -364,7 +364,7 @@ class TLEdgeOut( def Logical(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, atomic: UInt) = { require (manager.anySupportLogical) - val legal = manager.supportsLogical(toAddress, lgSize) + val legal = manager.supportsLogicalFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.LogicalData a.param := atomic @@ -378,7 +378,7 @@ class TLEdgeOut( def Hint(fromSource: UInt, toAddress: UInt, lgSize: UInt, param: UInt) = { require (manager.anySupportHint) - val legal = manager.supportsHint(toAddress, lgSize) + val legal = manager.supportsHintFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.Hint a.param := param @@ -445,7 +445,7 @@ class TLEdgeIn( // Transfers def Probe(fromAddress: UInt, toSource: UInt, lgSize: UInt, capPermissions: UInt) = { require (client.anySupportProbe) - val legal = client.supportsProbe(fromAddress, lgSize) + val legal = client.supportsProbe(toSource, lgSize) val b = Wire(new TLBundleB(bundle)) b.opcode := TLMessages.Probe b.param := capPermissions diff --git a/src/main/scala/uncore/tilelink2/Fragmenter.scala b/src/main/scala/uncore/tilelink2/Fragmenter.scala index 30775583..6c339255 100644 --- a/src/main/scala/uncore/tilelink2/Fragmenter.scala +++ b/src/main/scala/uncore/tilelink2/Fragmenter.scala @@ -189,7 +189,7 @@ class TLFragmenter(minSize: Int, maxSize: Int, alwaysMin: Boolean = false) exten val maxLgHints = maxHints .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) // If this is infront of a single manager, these become constants - val find = manager.find(edgeIn.address(in.a.bits)) + val find = manager.findFast(edgeIn.address(in.a.bits)) val maxLgArithmetic = Mux1H(find, maxLgArithmetics) val maxLgLogical = Mux1H(find, maxLgLogicals) val maxLgGet = Mux1H(find, maxLgGets) diff --git a/src/main/scala/uncore/tilelink2/HintHandler.scala b/src/main/scala/uncore/tilelink2/HintHandler.scala index 4e1bd12d..376f8e90 100644 --- a/src/main/scala/uncore/tilelink2/HintHandler.scala +++ b/src/main/scala/uncore/tilelink2/HintHandler.scala @@ -45,7 +45,7 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f // Who wants what? val address = edgeIn.address(in.a.bits) - val handleA = if (passthrough) !edgeOut.manager.supportsHint(address, edgeIn.size(in.a.bits)) else Bool(true) + val handleA = if (passthrough) !edgeOut.manager.supportsHintFast(address, edgeIn.size(in.a.bits)) else Bool(true) val hintBitsAtA = handleA && in.a.bits.opcode === TLMessages.Hint val hintWantsD = in.a.valid && hintBitsAtA val outerWantsD = out.d.valid @@ -57,7 +57,7 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f assert (!hintHoldsD || hintWantsD) in.d.valid := Mux(hintWinsD, hintWantsD, outerWantsD) - in.d.bits := Mux(hintWinsD, edgeIn.HintAck(in.a.bits, edgeOut.manager.findId(address)), out.d.bits) + in.d.bits := Mux(hintWinsD, edgeIn.HintAck(in.a.bits, edgeOut.manager.findIdStartFast(address)), out.d.bits) out.d.ready := in.d.ready && !hintHoldsD in.a.ready := Mux(hintBitsAtA, hintWinsD && in.d.ready, out.a.ready) diff --git a/src/main/scala/uncore/tilelink2/Monitor.scala b/src/main/scala/uncore/tilelink2/Monitor.scala index 9ab63c9c..bd07931b 100644 --- a/src/main/scala/uncore/tilelink2/Monitor.scala +++ b/src/main/scala/uncore/tilelink2/Monitor.scala @@ -24,7 +24,7 @@ object TLMonitor val mask = edge.full_mask(bundle) when (bundle.opcode === TLMessages.Acquire) { - assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'A' channel carries Acquire type unsupported by manager" + extra) + assert (edge.manager.supportsAcquireSafe(edge.address(bundle), bundle.size), "'A' channel carries Acquire type unsupported by manager" + extra) assert (source_ok, "'A' channel Acquire carries invalid source ID" + extra) assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'A' channel Acquire smaller than a beat" + extra) assert (is_aligned, "'A' channel Acquire address not aligned to size" + extra) @@ -33,7 +33,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.Get) { - assert (edge.manager.supportsGet(edge.address(bundle), bundle.size), "'A' channel carries Get type unsupported by manager" + extra) + assert (edge.manager.supportsGetSafe(edge.address(bundle), bundle.size), "'A' channel carries Get type unsupported by manager" + extra) assert (source_ok, "'A' channel Get carries invalid source ID" + extra) assert (is_aligned, "'A' channel Get address not aligned to size" + extra) assert (bundle.param === UInt(0), "'A' channel Get carries invalid param" + extra) @@ -41,7 +41,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.PutFullData) { - assert (edge.manager.supportsPutFull(edge.address(bundle), bundle.size), "'A' channel carries PutFull type unsupported by manager" + extra) + assert (edge.manager.supportsPutFullSafe(edge.address(bundle), bundle.size), "'A' channel carries PutFull type unsupported by manager" + extra) assert (source_ok, "'A' channel PutFull carries invalid source ID" + extra) assert (is_aligned, "'A' channel PutFull address not aligned to size" + extra) assert (bundle.param === UInt(0), "'A' channel PutFull carries invalid param" + extra) @@ -49,7 +49,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.PutPartialData) { - assert (edge.manager.supportsPutPartial(edge.address(bundle), bundle.size), "'A' channel carries PutPartial type unsupported by manager" + extra) + assert (edge.manager.supportsPutPartialSafe(edge.address(bundle), bundle.size), "'A' channel carries PutPartial type unsupported by manager" + extra) assert (source_ok, "'A' channel PutPartial carries invalid source ID" + extra) assert (is_aligned, "'A' channel PutPartial address not aligned to size" + extra) assert (bundle.param === UInt(0), "'A' channel PutPartial carries invalid param" + extra) @@ -57,7 +57,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.ArithmeticData) { - assert (edge.manager.supportsArithmetic(edge.address(bundle), bundle.size), "'A' channel carries Arithmetic type unsupported by manager" + extra) + assert (edge.manager.supportsArithmeticSafe(edge.address(bundle), bundle.size), "'A' channel carries Arithmetic type unsupported by manager" + extra) assert (source_ok, "'A' channel Arithmetic carries invalid source ID" + extra) assert (is_aligned, "'A' channel Arithmetic address not aligned to size" + extra) assert (TLAtomics.isArithmetic(bundle.param), "'A' channel Arithmetic carries invalid opcode param" + extra) @@ -65,7 +65,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.LogicalData) { - assert (edge.manager.supportsLogical(edge.address(bundle), bundle.size), "'A' channel carries Logical type unsupported by manager" + extra) + assert (edge.manager.supportsLogicalSafe(edge.address(bundle), bundle.size), "'A' channel carries Logical type unsupported by manager" + extra) assert (source_ok, "'A' channel Logical carries invalid source ID" + extra) assert (is_aligned, "'A' channel Logical address not aligned to size" + extra) assert (TLAtomics.isLogical(bundle.param), "'A' channel Logical carries invalid opcode param" + extra) @@ -73,7 +73,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.Hint) { - assert (edge.manager.supportsHint(edge.address(bundle), bundle.size), "'A' channel carries Hint type unsupported by manager" + extra) + assert (edge.manager.supportsHintSafe(edge.address(bundle), bundle.size), "'A' channel carries Hint type unsupported by manager" + extra) assert (source_ok, "'A' channel Hint carries invalid source ID" + extra) assert (is_aligned, "'A' channel Hint address not aligned to size" + extra) assert (bundle.mask === mask, "'A' channel Hint contains invalid mask" + extra) @@ -84,7 +84,7 @@ object TLMonitor assert (TLMessages.isB(bundle.opcode), "'B' channel has invalid opcode" + extra) // Reuse these subexpressions to save some firrtl lines - val address_ok = edge.manager.contains(bundle.source) + val address_ok = edge.manager.containsSafe(edge.address(bundle)) val is_aligned = edge.isHiAligned(bundle.addr_hi, bundle.size) val mask = edge.full_mask(bundle) @@ -150,7 +150,7 @@ object TLMonitor val source_ok = edge.client.contains(bundle.source) val is_aligned = edge.isHiAligned(bundle.addr_hi, bundle.size) && edge.isLoAligned(bundle.addr_lo, bundle.size) - val address_ok = edge.manager.contains(bundle.source) + val address_ok = edge.manager.containsSafe(edge.address(bundle)) when (bundle.opcode === TLMessages.ProbeAck) { assert (address_ok, "'C' channel ProbeAck carries unmanaged address" + extra) @@ -171,7 +171,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.Release) { - assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'C' channel carries Release type unsupported by manager" + extra) + assert (edge.manager.supportsAcquireSafe(edge.address(bundle), bundle.size), "'C' channel carries Release type unsupported by manager" + extra) assert (source_ok, "'C' channel Release carries invalid source ID" + extra) assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'C' channel Release smaller than a beat" + extra) assert (is_aligned, "'C' channel Release address not aligned to size" + extra) @@ -180,7 +180,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.ReleaseData) { - assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'C' channel carries ReleaseData type unsupported by manager" + extra) + assert (edge.manager.supportsAcquireSafe(edge.address(bundle), bundle.size), "'C' channel carries ReleaseData type unsupported by manager" + extra) assert (source_ok, "'C' channel ReleaseData carries invalid source ID" + extra) assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'C' channel ReleaseData smaller than a beat" + extra) assert (is_aligned, "'C' channel ReleaseData address not aligned to size" + extra) diff --git a/src/main/scala/uncore/tilelink2/Parameters.scala b/src/main/scala/uncore/tilelink2/Parameters.scala index 4c7d2603..ca5fe0e7 100644 --- a/src/main/scala/uncore/tilelink2/Parameters.scala +++ b/src/main/scala/uncore/tilelink2/Parameters.scala @@ -209,41 +209,59 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes val anySupportPutPartial = managers.map(!_.supportsPutPartial.none).reduce(_ || _) val anySupportHint = managers.map(!_.supportsHint.none) .reduce(_ || _) + // Which bits suffice to distinguish between all managers + lazy val routingMask = AddressDecoder(managers.map(_.address)) + // These return Option[TLManagerParameters] for your convenience def find(address: BigInt) = managers.find(_.address.exists(_.contains(address))) def findById(id: Int) = managers.find(_.sinkId.contains(id)) // Synthesizable lookup methods - def find(address: UInt) = Vec(managers.map(_.address.map(_.contains(address)).reduce(_ || _))) def findById(id: UInt) = Vec(managers.map(_.sinkId.contains(id))) - def findId(address: UInt) = Mux1H(find(address), managers.map(m => UInt(m.sinkId.start))) + def findIdStartSafe(address: UInt) = Mux1H(findSafe(address), managers.map(m => UInt(m.sinkId.start))) + def findIdStartFast(address: UInt) = Mux1H(findFast(address), managers.map(m => UInt(m.sinkId.start))) + def findIdEndSafe(address: UInt) = Mux1H(findSafe(address), managers.map(m => UInt(m.sinkId.end))) + def findIdEndFast(address: UInt) = Mux1H(findFast(address), managers.map(m => UInt(m.sinkId.end))) + + // The safe version will check the entire address + def findSafe(address: UInt) = Vec(managers.map(_.address.map(_.contains(address)).reduce(_ || _))) + // The fast version assumes the address is valid + def findFast(address: UInt) = Vec(managers.map(_.address.map(_.widen(~routingMask)).distinct.map(_.contains(address)).reduce(_ || _))) // Note: returns the actual fifoId + 1 or 0 if None - def findFifoId(address: UInt) = Mux1H(find(address), managers.map(m => UInt(m.fifoId.map(_+1).getOrElse(0)))) - def hasFifoId(address: UInt) = Mux1H(find(address), managers.map(m => Bool(m.fifoId.isDefined))) + def findFifoIdSafe(address: UInt) = Mux1H(findSafe(address), managers.map(m => UInt(m.fifoId.map(_+1).getOrElse(0)))) + def findFifoIdFast(address: UInt) = Mux1H(findFast(address), managers.map(m => UInt(m.fifoId.map(_+1).getOrElse(0)))) + def hasFifoIdSafe(address: UInt) = Mux1H(findSafe(address), managers.map(m => Bool(m.fifoId.isDefined))) + def hasFifoIdFast(address: UInt) = Mux1H(findFast(address), managers.map(m => Bool(m.fifoId.isDefined))) - lazy val addressMask = AddressDecoder(managers.map(_.address)) - // !!! need a cheaper version of find, where we assume a valid address match exists - // Does this Port manage this ID/address? - def contains(address: UInt) = find(address).reduce(_ || _) + def containsSafe(address: UInt) = findSafe(address).reduce(_ || _) + // containsFast would be useless; it could always be true def containsById(id: UInt) = findById(id).reduce(_ || _) - private def safety_helper(member: TLManagerParameters => TransferSizes)(address: UInt, lgSize: UInt) = { + private def safety_helper(member: TLManagerParameters => TransferSizes, select: UInt => Vec[Bool])(address: UInt, lgSize: UInt) = { val allSame = managers.map(member(_) == member(managers(0))).reduce(_ && _) if (allSame) member(managers(0)).containsLg(lgSize) else { - Mux1H(find(address), managers.map(member(_).containsLg(lgSize))) + Mux1H(select(address), managers.map(member(_).containsLg(lgSize))) } } // Check for support of a given operation at a specific address - val supportsAcquire = safety_helper(_.supportsAcquire) _ - val supportsArithmetic = safety_helper(_.supportsArithmetic) _ - val supportsLogical = safety_helper(_.supportsLogical) _ - val supportsGet = safety_helper(_.supportsGet) _ - val supportsPutFull = safety_helper(_.supportsPutFull) _ - val supportsPutPartial = safety_helper(_.supportsPutPartial) _ - val supportsHint = safety_helper(_.supportsHint) _ + val supportsAcquireSafe = safety_helper(_.supportsAcquire, findSafe) _ + val supportsArithmeticSafe = safety_helper(_.supportsArithmetic, findSafe) _ + val supportsLogicalSafe = safety_helper(_.supportsLogical, findSafe) _ + val supportsGetSafe = safety_helper(_.supportsGet, findSafe) _ + val supportsPutFullSafe = safety_helper(_.supportsPutFull, findSafe) _ + val supportsPutPartialSafe = safety_helper(_.supportsPutPartial, findSafe) _ + val supportsHintSafe = safety_helper(_.supportsHint, findSafe) _ + + val supportsAcquireFast = safety_helper(_.supportsAcquire, findFast) _ + val supportsArithmeticFast = safety_helper(_.supportsArithmetic, findFast) _ + val supportsLogicalFast = safety_helper(_.supportsLogical, findFast) _ + val supportsGetFast = safety_helper(_.supportsGet, findFast) _ + val supportsPutFullFast = safety_helper(_.supportsPutFull, findFast) _ + val supportsPutPartialFast = safety_helper(_.supportsPutPartial, findFast) _ + val supportsHintFast = safety_helper(_.supportsHint, findFast) _ } case class TLClientParameters( diff --git a/src/main/scala/uncore/tilelink2/RAMModel.scala b/src/main/scala/uncore/tilelink2/RAMModel.scala index d6609174..6d75475a 100644 --- a/src/main/scala/uncore/tilelink2/RAMModel.scala +++ b/src/main/scala/uncore/tilelink2/RAMModel.scala @@ -119,7 +119,7 @@ class TLRAMModel extends LazyModule val a_addr_hi = a.addr_hi | (a_beats1 & ~a_counter1) val a_base = edge.address(a) val a_mask = edge.mask(a_base, a_size) - val a_fifo = edge.manager.hasFifoId(a_base) + val a_fifo = edge.manager.hasFifoIdFast(a_base) // Grab the concurrency state we need val a_inc_bytes = inc_bytes.map(_.read(a_addr_hi)) @@ -203,7 +203,7 @@ class TLRAMModel extends LazyModule val d_base = d_flight.base val d_addr_hi = d_base >> shift | (d_beats1 & ~d_counter1) val d_mask = edge.mask(d_base, d_size) - val d_fifo = edge.manager.hasFifoId(d_flight.base) + val d_fifo = edge.manager.hasFifoIdFast(d_flight.base) // Grab the concurrency state we need val d_inc_bytes = inc_bytes.map(_.read(d_addr_hi)) @@ -222,7 +222,8 @@ class TLRAMModel extends LazyModule // Check the response is correct assert (d_size === d_flight.size) - assert (edge.manager.findId(d_flight.base) === d.sink) + assert (edge.manager.findIdStartFast(d_flight.base) <= d.sink) + assert (edge.manager.findIdEndFast (d_flight.base) > d.sink) // addr_lo is allowed to differ when (d_flight.opcode === TLMessages.Hint) { From 9817a00ed9b7fbd2ebeda92e4496b53e82d9d113 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Sat, 17 Sep 2016 17:07:21 -0700 Subject: [PATCH 23/28] tilelink2: Fuzzer should check address validity before injection --- src/main/scala/uncore/tilelink2/Fuzzer.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/scala/uncore/tilelink2/Fuzzer.scala b/src/main/scala/uncore/tilelink2/Fuzzer.scala index 30828c61..77efc7d4 100644 --- a/src/main/scala/uncore/tilelink2/Fuzzer.scala +++ b/src/main/scala/uncore/tilelink2/Fuzzer.scala @@ -163,10 +163,12 @@ class TLFuzzer( edge.Hint(src, addr, size, UInt(0)) } else { (glegal, gbits) } + val legal_dest = edge.manager.containsSafe(addr) + // Pick a specific message to try to send val a_type_sel = noiseMaker(3, inc) - val legal = MuxLookup(a_type_sel, glegal, Seq( + val legal = legal_dest && MuxLookup(a_type_sel, glegal, Seq( UInt("b000") -> glegal, UInt("b001") -> pflegal, UInt("b010") -> pplegal, From a49814c6677a83a8efac1d5769f109c0d6bdbc99 Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Sun, 18 Sep 2016 18:45:51 -0700 Subject: [PATCH 24/28] Allow WideCounter to not be reset --- src/main/scala/rocket/util.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/rocket/util.scala b/src/main/scala/rocket/util.scala index 182c7ba9..e40da39a 100644 --- a/src/main/scala/rocket/util.scala +++ b/src/main/scala/rocket/util.scala @@ -127,16 +127,16 @@ object Split } // a counter that clock gates most of its MSBs using the LSB carry-out -case class WideCounter(width: Int, inc: UInt = UInt(1)) +case class WideCounter(width: Int, inc: UInt = UInt(1), reset: Boolean = true) { private val isWide = width > 2*inc.getWidth private val smallWidth = if (isWide) inc.getWidth max log2Up(width) else width - private val small = Reg(init=UInt(0, smallWidth)) + private val small = if (reset) Reg(init=UInt(0, smallWidth)) else Reg(UInt(width = smallWidth)) private val nextSmall = small +& inc small := nextSmall private val large = if (isWide) { - val r = Reg(init=UInt(0, width - smallWidth)) + val r = if (reset) Reg(init=UInt(0, width - smallWidth)) else Reg(UInt(width = width - smallWidth)) when (nextSmall(smallWidth)) { r := r + UInt(1) } r } else null From 1b26d78114af78dcad0d28d789319c1dfac00e7f Mon Sep 17 00:00:00 2001 From: Yunsup Lee Date: Mon, 19 Sep 2016 13:34:58 -0700 Subject: [PATCH 25/28] correctly print out the addrmap overlapping error message --- src/main/scala/junctions/addrmap.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/junctions/addrmap.scala b/src/main/scala/junctions/addrmap.scala index 19613ae7..275e4ff3 100644 --- a/src/main/scala/junctions/addrmap.scala +++ b/src/main/scala/junctions/addrmap.scala @@ -125,7 +125,7 @@ class AddrMap( val brEnd = br.start + br.size val abOverlaps = ar.start < brEnd && br.start < arEnd require(!abOverlaps, - "region $an@0x${ar.start.toString(16)} overlaps region $bn@0x${br.start.toString(16)}") + s"region $an@0x${ar.start.toString(16)} overlaps region $bn@0x${br.start.toString(16)}") } def toRange: MemRange = MemRange(start, size, attr) From e6c1bcfedd844f717827dcd5d09cf954d57894c5 Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Mon, 19 Sep 2016 15:54:17 -0700 Subject: [PATCH 26/28] Expose carry-out bits from WideCounter --- src/main/scala/rocket/util.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/scala/rocket/util.scala b/src/main/scala/rocket/util.scala index e40da39a..a311364a 100644 --- a/src/main/scala/rocket/util.scala +++ b/src/main/scala/rocket/util.scala @@ -137,11 +137,18 @@ case class WideCounter(width: Int, inc: UInt = UInt(1), reset: Boolean = true) private val large = if (isWide) { val r = if (reset) Reg(init=UInt(0, width - smallWidth)) else Reg(UInt(width = width - smallWidth)) - when (nextSmall(smallWidth)) { r := r + UInt(1) } + when (nextSmall(smallWidth)) { r := r +& UInt(1) } r } else null val value = if (isWide) Cat(large, small) else small + lazy val carryOut = { + val lo = (small ^ nextSmall) >> 1 + if (!isWide) lo else { + val hi = Mux(nextSmall(smallWidth), large ^ (large +& UInt(1)), UInt(0)) >> 1 + Cat(hi, lo) + } + } def := (x: UInt) = { small := x From d0572d6aabedaab924d5282df8e98a44eb85be38 Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Mon, 19 Sep 2016 16:45:57 -0700 Subject: [PATCH 27/28] Allow reset vector to be set dynamically A chip's power-up sequence, or awake-from-sleep sequence, may wish to set the reset PC based upon dynamic properties, e.g., the settings of external pins. Support this by passing the reset vector to the Coreplex. ExampleTop simply hard-wires the reset vector, as was the case before. Additionally, allow MTVEC to *not* be reset. In most cases, including riscv-tests, pk, and bbl, overriding MTVEC is one of the first things that the boot sequence does. So the reset value is superfluous. --- src/main/scala/coreplex/Configs.scala | 3 +-- src/main/scala/coreplex/Coreplex.scala | 2 ++ src/main/scala/rocket/csr.scala | 6 +++++- src/main/scala/rocket/frontend.scala | 3 ++- src/main/scala/rocket/rocket.scala | 3 +-- src/main/scala/rocket/tile.scala | 2 ++ src/main/scala/rocketchip/Periphery.scala | 11 ++++++++++- src/main/scala/rocketchip/Top.scala | 1 + src/main/scala/rocketchip/Utils.scala | 13 +++---------- 9 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/main/scala/coreplex/Configs.scala b/src/main/scala/coreplex/Configs.scala index 2e124a9c..c75ae487 100644 --- a/src/main/scala/coreplex/Configs.scala +++ b/src/main/scala/coreplex/Configs.scala @@ -131,8 +131,7 @@ class BaseCoreplexConfig extends Config ( case UseCompressed => true case DMKey => new DefaultDebugModuleConfig(site(NTiles), site(XLen)) case NCustomMRWCSRs => 0 - case ResetVector => BigInt(0x1000) - case MtvecInit => BigInt(0x1010) + case MtvecInit => None case MtvecWritable => true //Uncore Paramters case LNEndpoints => site(TLKey(site(TLId))).nManagers + site(TLKey(site(TLId))).nClients diff --git a/src/main/scala/coreplex/Coreplex.scala b/src/main/scala/coreplex/Coreplex.scala index fe1d5bf0..341f4977 100644 --- a/src/main/scala/coreplex/Coreplex.scala +++ b/src/main/scala/coreplex/Coreplex.scala @@ -58,6 +58,7 @@ abstract class Coreplex(implicit val p: Parameters, implicit val c: CoreplexConf val debug = new DebugBusIO()(p).flip val clint = Vec(c.nTiles, new CoreplexLocalInterrupts).asInput val success: Option[Bool] = hasSuccessFlag.option(Bool(OUTPUT)) + val resetVector = UInt(INPUT, p(XLen)) } def hasSuccessFlag: Boolean = false @@ -153,6 +154,7 @@ class DefaultCoreplex(tp: Parameters, tc: CoreplexConfig) extends Coreplex()(tp, tile.io.interrupts.seip.foreach(_ := plic.io.harts(plic.cfg.context(i, 'S'))) tile.io.interrupts.debug := debugModule.io.debugInterrupts(i) tile.io.hartid := i + tile.io.resetVector := io.resetVector } val tileSlavePorts = (0 until tc.nTiles) map (i => s"int:dmem$i") filter (ioAddrMap contains _) diff --git a/src/main/scala/rocket/csr.scala b/src/main/scala/rocket/csr.scala index 8b3ecff0..e542d233 100644 --- a/src/main/scala/rocket/csr.scala +++ b/src/main/scala/rocket/csr.scala @@ -227,7 +227,11 @@ class CSRFile(implicit p: Parameters) extends CoreModule()(p) val reg_mcause = Reg(Bits(width = xLen)) val reg_mbadaddr = Reg(UInt(width = vaddrBitsExtended)) val reg_mscratch = Reg(Bits(width = xLen)) - val reg_mtvec = Reg(init=UInt(p(MtvecInit), paddrBits min xLen)) + val mtvecWidth = paddrBits min xLen + val reg_mtvec = p(MtvecInit) match { + case Some(addr) => Reg(init=UInt(addr, mtvecWidth)) + case None => Reg(UInt(width = mtvecWidth)) + } val reg_mucounteren = Reg(UInt(width = 32)) val reg_mscounteren = Reg(UInt(width = 32)) val delegable_counters = (BigInt(1) << (nPerfCounters + CSR.firstHPM)) - 1 diff --git a/src/main/scala/rocket/frontend.scala b/src/main/scala/rocket/frontend.scala index 25c47b80..7fdc37a5 100644 --- a/src/main/scala/rocket/frontend.scala +++ b/src/main/scala/rocket/frontend.scala @@ -35,6 +35,7 @@ class Frontend(implicit p: Parameters) extends CoreModule()(p) with HasL1CachePa val cpu = new FrontendIO().flip val ptw = new TLBPTWIO() val mem = new ClientUncachedTileLinkIO + val resetVector = UInt(INPUT, vaddrBitsExtended) } val icache = Module(new ICache(latency = 2)) @@ -45,7 +46,7 @@ class Frontend(implicit p: Parameters) extends CoreModule()(p) with HasL1CachePa val s1_speculative = Reg(Bool()) val s1_same_block = Reg(Bool()) val s2_valid = Reg(init=Bool(true)) - val s2_pc = Reg(init=UInt(p(ResetVector))) + val s2_pc = Reg(init=io.resetVector) val s2_btb_resp_valid = Reg(init=Bool(false)) val s2_btb_resp_bits = Reg(new BTBResp) val s2_xcpt_if = Reg(init=Bool(false)) diff --git a/src/main/scala/rocket/rocket.scala b/src/main/scala/rocket/rocket.scala index 61b013b2..4939412d 100644 --- a/src/main/scala/rocket/rocket.scala +++ b/src/main/scala/rocket/rocket.scala @@ -27,8 +27,7 @@ case object FastJAL extends Field[Boolean] case object CoreInstBits extends Field[Int] case object NCustomMRWCSRs extends Field[Int] case object MtvecWritable extends Field[Boolean] -case object MtvecInit extends Field[BigInt] -case object ResetVector extends Field[BigInt] +case object MtvecInit extends Field[Option[BigInt]] case object NBreakpoints extends Field[Int] case object NPerfCounters extends Field[Int] case object NPerfEvents extends Field[Int] diff --git a/src/main/scala/rocket/tile.scala b/src/main/scala/rocket/tile.scala index 1a868fa8..9280bae2 100644 --- a/src/main/scala/rocket/tile.scala +++ b/src/main/scala/rocket/tile.scala @@ -34,6 +34,7 @@ abstract class Tile(clockSignal: Clock = null, resetSignal: Bool = null) val hartid = UInt(INPUT, p(XLen)) val interrupts = new TileInterrupts().asInput val slave = (p(DataScratchpadSize) > 0).option(new ClientUncachedTileLinkIO().flip) + val resetVector = UInt(INPUT, p(XLen)) } val io = new TileIO @@ -58,6 +59,7 @@ class RocketTile(clockSignal: Clock = null, resetSignal: Bool = null) core.io.interrupts := io.interrupts core.io.hartid := io.hartid icache.io.cpu <> core.io.imem + icache.io.resetVector := io.resetVector val fpuOpt = p(FPUKey).map(cfg => Module(new FPU(cfg))) fpuOpt.foreach(fpu => core.io.fpu <> fpu.io) diff --git a/src/main/scala/rocketchip/Periphery.scala b/src/main/scala/rocketchip/Periphery.scala index c69ef131..f7d6d5bb 100644 --- a/src/main/scala/rocketchip/Periphery.scala +++ b/src/main/scala/rocketchip/Periphery.scala @@ -311,7 +311,9 @@ trait PeripheryBootROM extends LazyModule { implicit val p: Parameters val peripheryBus: TLXbar - val rom = LazyModule(new TLROM(0x1000, 0x1000, GenerateBootROM(p)) { override def name = "bootrom" }) + val address = 0x1000 + val size = 0x1000 + val rom = LazyModule(new TLROM(address, size, GenerateBootROM(p, address)) { override def name = "bootrom" }) rom.node := TLFragmenter(peripheryBus.node, 4, 256) } @@ -365,3 +367,10 @@ trait PeripheryTestBusMasterModule { implicit val p: Parameters val outer: PeripheryTestBusMaster } + +///// + +trait HardwiredResetVector { + val coreplex: Coreplex + coreplex.io.resetVector := UInt(0x1000) // boot ROM +} diff --git a/src/main/scala/rocketchip/Top.scala b/src/main/scala/rocketchip/Top.scala index ef4a98b4..dc36acb3 100644 --- a/src/main/scala/rocketchip/Top.scala +++ b/src/main/scala/rocketchip/Top.scala @@ -103,6 +103,7 @@ class ExampleTopBundle(p: Parameters, c: Coreplex) extends BaseTopBundle(p, c) class ExampleTopModule[+L <: ExampleTop, +B <: ExampleTopBundle](p: Parameters, l: L, b: Coreplex => B) extends BaseTopModule(p, l, b) with PeripheryBootROMModule with PeripheryDebugModule with PeripheryExtInterruptsModule with PeripheryCoreplexLocalInterrupterModule with PeripheryMasterMemModule with PeripheryMasterMMIOModule with PeripherySlaveModule + with HardwiredResetVector /** Example Top with TestRAM */ class ExampleTopWithTestRAM(q: Parameters) extends ExampleTop(q) diff --git a/src/main/scala/rocketchip/Utils.scala b/src/main/scala/rocketchip/Utils.scala index 1436050a..387e23bb 100644 --- a/src/main/scala/rocketchip/Utils.scala +++ b/src/main/scala/rocketchip/Utils.scala @@ -168,21 +168,14 @@ object GenerateConfigString { } object GenerateBootROM { - def apply(p: Parameters) = { + def apply(p: Parameters, address: BigInt) = { val romdata = Files.readAllBytes(Paths.get(p(BootROMFile))) val rom = ByteBuffer.wrap(romdata) rom.order(ByteOrder.LITTLE_ENDIAN) - // for now, have the reset vector jump straight to memory - val memBase = ( - if (p(GlobalAddrMap) contains "mem") p(GlobalAddrMap)("mem") - else p(GlobalAddrMap)("io:int:dmem0") - ).start - val resetToMemDist = memBase - p(ResetVector) - require(resetToMemDist == (resetToMemDist.toInt >> 12 << 12)) - val configStringAddr = p(ResetVector).toInt + rom.capacity - + require(address == address.toInt) + val configStringAddr = address.toInt + rom.capacity require(rom.getInt(12) == 0, "Config string address position should not be occupied by code") rom.putInt(12, configStringAddr) From 3b38736a8e9a45be7ccd5a43c24cd958c16bc575 Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Mon, 19 Sep 2016 16:50:04 -0700 Subject: [PATCH 28/28] Make BaseTopModule and BaseTopModule abstract They aren't meant to be directly instantiated. --- src/main/scala/rocketchip/Top.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/rocketchip/Top.scala b/src/main/scala/rocketchip/Top.scala index dc36acb3..8dc579aa 100644 --- a/src/main/scala/rocketchip/Top.scala +++ b/src/main/scala/rocketchip/Top.scala @@ -53,11 +53,11 @@ abstract class BaseTop(q: Parameters) extends LazyModule { peripheryBus.node := TLBuffer(TLWidthWidget(TLHintHandler(legacy.node), legacy.tlDataBytes)) } -class BaseTopBundle(val p: Parameters, val c: Coreplex) extends ParameterizedBundle()(p) { +abstract class BaseTopBundle(val p: Parameters, val c: Coreplex) extends ParameterizedBundle()(p) { val success = c.hasSuccessFlag.option(Bool(OUTPUT)) } -class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle](val p: Parameters, l: L, b: Coreplex => B) extends LazyModuleImp(l) { +abstract class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle](val p: Parameters, l: L, b: Coreplex => B) extends LazyModuleImp(l) { val outer: L = l val coreplex = p(BuildCoreplex)(p, outer.c)