diff --git a/uncore/src/main/scala/tilelink2/GPIO.scala b/uncore/src/main/scala/tilelink2/GPIO.scala new file mode 100644 index 00000000..e4b8572f --- /dev/null +++ b/uncore/src/main/scala/tilelink2/GPIO.scala @@ -0,0 +1,29 @@ +// See LICENSE for license details. + +package uncore.tilelink2 + +import Chisel._ + +case class GPIOParams(num: Int, address: Option[BigInt] = None) + +trait GPIOBundle +{ + val params: GPIOParams + val gpio = UInt(width = params.num) +} + +trait GPIOModule extends HasRegMap +{ + val params: GPIOParams + val io: GPIOBundle + + val state = RegInit(UInt(0)) + io.gpio := state + + regmap(0 -> Seq(RegField(params.num, state))) +} + +// Create a concrete TL2 version of the abstract GPIO slave +class TLGPIO(p: GPIOParams) extends TLRegisterRouter(p.address)( + new TLRegBundle(p, _) with GPIOBundle)( + new TLRegModule(p, _, _) with GPIOModule) diff --git a/uncore/src/main/scala/tilelink2/RegField.scala b/uncore/src/main/scala/tilelink2/RegField.scala index b8e638e3..509c7441 100644 --- a/uncore/src/main/scala/tilelink2/RegField.scala +++ b/uncore/src/main/scala/tilelink2/RegField.scala @@ -5,6 +5,10 @@ package uncore.tilelink2 import Chisel._ case class RegField(width: Int, read: RegField.ReadFn, write: RegField.WriteFn) +{ + require (width > 0) +} + object RegField { type ReadFn = Bool => (Bool, UInt) diff --git a/uncore/src/main/scala/tilelink2/RegisterRouter.scala b/uncore/src/main/scala/tilelink2/RegisterRouter.scala new file mode 100644 index 00000000..cda3426e --- /dev/null +++ b/uncore/src/main/scala/tilelink2/RegisterRouter.scala @@ -0,0 +1,122 @@ +// See LICENSE for license details. + +package uncore.tilelink2 + +import Chisel._ + +class TLRegisterNode(address: AddressSet, beatBytes: Int = 4) + extends TLManagerNode(beatBytes, TLManagerParameters( + address = Seq(address), + supportsGet = TransferSizes(1, beatBytes), + supportsPutPartial = TransferSizes(1, beatBytes), + supportsPutFull = TransferSizes(1, beatBytes), + fifoId = Some(0))) // requests are handled in order +{ + require (!address.strided) + + // Calling this method causes the matching TL2 bundle to be + // configured to route all requests to the listed RegFields. + def regmap(mapping: RegField.Map*) = { + val regmap = mapping.toList + require (!regmap.isEmpty) + + // 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 + + // Confirm that no register is too big + require (flat.map(_._2).max <= beatBytes*8) + + // All registers must fit inside the device address space + val maxIndex = regmap.map(_._1).max + require (address.mask >= maxIndex*beatBytes) + + // Which register is touched? + val alignBits = log2Ceil(beatBytes) + val addressBits = log2Up(maxIndex+1) + val a = bundleIn(0).a // Must apply Queue !!! (so no change once started) + val d = bundleIn(0).d + val regIdx = a.bits.address(addressBits+alignBits-1, alignBits) + val regSel = UIntToOH(regIdx) + + // What is the access? + val opcode = a.bits.opcode + val read = a.valid && opcode === TLMessages.Get + val write = a.valid && (opcode === TLMessages.PutFullData || opcode === TLMessages.PutPartialData) + val wmaskWide = Vec.tabulate(beatBytes*8) { i => a.bits.wmask(i/8) } .toBits.asUInt + val dataIn = a.bits.data & wmaskWide // zero undefined bits + + // The output values for each register + val dataOutAcc = Array.tabulate(maxIndex+1) { _ => UInt(0) } + // The ready state for read and write + val rReadyAcc = Array.tabulate(maxIndex+1) { _ => Bool(true) } + val wReadyAcc = Array.tabulate(maxIndex+1) { _ => Bool(true) } + + // Apply all the field methods + flat.foreach { case (reg, low, field) => + val high = low + field.width - 1 + val rfire = wmaskWide(high, low).orR() + val wfire = wmaskWide(high, low).andR() + val sel = regSel(reg) + val ren = read && sel && rfire + val wen = write && sel && wfire + val (rReady, rResult) = field.read(ren) + val wReady = field.write(wen, dataIn(high, low)) + dataOutAcc(reg) = dataOutAcc(reg) | (rResult << low) + rReadyAcc(reg) = rReadyAcc(reg) && (!rfire || rReady) + wReadyAcc(reg) = wReadyAcc(reg) && (!wfire || wReady) + } + + // Create the output data signal + val dataOut = Vec(dataOutAcc)(regIdx) + val rReady = Vec(rReadyAcc)(regIdx) + val wReady = Vec(wReadyAcc)(regIdx) + + val ready = (read && rReady) || (write && wReady) + a.ready := ready && d.ready + d.valid := a.valid && ready + + val edge = edgesIn(0) + d.bits := edge.AccessAck(a.bits.source, a.bits.size) + // avoid a Mux on the data bus by manually overriding two fields + d.bits.data := dataOut + d.bits.opcode := Mux(opcode === TLMessages.Get, TLMessages.AccessAck, TLMessages.AccessAckData) + } +} + +object TLRegisterNode +{ + def apply(address: AddressSet, beatBytes: Int = 4) = new TLRegisterNode(address, beatBytes) +} + +// 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 TLRegFactory(address: AddressSet, beatBytes: Int) extends TLFactory +{ + val node = TLRegisterNode(address, beatBytes) +} + +class TLRegBundle[P](val params: P, val tl_in: Vec[TLBundle]) extends Bundle + +class TLRegModule[P, B <: Bundle](val params: P, val io: B, factory: TLRegFactory) + extends TLModule(factory) with HasRegMap +{ + def regmap(mapping: RegField.Map*) = factory.node.regmap(mapping:_*) +} + +class TLRegisterRouter[B <: Bundle, M <: TLModule] + (address: Option[BigInt] = None, size: BigInt = 4096, beatBytes: Int = 4) + (bundleBuilder: Vec[TLBundle] => B) + (moduleBuilder: (B, TLRegFactory) => M) + extends TLRegFactory(AddressSet(size-1, address), beatBytes) +{ + require (size % 4096 == 0) // devices should be 4K aligned + require (isPow2(size)) + require (size >= 4096) + + lazy val module = Module(moduleBuilder(bundleBuilder(node.bundleIn), this)) +}