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
|
* bff8 mtime lo
|
||||||
* bffc mtime hi
|
* bffc mtime hi
|
||||||
*/
|
*/
|
||||||
val ipi_base = 0
|
|
||||||
val timecmp_base = c.timecmpOffset(0) / c.beatBytes
|
|
||||||
val time_base = c.timeOffset / c.beatBytes
|
|
||||||
|
|
||||||
regmap((
|
regmap(
|
||||||
RegField.split(makeRegFields(ipi), ipi_base, c.beatBytes) ++
|
0 -> makeRegFields(ipi),
|
||||||
RegField.split(makeRegFields(timecmp.flatten), timecmp_base, c.beatBytes) ++
|
c.timecmpOffset(0) -> makeRegFields(timecmp.flatten),
|
||||||
RegField.split(makeRegFields(time), time_base, c.beatBytes)):_*)
|
c.timeOffset -> makeRegFields(time))
|
||||||
|
|
||||||
def makeRegFields(s: Seq[UInt]) = s.map(r => RegField(regWidth, r))
|
def makeRegFields(s: Seq[UInt]) = s.map(r => RegField(regWidth, r))
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ trait ExampleModule extends HasRegMap
|
|||||||
regmap(
|
regmap(
|
||||||
0 -> Seq(
|
0 -> Seq(
|
||||||
RegField(params.num, state)),
|
RegField(params.num, state)),
|
||||||
1 -> Seq(
|
4 -> Seq(
|
||||||
RegField.w1ToClear(4, pending, state)))
|
RegField.w1ToClear(4, pending, state)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,9 @@ case class RegField(width: Int, read: RegReadFn, write: RegWriteFn)
|
|||||||
|
|
||||||
object RegField
|
object RegField
|
||||||
{
|
{
|
||||||
|
// Byte address => sequence of bitfields, lowest index => lowest address
|
||||||
type Map = (Int, Seq[RegField])
|
type Map = (Int, Seq[RegField])
|
||||||
|
|
||||||
def apply(n: Int) : RegField = apply(n, (), ())
|
def apply(n: Int) : RegField = apply(n, (), ())
|
||||||
def apply(n: Int, rw: UInt) : RegField = apply(n, rw, rw)
|
def apply(n: Int, rw: UInt) : RegField = apply(n, rw, rw)
|
||||||
def r(n: Int, r: RegReadFn) : RegField = apply(n, r, ())
|
def r(n: Int, r: RegReadFn) : RegField = apply(n, r, ())
|
||||||
|
@ -29,26 +29,35 @@ object RegMapper
|
|||||||
{
|
{
|
||||||
// Create a generic register-based device
|
// Create a generic register-based device
|
||||||
def apply(bytes: Int, concurrency: Int, undefZero: Boolean, in: DecoupledIO[RegMapperInput], mapping: RegField.Map*) = {
|
def apply(bytes: Int, concurrency: Int, undefZero: Boolean, in: DecoupledIO[RegMapperInput], mapping: RegField.Map*) = {
|
||||||
val regmap = mapping.toList.filter(!_._2.isEmpty)
|
val bytemap = mapping.toList
|
||||||
require (!regmap.isEmpty)
|
|
||||||
|
|
||||||
// Ensure no register appears twice
|
|
||||||
regmap.combinations(2).foreach { case Seq((reg1, _), (reg2, _)) =>
|
|
||||||
require (reg1 != reg2)
|
|
||||||
}
|
|
||||||
// Don't be an asshole...
|
// 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
|
// Make sure registers fit
|
||||||
val inParams = in.bits.params
|
val inParams = in.bits.params
|
||||||
val inBits = inParams.indexBits
|
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 out = Wire(Irrevocable(new RegMapperOutput(inParams)))
|
||||||
val front = Wire(Irrevocable(new RegMapperInput(inParams)))
|
val front = Wire(Irrevocable(new RegMapperInput(inParams)))
|
||||||
front.bits := in.bits
|
front.bits := in.bits
|
||||||
|
|
||||||
// Must this device pipeline the control channel?
|
// 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
|
val depth = concurrency
|
||||||
require (depth >= 0)
|
require (depth >= 0)
|
||||||
require (!pipelined || depth > 0, "Register-based device with request/response handshaking needs concurrency > 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 }
|
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
|
// 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 maskMatch = ~UInt(mask, width = inBits)
|
||||||
val maskFilter = toBits(mask)
|
val maskFilter = toBits(mask)
|
||||||
val maskBits = maskFilter.filter(x => x).size
|
val maskBits = maskFilter.filter(x => x).size
|
||||||
@ -75,17 +84,22 @@ object RegMapper
|
|||||||
val iRightReg = Array.fill(regSize) { Bool(true) }
|
val iRightReg = Array.fill(regSize) { Bool(true) }
|
||||||
val oRightReg = Array.fill(regSize) { Bool(true) }
|
val oRightReg = Array.fill(regSize) { Bool(true) }
|
||||||
|
|
||||||
// Flatten the regmap into (RegIndex:Int, Offset:Int, field:RegField)
|
// Transform the wordmap into minimal decoded indexes, Seq[(index, bit, field)]
|
||||||
val flat = regmap.map { case (reg, fields) =>
|
val flat = wordmap.toList.map { case (word, fields) =>
|
||||||
val offsets = fields.scanLeft(0)(_ + _.width).init
|
val index = regIndexI(word)
|
||||||
val index = regIndexI(reg)
|
val uint = UInt(word, width = inBits)
|
||||||
val uint = UInt(reg, width = inBits)
|
|
||||||
if (undefZero) {
|
if (undefZero) {
|
||||||
iRightReg(index) = ((front.bits.index ^ uint) & maskMatch) === UInt(0)
|
iRightReg(index) = ((front.bits.index ^ uint) & maskMatch) === UInt(0)
|
||||||
oRightReg(index) = ((back .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))
|
// Confirm that no field spans a word boundary
|
||||||
(offsets zip fields) map { case (o, f) => (index, o, f) }
|
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
|
}.flatten
|
||||||
|
|
||||||
// Forward declaration of all flow control signals
|
// 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
|
// All fields must respect byte alignment, or else it won't behave like an SRAM
|
||||||
val map = Seq(
|
val map = Seq(
|
||||||
0 -> Seq(aa(8), ar(8), ad(8), ae(8)),
|
0 -> Seq(aa(8), ar(8), ad(8), ae(8)),
|
||||||
1 -> Seq(ra(8), rr(8), rd(8), re(8)),
|
4 -> Seq(ra(8), rr(8), rd(8), re(8)),
|
||||||
2 -> Seq(da(8), dr(8), dd(8), de(8)),
|
8 -> Seq(da(8), dr(8), dd(8), de(8)),
|
||||||
3 -> Seq(ea(8), er(8), ed(8), ee(8)),
|
12 -> 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)),
|
16 -> 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)),
|
20 -> 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)),
|
24 -> Seq(aa(8), rr(8), dd(8), ee(8)),
|
||||||
7 -> Seq(ar(8), rd(8), de(8), ea(8)))
|
28 -> Seq(ar(8), rd(8), de(8), ea(8)))
|
||||||
}
|
}
|
||||||
|
|
||||||
object RRTest1Map
|
object RRTest1Map
|
||||||
@ -203,8 +203,8 @@ object RRTest1Map
|
|||||||
def bb(bits: Int) = request(bits, busy, busy)
|
def bb(bits: Int) = request(bits, busy, busy)
|
||||||
|
|
||||||
val map = RRTest0Map.map.take(6) ++ Seq(
|
val map = RRTest0Map.map.take(6) ++ Seq(
|
||||||
6 -> Seq(pp(8), pb(8), bp(8), bb(8)),
|
24 -> 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)))
|
28 -> Seq(pp(3), pb(5), bp(1), bb(7), pb(5), bp(3), pp(4), bb(4)))
|
||||||
}
|
}
|
||||||
|
|
||||||
trait RRTest0Bundle
|
trait RRTest0Bundle
|
||||||
|
Loading…
Reference in New Issue
Block a user