diff --git a/src/main/scala/rocketchip/BaseTop.scala b/src/main/scala/rocketchip/BaseTop.scala index 32b925d2..69c501ee 100644 --- a/src/main/scala/rocketchip/BaseTop.scala +++ b/src/main/scala/rocketchip/BaseTop.scala @@ -49,7 +49,7 @@ abstract class BaseTop(q: Parameters) extends LazyModule { val legacy = LazyModule(new TLLegacy()(p.alterPartial({ case TLId => "L2toMMIO" }))) - peripheryBus.node := TLWidthWidget(TLBuffer(TLAtomicAutomata()(TLHintHandler(legacy.node))), legacy.tlDataBytes) + peripheryBus.node := TLWidthWidget(legacy.tlDataBytes)(TLBuffer()(TLAtomicAutomata()(TLHintHandler()(legacy.node)))) } abstract class BaseTopBundle(val p: Parameters) extends Bundle { diff --git a/src/main/scala/rocketchip/Periphery.scala b/src/main/scala/rocketchip/Periphery.scala index a5fe8d6b..53bdfd09 100644 --- a/src/main/scala/rocketchip/Periphery.scala +++ b/src/main/scala/rocketchip/Periphery.scala @@ -288,7 +288,7 @@ trait PeripheryCoreplexLocalInterrupter extends LazyModule with HasPeripheryPara val clintConfig = CoreplexLocalInterrupterConfig(beatBytes) 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) + clint.node := TLFragmenter(beatBytes, 256)(TLWidthWidget(4)(peripheryBus.node)) } trait PeripheryCoreplexLocalInterrupterBundle { @@ -314,7 +314,7 @@ trait PeripheryBootROM extends LazyModule { 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) + rom.node := TLFragmenter(4, 256)(peripheryBus.node) } trait PeripheryBootROMBundle { @@ -337,7 +337,7 @@ trait PeripheryTestRAM extends LazyModule { val ramSize = 0x1000 val sram = LazyModule(new TLRAM(AddressSet(ramBase, ramSize-1)) { override def name = "testram" }) - sram.node := TLFragmenter(peripheryBus.node, 4, 256) + sram.node := TLFragmenter(4, 256)(peripheryBus.node) } trait PeripheryTestRAMBundle { diff --git a/src/main/scala/uncore/devices/Prci.scala b/src/main/scala/uncore/devices/Prci.scala index ce32e8c2..50315a6d 100644 --- a/src/main/scala/uncore/devices/Prci.scala +++ b/src/main/scala/uncore/devices/Prci.scala @@ -74,14 +74,11 @@ trait CoreplexLocalInterrupterModule extends Module with HasRegMap with MixCorep * bff8 mtime lo * bffc mtime hi */ - val ipi_base = 0 - val timecmp_base = c.timecmpOffset(0) / c.beatBytes - val time_base = c.timeOffset / c.beatBytes - regmap(( - RegField.split(makeRegFields(ipi), ipi_base, c.beatBytes) ++ - RegField.split(makeRegFields(timecmp.flatten), timecmp_base, c.beatBytes) ++ - RegField.split(makeRegFields(time), time_base, c.beatBytes)):_*) + regmap( + 0 -> makeRegFields(ipi), + c.timecmpOffset(0) -> makeRegFields(timecmp.flatten), + c.timeOffset -> makeRegFields(time)) def makeRegFields(s: Seq[UInt]) = s.map(r => RegField(regWidth, r)) } diff --git a/src/main/scala/uncore/tilelink2/Buffer.scala b/src/main/scala/uncore/tilelink2/Buffer.scala index e4ba752b..75f8794b 100644 --- a/src/main/scala/uncore/tilelink2/Buffer.scala +++ b/src/main/scala/uncore/tilelink2/Buffer.scala @@ -48,12 +48,12 @@ class TLBuffer(a: Int = 2, b: Int = 2, c: Int = 2, d: Int = 2, e: Int = 2, pipe: object TLBuffer { // applied to the TL source node; y.node := TLBuffer(x.node) - def apply(x: TLBaseNode) (implicit sourceInfo: SourceInfo): TLBaseNode = apply(x, 2) - def apply(x: TLBaseNode, entries: Int) (implicit sourceInfo: SourceInfo): TLBaseNode = apply(x, entries, true) - def apply(x: TLBaseNode, entries: Int, pipe: Boolean) (implicit sourceInfo: SourceInfo): TLBaseNode = apply(x, entries, entries, pipe) - def apply(x: TLBaseNode, ace: Int, bd: Int) (implicit sourceInfo: SourceInfo): TLBaseNode = apply(x, ace, bd, true) - def apply(x: TLBaseNode, ace: Int, bd: Int, pipe: Boolean)(implicit sourceInfo: SourceInfo): TLBaseNode = apply(x, ace, bd, ace, bd, ace, pipe) - def apply(x: TLBaseNode, a: Int, b: Int, c: Int, d: Int, e: Int, pipe: Boolean = true)(implicit sourceInfo: SourceInfo): TLBaseNode = { + def apply() (x: TLBaseNode)(implicit sourceInfo: SourceInfo): TLBaseNode = apply(2)(x) + def apply(entries: Int) (x: TLBaseNode)(implicit sourceInfo: SourceInfo): TLBaseNode = apply(entries, true)(x) + def apply(entries: Int, pipe: Boolean) (x: TLBaseNode)(implicit sourceInfo: SourceInfo): TLBaseNode = apply(entries, entries, pipe)(x) + def apply(ace: Int, bd: Int) (x: TLBaseNode)(implicit sourceInfo: SourceInfo): TLBaseNode = apply(ace, bd, true)(x) + def apply(ace: Int, bd: Int, pipe: Boolean)(x: TLBaseNode)(implicit sourceInfo: SourceInfo): TLBaseNode = apply(ace, bd, ace, bd, ace, pipe)(x) + def apply(a: Int, b: Int, c: Int, d: Int, e: Int, pipe: Boolean = true)(x: TLBaseNode)(implicit sourceInfo: SourceInfo): TLBaseNode = { val buffer = LazyModule(new TLBuffer(a, b, c, d, e, pipe)) buffer.node := x buffer.node diff --git a/src/main/scala/uncore/tilelink2/Example.scala b/src/main/scala/uncore/tilelink2/Example.scala index fb62bbed..84fa377e 100644 --- a/src/main/scala/uncore/tilelink2/Example.scala +++ b/src/main/scala/uncore/tilelink2/Example.scala @@ -27,7 +27,7 @@ trait ExampleModule extends HasRegMap regmap( 0 -> Seq( RegField(params.num, state)), - 1 -> Seq( + 4 -> Seq( RegField.w1ToClear(4, pending, state))) } diff --git a/src/main/scala/uncore/tilelink2/Fragmenter.scala b/src/main/scala/uncore/tilelink2/Fragmenter.scala index 05e663eb..511863d3 100644 --- a/src/main/scala/uncore/tilelink2/Fragmenter.scala +++ b/src/main/scala/uncore/tilelink2/Fragmenter.scala @@ -244,7 +244,7 @@ class TLFragmenter(minSize: Int, maxSize: Int, alwaysMin: Boolean = false) exten object TLFragmenter { // applied to the TL source node; y.node := TLFragmenter(x.node, 256, 4) - def apply(x: TLBaseNode, minSize: Int, maxSize: Int, alwaysMin: Boolean = false)(implicit sourceInfo: SourceInfo): TLBaseNode = { + def apply(minSize: Int, maxSize: Int, alwaysMin: Boolean = false)(x: TLBaseNode)(implicit sourceInfo: SourceInfo): TLBaseNode = { val fragmenter = LazyModule(new TLFragmenter(minSize, maxSize, alwaysMin)) fragmenter.node := x fragmenter.node diff --git a/src/main/scala/uncore/tilelink2/Fuzzer.scala b/src/main/scala/uncore/tilelink2/Fuzzer.scala index aa7e0d2a..4c3d58de 100644 --- a/src/main/scala/uncore/tilelink2/Fuzzer.scala +++ b/src/main/scala/uncore/tilelink2/Fuzzer.scala @@ -222,11 +222,11 @@ class TLFuzzRAM extends LazyModule model.node := fuzz.node xbar2.node := TLAtomicAutomata()(model.node) - ram2.node := TLFragmenter(xbar2.node, 16, 256) - xbar.node := TLWidthWidget(TLHintHandler(xbar2.node), 16) - cross.node := TLFragmenter(TLBuffer(xbar.node), 4, 256) + ram2.node := TLFragmenter(16, 256)(xbar2.node) + xbar.node := TLWidthWidget(16)(TLHintHandler()(xbar2.node)) + cross.node := TLFragmenter(4, 256)(TLBuffer()(xbar.node)) val monitor = (ram.node := cross.node) - gpio.node := TLFragmenter(TLBuffer(xbar.node), 4, 32) + gpio.node := TLFragmenter(4, 32)(TLBuffer()(xbar.node)) lazy val module = new LazyModuleImp(this) with HasUnitTestIO { io.finished := fuzz.module.io.finished diff --git a/src/main/scala/uncore/tilelink2/HintHandler.scala b/src/main/scala/uncore/tilelink2/HintHandler.scala index 6ebef935..5480dd67 100644 --- a/src/main/scala/uncore/tilelink2/HintHandler.scala +++ b/src/main/scala/uncore/tilelink2/HintHandler.scala @@ -135,7 +135,7 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f object TLHintHandler { // applied to the TL source node; y.node := TLHintHandler(x.node) - def apply(x: TLBaseNode, supportManagers: Boolean = true, supportClients: Boolean = false, passthrough: Boolean = true)(implicit sourceInfo: SourceInfo): TLBaseNode = { + def apply(supportManagers: Boolean = true, supportClients: Boolean = false, passthrough: Boolean = true)(x: TLBaseNode)(implicit sourceInfo: SourceInfo): TLBaseNode = { val hints = LazyModule(new TLHintHandler(supportManagers, supportClients, passthrough)) hints.node := x hints.node diff --git a/src/main/scala/uncore/tilelink2/RegField.scala b/src/main/scala/uncore/tilelink2/RegField.scala index 2b372208..e33dc3a8 100644 --- a/src/main/scala/uncore/tilelink2/RegField.scala +++ b/src/main/scala/uncore/tilelink2/RegField.scala @@ -77,7 +77,7 @@ object RegWriteFn implicit def apply(x: Unit): RegWriteFn = RegWriteFn((valid, data) => { Bool(true) }) } -case class RegField(width: Int, read: RegReadFn, write: RegWriteFn) +case class RegField(width: Int, read: RegReadFn, write: RegWriteFn, name: String, description: String) { require (width > 0) def pipelined = !read.combinational || !write.combinational @@ -85,11 +85,15 @@ case class RegField(width: Int, read: RegReadFn, write: RegWriteFn) object RegField { + // Byte address => sequence of bitfields, lowest index => lowest address type Map = (Int, Seq[RegField]) - def apply(n: Int) : RegField = apply(n, (), ()) - def apply(n: Int, rw: UInt) : RegField = apply(n, rw, rw) - def r(n: Int, r: RegReadFn) : RegField = apply(n, r, ()) - def w(n: Int, w: RegWriteFn) : RegField = apply(n, (), w) + + def apply(n: Int) : RegField = apply(n, (), (), "", "") + def apply(n: Int, r: RegReadFn, w: RegWriteFn) : RegField = apply(n, r, w, "", "") + def apply(n: Int, rw: UInt) : RegField = apply(n, rw, rw, "", "") + def apply(n: Int, rw: UInt, name: String, description: String) : RegField = apply(n, rw, rw, name, description) + def r(n: Int, r: RegReadFn, name: String = "", description: String = "") : RegField = apply(n, r, (), name, description) + def w(n: Int, w: RegWriteFn, name: String = "", description: String = "") : RegField = apply(n, (), w, name, description) // This RegField allows 'set' to set bits in 'reg'. // and to clear bits when the bus writes bits of value 1. @@ -105,28 +109,6 @@ object RegField bb.d := data Bool(true) })) - - // Split a large register into a sequence of byte fields - // The bytes can be individually written, as they are one byte per field - def bytes(x: UInt): Seq[RegField] = { - require (x.getWidth % 8 == 0) - val bytes = Seq.tabulate(x.getWidth/8) { i => x(8*(i+1)-1, 8*i) } - val wires = bytes.map { b => Wire(init = b) } - x := Cat(wires.reverse) - Seq.tabulate(x.getWidth/8) { i => - RegField(8, bytes(i), RegWriteFn { (valid, data) => - when (valid) { wires(i) := data } - Bool(true) - }) - } - } - - // Divide a long sequence of RegFields into a maximum sized registers - // Your input RegFields may not cross a beatBytes boundary! - def split(fields: Seq[RegField], base: Int, beatBytes: Int = 4): Seq[RegField.Map] = { - val offsets = fields.map(_.width).scanLeft(0)(_ + _).init - (offsets zip fields).groupBy(_._1 / (beatBytes*8)).toList.map(r => (r._1 + base, r._2.map(_._2))) - } } trait HasRegMap diff --git a/src/main/scala/uncore/tilelink2/RegMapper.scala b/src/main/scala/uncore/tilelink2/RegMapper.scala index 5a661af8..714f416a 100644 --- a/src/main/scala/uncore/tilelink2/RegMapper.scala +++ b/src/main/scala/uncore/tilelink2/RegMapper.scala @@ -29,26 +29,35 @@ object RegMapper { // Create a generic register-based device def apply(bytes: Int, concurrency: Int, undefZero: Boolean, in: DecoupledIO[RegMapperInput], mapping: RegField.Map*) = { - val regmap = mapping.toList.filter(!_._2.isEmpty) - require (!regmap.isEmpty) - - // Ensure no register appears twice - regmap.combinations(2).foreach { case Seq((reg1, _), (reg2, _)) => - require (reg1 != reg2) - } + val bytemap = mapping.toList // Don't be an asshole... - regmap.foreach { reg => require (reg._1 >= 0) } + bytemap.foreach { byte => require (byte._1 >= 0) } + + // Transform all fields into bit offsets Seq[(bit, field)] + val bitmap = bytemap.map { case (byte, fields) => + val bits = fields.scanLeft(byte * 8)(_ + _.width).init + bits zip fields + }.flatten.sortBy(_._1) + + // Detect overlaps + (bitmap.init zip bitmap.tail) foreach { case ((lbit, lfield), (rbit, rfield)) => + require (lbit + lfield.width <= rbit, s"Register map overlaps at bit ${rbit}.") + } + + // Group those fields into bus words Map[word, List[(bit, field)]] + val wordmap = bitmap.groupBy(_._1 / (8*bytes)) + // Make sure registers fit val inParams = in.bits.params val inBits = inParams.indexBits - assert (regmap.map(_._1).max < (1 << inBits)) + assert (wordmap.keySet.max < (1 << inBits), "Register map does not fit in device") 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 pipelined = wordmap.values.map(_.map(_._2.pipelined)).flatten.reduce(_ || _) val depth = concurrency require (depth >= 0) require (!pipelined || depth > 0, "Register-based device with request/response handshaking needs concurrency > 0") @@ -60,7 +69,7 @@ object RegMapper def ofBits(bits: List[Boolean]) = bits.foldRight(0){ case (x,y) => (if (x) 1 else 0) | y << 1 } // Find the minimal mask that can decide the register map - val mask = AddressDecoder(regmap.map(_._1)) + val mask = AddressDecoder(wordmap.keySet.toList) val maskMatch = ~UInt(mask, width = inBits) val maskFilter = toBits(mask) val maskBits = maskFilter.filter(x => x).size @@ -75,17 +84,22 @@ object RegMapper 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) + // Transform the wordmap into minimal decoded indexes, Seq[(index, bit, field)] + val flat = wordmap.toList.map { case (word, fields) => + val index = regIndexI(word) + val uint = UInt(word, 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) } + // Confirm that no field spans a word boundary + fields foreach { case (bit, field) => + val off = bit - 8*bytes*word + // println(s"Reg ${word}: [${off}, ${off+field.width})") + require (off + field.width <= bytes * 8, s"Field at word ${word}*(${bytes}B) has bits [${off}, ${off+field.width}), which exceeds word limit.") + } + // println("mapping 0x%x -> 0x%x for 0x%x/%d".format(word, index, mask, maskBits)) + fields.map { case (bit, field) => (index, bit-8*bytes*word, field) } }.flatten // Forward declaration of all flow control signals diff --git a/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala index 395bae81..36297152 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala @@ -183,14 +183,14 @@ object RRTest0Map // All fields must respect byte alignment, or else it won't behave like an SRAM val map = Seq( - 0 -> Seq(aa(8), ar(8), ad(8), ae(8)), - 1 -> Seq(ra(8), rr(8), rd(8), re(8)), - 2 -> Seq(da(8), dr(8), dd(8), de(8)), - 3 -> Seq(ea(8), er(8), ed(8), ee(8)), - 4 -> Seq(aa(3), ar(5), ad(1), ae(7), ra(2), rr(6), rd(4), re(4)), - 5 -> Seq(da(3), dr(5), dd(1), de(7), ea(2), er(6), ed(4), ee(4)), - 6 -> Seq(aa(8), rr(8), dd(8), ee(8)), - 7 -> Seq(ar(8), rd(8), de(8), ea(8))) + 0 -> Seq(aa(8), ar(8), ad(8), ae(8)), + 4 -> Seq(ra(8), rr(8), rd(8), re(8)), + 8 -> Seq(da(8), dr(8), dd(8), de(8)), + 12 -> Seq(ea(8), er(8), ed(8), ee(8)), + 16 -> Seq(aa(3), ar(5), ad(1), ae(7), ra(2), rr(6), rd(4), re(4)), + 20 -> Seq(da(3), dr(5), dd(1), de(7), ea(2), er(6), ed(4), ee(4)), + 24 -> Seq(aa(8), rr(8), dd(8), ee(8)), + 28 -> Seq(ar(8), rd(8), de(8), ea(8))) } object RRTest1Map @@ -203,8 +203,8 @@ object RRTest1Map def bb(bits: Int) = request(bits, busy, busy) val map = RRTest0Map.map.take(6) ++ Seq( - 6 -> Seq(pp(8), pb(8), bp(8), bb(8)), - 7 -> Seq(pp(3), pb(5), bp(1), bb(7), pb(5), bp(3), pp(4), bb(4))) + 24 -> Seq(pp(8), pb(8), bp(8), bb(8)), + 28 -> Seq(pp(3), pb(5), bp(1), bb(7), pb(5), bp(3), pp(4), bb(4))) } trait RRTest0Bundle diff --git a/src/main/scala/uncore/tilelink2/WidthWidget.scala b/src/main/scala/uncore/tilelink2/WidthWidget.scala index 87e66903..a555f2a0 100644 --- a/src/main/scala/uncore/tilelink2/WidthWidget.scala +++ b/src/main/scala/uncore/tilelink2/WidthWidget.scala @@ -174,7 +174,7 @@ class TLWidthWidget(innerBeatBytes: Int) extends LazyModule object TLWidthWidget { // applied to the TL source node; y.node := WidthWidget(x.node, 16) - def apply(x: TLBaseNode, innerBeatBytes: Int)(implicit sourceInfo: SourceInfo): TLBaseNode = { + def apply(innerBeatBytes: Int)(x: TLBaseNode)(implicit sourceInfo: SourceInfo): TLBaseNode = { val widget = LazyModule(new TLWidthWidget(innerBeatBytes)) widget.node := x widget.node