From 5e34b313ee0c1e4762a3bd8dde1c3eb5fbb4c13d Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Thu, 22 Sep 2016 19:49:29 -0700 Subject: [PATCH] RegMapper: regmap(...) now takes BYTE addresses If a device has configurable bus-width, we need a stable way of enumerating registers. The byte offset stays unchanged. This change also makes it possible to put an arbitrary number of RegFields starting at some address which are then chopped up into appropriately bus- sized registers. --- src/main/scala/uncore/devices/Prci.scala | 11 ++-- src/main/scala/uncore/tilelink2/Example.scala | 2 +- .../scala/uncore/tilelink2/RegField.scala | 2 + .../scala/uncore/tilelink2/RegMapper.scala | 50 ++++++++++++------- .../uncore/tilelink2/RegisterRouterTest.scala | 20 ++++---- 5 files changed, 49 insertions(+), 36 deletions(-) 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/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/RegField.scala b/src/main/scala/uncore/tilelink2/RegField.scala index 365649dc..5db870d6 100644 --- a/src/main/scala/uncore/tilelink2/RegField.scala +++ b/src/main/scala/uncore/tilelink2/RegField.scala @@ -85,7 +85,9 @@ 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, ()) 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