diff --git a/src/main/scala/uncore/tilelink2/RegMapper.scala b/src/main/scala/uncore/tilelink2/RegMapper.scala index 88882737..b20937f8 100644 --- a/src/main/scala/uncore/tilelink2/RegMapper.scala +++ b/src/main/scala/uncore/tilelink2/RegMapper.scala @@ -28,8 +28,8 @@ class RegMapperOutput(params: RegMapperParams) extends GenericParameterizedBundl object RegMapper { // Create a generic register-based device - def apply(bytes: Int, concurrency: Option[Int], in: DecoupledIO[RegMapperInput], mapping: RegField.Map*) = { - val regmap = mapping.toList + def apply(bytes: Int, concurrency: Option[Int], undefZero: Boolean, in: DecoupledIO[RegMapperInput], mapping: RegField.Map*) = { + val regmap = mapping.toList.filter(!_._2.isEmpty) require (!regmap.isEmpty) // Ensure no register appears twice @@ -37,15 +37,31 @@ object RegMapper require (reg1 != reg2) } - // Flatten the regmap into (Reg:Int, Offset:Int, field:RegField) - val flat = regmap.map { case (reg, fields) => - val offsets = fields.scanLeft(0)(_ + _.width).init - (offsets zip fields) map { case (o, f) => (reg, o, f) } - }.flatten - require (!flat.isEmpty) + // Convert to and from Bits + def toBits(x: Int, tail: List[Boolean] = List.empty): List[Boolean] = + if (x == 0) tail.reverse else toBits(x >> 1, ((x & 1) == 1) :: tail) + 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 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) + + // 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) + // 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))) @@ -69,13 +85,13 @@ object RegMapper val woready = Wire(Vec(flat.size, Bool())) // Per-register list of all control signals needed for data to flow - val rifire = Array.tabulate(endIndex) { i => Seq(Bool(true)) } - val wifire = Array.tabulate(endIndex) { i => Seq(Bool(true)) } - val rofire = Array.tabulate(endIndex) { i => Seq(Bool(true)) } - val wofire = Array.tabulate(endIndex) { i => Seq(Bool(true)) } + val rifire = Array.tabulate(regSize) { i => Seq(Bool(true)) } + val wifire = Array.tabulate(regSize) { i => Seq(Bool(true)) } + val rofire = Array.tabulate(regSize) { i => Seq(Bool(true)) } + val wofire = Array.tabulate(regSize) { i => Seq(Bool(true)) } // The output values for each register - val dataOut = Array.tabulate(endIndex) { _ => UInt(0) } + val dataOut = Array.tabulate(regSize) { _ => UInt(0) } // Which bits are touched? val frontMask = FillInterleaved(8, front.bits.mask) @@ -110,8 +126,10 @@ object RegMapper val wifireMux = Vec(wifire.map(_.reduce(_ && _))) val rofireMux = Vec(rofire.map(_.reduce(_ && _))) val wofireMux = Vec(wofire.map(_.reduce(_ && _))) - val iready = Mux(front.bits.read, rifireMux(front.bits.index), wifireMux(front.bits.index)) - val oready = Mux(back .bits.read, rofireMux(back .bits.index), wofireMux(back .bits.index)) + val iindex = regIndexU(front.bits.index) + val oindex = regIndexU(back .bits.index) + val iready = Mux(front.bits.read, rifireMux(iindex), wifireMux(iindex)) + val oready = Mux(back .bits.read, rofireMux(oindex), wofireMux(oindex)) // Connect the pipeline in.ready := front.ready && iready @@ -120,11 +138,11 @@ object RegMapper out.valid := back.valid && oready // Which register is touched? - val frontSel = UIntToOH(front.bits.index) - val backSel = UIntToOH(back.bits.index) + val frontSel = UIntToOH(iindex) + val backSel = UIntToOH(oindex) // Include the per-register one-hot selected criteria - for (reg <- 0 until endIndex) { + for (reg <- 0 until regSize) { rifire(reg) = (in.valid && front.ready && front.bits.read && frontSel(reg)) +: rifire(reg) wifire(reg) = (in.valid && front.ready && !front.bits.read && frontSel(reg)) +: wifire(reg) rofire(reg) = (back.valid && out.ready && back .bits.read && backSel (reg)) +: rofire(reg) @@ -141,7 +159,7 @@ object RegMapper } out.bits.read := back.bits.read - out.bits.data := Vec(dataOut)(back.bits.index) + out.bits.data := Vec(dataOut)(oindex) out.bits.extra := back.bits.extra (endIndex, out) diff --git a/src/main/scala/uncore/tilelink2/RegisterRouter.scala b/src/main/scala/uncore/tilelink2/RegisterRouter.scala index c20f0c49..58e9f8c6 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouter.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouter.scala @@ -4,7 +4,7 @@ package uncore.tilelink2 import Chisel._ -class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatBytes: Int = 4) +class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatBytes: Int = 4, undefZero: Boolean = true) extends TLManagerNode(beatBytes, TLManagerParameters( address = Seq(address), supportsGet = TransferSizes(1, beatBytes), @@ -36,7 +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, in, mapping:_*) + val (endIndex, out) = RegMapper(beatBytes, concurrency, undefZero, in, mapping:_*) // All registers must fit inside the device address space require (address.mask >= (endIndex-1)*beatBytes) @@ -67,17 +67,17 @@ class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatB object TLRegisterNode { - def apply(address: AddressSet, concurrency: Option[Int] = None, beatBytes: Int = 4) = - new TLRegisterNode(address, concurrency, beatBytes) + def apply(address: AddressSet, concurrency: Option[Int] = None, beatBytes: Int = 4, undefZero: Boolean = true) = + new TLRegisterNode(address, concurrency, beatBytes, undefZero) } // These convenience methods below combine to make it possible to create a TL2 // register mapped device from a totally abstract register mapped device. // See GPIO.scala in this directory for an example -abstract class TLRegisterRouterBase(address: AddressSet, interrupts: Int, concurrency: Option[Int], beatBytes: Int) extends LazyModule +abstract class TLRegisterRouterBase(address: AddressSet, interrupts: Int, concurrency: Option[Int], beatBytes: Int, undefZero: Boolean) extends LazyModule { - val node = TLRegisterNode(address, concurrency, beatBytes) + val node = TLRegisterNode(address, concurrency, beatBytes, undefZero) val intnode = IntSourceNode(name + s" @ ${address.base}", interrupts) } @@ -100,10 +100,10 @@ class TLRegModule[P, B <: TLRegBundleBase](val params: P, bundleBuilder: => B, r } class TLRegisterRouter[B <: TLRegBundleBase, M <: LazyModuleImp] - (val base: BigInt, val interrupts: Int = 0, val size: BigInt = 4096, val concurrency: Option[Int] = None, val beatBytes: Int = 4) + (val base: BigInt, val interrupts: Int = 0, val size: BigInt = 4096, val concurrency: Option[Int] = None, val beatBytes: Int = 4, undefZero: Boolean = true) (bundleBuilder: TLRegBundleArg => B) (moduleBuilder: (=> B, TLRegisterRouterBase) => M) - extends TLRegisterRouterBase(AddressSet(base, size-1), interrupts, concurrency, beatBytes) + extends TLRegisterRouterBase(AddressSet(base, size-1), interrupts, concurrency, beatBytes, undefZero) { require (isPow2(size)) // require (size >= 4096) ... not absolutely required, but highly recommended