When we first implemented TL, we thought this was helpful, because it made WidthWidgets stateless in all cases. However, it put too much burden on all other masters and slaves, none of which benefitted from this signal. Furthermore, even with addr_lo, WidthWidgets were information lossy because when they widen, they have no information about what to fill in the new high bits of addr_lo.
147 lines
5.6 KiB
Scala
147 lines
5.6 KiB
Scala
// See LICENSE.SiFive for license details.
|
|
|
|
package freechips.rocketchip.tilelink
|
|
|
|
import Chisel._
|
|
import freechips.rocketchip.config.Parameters
|
|
import freechips.rocketchip.diplomacy._
|
|
import freechips.rocketchip.regmapper._
|
|
import freechips.rocketchip.util.HeterogeneousBag
|
|
import scala.math.{min,max}
|
|
|
|
class TLRegisterNode(
|
|
address: Seq[AddressSet],
|
|
device: Device,
|
|
deviceKey: String = "reg/control",
|
|
concurrency: Int = 0,
|
|
beatBytes: Int = 4,
|
|
undefZero: Boolean = true,
|
|
executable: Boolean = false)
|
|
extends TLManagerNode(Seq(TLManagerPortParameters(
|
|
Seq(TLManagerParameters(
|
|
address = address,
|
|
resources = Seq(Resource(device, deviceKey)),
|
|
executable = executable,
|
|
supportsGet = TransferSizes(1, beatBytes),
|
|
supportsPutPartial = TransferSizes(1, beatBytes),
|
|
supportsPutFull = TransferSizes(1, beatBytes),
|
|
fifoId = Some(0))), // requests are handled in order
|
|
beatBytes = beatBytes,
|
|
minLatency = min(concurrency, 1)))) // the Queue adds at most one cycle
|
|
{
|
|
val size = 1 << log2Ceil(1 + address.map(_.max).max - address.map(_.base).min)
|
|
require (size >= beatBytes)
|
|
address.foreach { case a =>
|
|
require (a.widen(size-1).base == address.head.widen(size-1).base,
|
|
s"TLRegisterNode addresses (${address}) must be aligned to its size ${size}")
|
|
}
|
|
|
|
// 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 a = bundleIn(0).a
|
|
val d = bundleIn(0).d
|
|
val edge = edgesIn(0)
|
|
|
|
// Please forgive me ...
|
|
val baseEnd = 0
|
|
val (sizeEnd, sizeOff) = (edge.bundle.sizeBits + baseEnd, baseEnd)
|
|
val (sourceEnd, sourceOff) = (edge.bundle.sourceBits + sizeEnd, sizeEnd)
|
|
|
|
val params = RegMapperParams(log2Up(size/beatBytes), beatBytes, sourceEnd)
|
|
val in = Wire(Decoupled(new RegMapperInput(params)))
|
|
in.bits.read := a.bits.opcode === TLMessages.Get
|
|
in.bits.index := edge.addr_hi(a.bits)
|
|
in.bits.data := a.bits.data
|
|
in.bits.mask := a.bits.mask
|
|
in.bits.extra := Cat(a.bits.source, a.bits.size)
|
|
|
|
// Invoke the register map builder
|
|
val out = RegMapper(beatBytes, concurrency, undefZero, in, mapping:_*)
|
|
|
|
// No flow control needed
|
|
in.valid := a.valid
|
|
a.ready := in.ready
|
|
d.valid := out.valid
|
|
out.ready := d.ready
|
|
|
|
// We must restore the size to enable width adapters to work
|
|
d.bits := edge.AccessAck(
|
|
toSource = out.bits.extra(sourceEnd-1, sourceOff),
|
|
lgSize = out.bits.extra(sizeEnd-1, sizeOff))
|
|
|
|
// avoid a Mux on the data bus by manually overriding two fields
|
|
d.bits.data := out.bits.data
|
|
d.bits.opcode := Mux(out.bits.read, TLMessages.AccessAckData, TLMessages.AccessAck)
|
|
|
|
// Tie off unused channels
|
|
bundleIn(0).b.valid := Bool(false)
|
|
bundleIn(0).c.ready := Bool(true)
|
|
bundleIn(0).e.ready := Bool(true)
|
|
}
|
|
}
|
|
|
|
object TLRegisterNode
|
|
{
|
|
def apply(
|
|
address: Seq[AddressSet],
|
|
device: Device,
|
|
deviceKey: String = "reg/control",
|
|
concurrency: Int = 0,
|
|
beatBytes: Int = 4,
|
|
undefZero: Boolean = true,
|
|
executable: Boolean = false) =
|
|
new TLRegisterNode(address, device, deviceKey, concurrency, beatBytes, undefZero, executable)
|
|
}
|
|
|
|
// 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(devname: String, devcompat: Seq[String], val address: AddressSet, interrupts: Int, concurrency: Int, beatBytes: Int, undefZero: Boolean, executable: Boolean)(implicit p: Parameters) extends LazyModule
|
|
{
|
|
val device = new SimpleDevice(devname, devcompat)
|
|
val node = TLRegisterNode(Seq(address), device, "reg/control", concurrency, beatBytes, undefZero, executable)
|
|
val intnode = IntSourceNode(IntSourcePortSimple(num = interrupts, resources = Seq(Resource(device, "int"))))
|
|
}
|
|
|
|
case class TLRegBundleArg(interrupts: HeterogeneousBag[Vec[Bool]], in: HeterogeneousBag[TLBundle])(implicit val p: Parameters)
|
|
|
|
class TLRegBundleBase(arg: TLRegBundleArg) extends Bundle
|
|
{
|
|
implicit val p = arg.p
|
|
val interrupts = arg.interrupts
|
|
val in = arg.in
|
|
}
|
|
|
|
class TLRegBundle[P](val params: P, arg: TLRegBundleArg)(implicit p: Parameters) extends TLRegBundleBase(arg)
|
|
|
|
class TLRegModule[P, B <: TLRegBundleBase](val params: P, bundleBuilder: => B, router: TLRegisterRouterBase)
|
|
extends LazyModuleImp(router) with HasRegMap
|
|
{
|
|
val io = bundleBuilder
|
|
val interrupts = if (io.interrupts.isEmpty) Vec(0, Bool()) else io.interrupts(0)
|
|
val address = router.address
|
|
def regmap(mapping: RegField.Map*) = router.node.regmap(mapping:_*)
|
|
}
|
|
|
|
class TLRegisterRouter[B <: TLRegBundleBase, M <: LazyModuleImp](
|
|
val base: BigInt,
|
|
val devname: String,
|
|
val devcompat: Seq[String],
|
|
val interrupts: Int = 0,
|
|
val size: BigInt = 4096,
|
|
val concurrency: Int = 0,
|
|
val beatBytes: Int = 4,
|
|
val undefZero: Boolean = true,
|
|
val executable: Boolean = false)
|
|
(bundleBuilder: TLRegBundleArg => B)
|
|
(moduleBuilder: (=> B, TLRegisterRouterBase) => M)(implicit p: Parameters)
|
|
extends TLRegisterRouterBase(devname, devcompat, AddressSet(base, size-1), interrupts, concurrency, beatBytes, undefZero, executable)
|
|
{
|
|
require (isPow2(size))
|
|
// require (size >= 4096) ... not absolutely required, but highly recommended
|
|
|
|
lazy val module = moduleBuilder(bundleBuilder(TLRegBundleArg(intnode.bundleOut, node.bundleIn)), this)
|
|
}
|