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.
This commit is contained in:
parent
972ca06729
commit
5e34b313ee
@ -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))
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ trait ExampleModule extends HasRegMap
|
||||
regmap(
|
||||
0 -> Seq(
|
||||
RegField(params.num, state)),
|
||||
1 -> Seq(
|
||||
4 -> Seq(
|
||||
RegField.w1ToClear(4, pending, state)))
|
||||
}
|
||||
|
||||
|
@ -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, ())
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user