1
0

tilelink2: connect abstract register-based modules to TileLink

This commit is contained in:
Wesley W. Terpstra 2016-08-26 15:48:48 -07:00
parent 917a9c8e5d
commit 18e149098a
3 changed files with 155 additions and 0 deletions

View File

@ -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)

View File

@ -5,6 +5,10 @@ package uncore.tilelink2
import Chisel._ import Chisel._
case class RegField(width: Int, read: RegField.ReadFn, write: RegField.WriteFn) case class RegField(width: Int, read: RegField.ReadFn, write: RegField.WriteFn)
{
require (width > 0)
}
object RegField object RegField
{ {
type ReadFn = Bool => (Bool, UInt) type ReadFn = Bool => (Bool, UInt)

View File

@ -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))
}