Merge branch 'master' into use-companion
This commit is contained in:
commit
7603b86239
48
src/main/scala/uncore/tilelink2/Buffer.scala
Normal file
48
src/main/scala/uncore/tilelink2/Buffer.scala
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import chisel3.internal.sourceinfo.SourceInfo
|
||||||
|
|
||||||
|
class TLBuffer(entries: Int = 2, pipe: Boolean = false) extends LazyModule
|
||||||
|
{
|
||||||
|
val node = TLIdentityNode()
|
||||||
|
|
||||||
|
lazy val module = new LazyModuleImp(this) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val in = node.bundleIn
|
||||||
|
val out = node.bundleOut
|
||||||
|
}
|
||||||
|
|
||||||
|
val in = io.in(0)
|
||||||
|
val out = io.out(0)
|
||||||
|
|
||||||
|
out.a <> Queue(in .a, entries, pipe)
|
||||||
|
in .d <> Queue(out.d, entries, pipe)
|
||||||
|
|
||||||
|
val edge = node.edgesOut(0) // same as edgeIn(0)
|
||||||
|
if (edge.manager.anySupportAcquire && edge.client.anySupportProbe) {
|
||||||
|
in .b <> Queue(out.b, entries, pipe)
|
||||||
|
out.c <> Queue(in .c, entries, pipe)
|
||||||
|
out.e <> Queue(out.e, entries, pipe)
|
||||||
|
} else {
|
||||||
|
in.b.valid := Bool(false)
|
||||||
|
in.c.ready := Bool(true)
|
||||||
|
in.e.ready := Bool(true)
|
||||||
|
out.b.ready := Bool(true)
|
||||||
|
out.c.valid := Bool(false)
|
||||||
|
out.e.valid := Bool(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object TLBuffer
|
||||||
|
{
|
||||||
|
// applied to the TL source node; connect (TLBuffer(x.node) -> y.node)
|
||||||
|
def apply(x: TLBaseNode, entries: Int = 2, pipe: Boolean = false)(implicit lazyModule: LazyModule, sourceInfo: SourceInfo): TLBaseNode = {
|
||||||
|
val buffer = LazyModule(new TLBuffer(entries, pipe))
|
||||||
|
lazyModule.connect(x -> buffer.node)
|
||||||
|
buffer.node
|
||||||
|
}
|
||||||
|
}
|
183
src/main/scala/uncore/tilelink2/Bundles.scala
Normal file
183
src/main/scala/uncore/tilelink2/Bundles.scala
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
|
||||||
|
abstract class GenericParameterizedBundle[T <: Object](val params: T) extends Bundle
|
||||||
|
{
|
||||||
|
override def cloneType = {
|
||||||
|
try {
|
||||||
|
this.getClass.getConstructors.head.newInstance(params).asInstanceOf[this.type]
|
||||||
|
} catch {
|
||||||
|
case e: java.lang.IllegalArgumentException =>
|
||||||
|
throwException("Unable to use GenericParameterizedBundle.cloneType on " +
|
||||||
|
this.getClass + ", probably because " + this.getClass +
|
||||||
|
"() takes more than one argument. Consider overriding " +
|
||||||
|
"cloneType() on " + this.getClass, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class TLBundleBase(params: TLBundleParameters) extends GenericParameterizedBundle(params)
|
||||||
|
|
||||||
|
// common combos in lazy policy:
|
||||||
|
// Put + Acquire
|
||||||
|
// Release + AccessAck
|
||||||
|
|
||||||
|
object TLMessages
|
||||||
|
{
|
||||||
|
// A B C D E
|
||||||
|
val PutFullData = UInt(0) // . . => AccessAck
|
||||||
|
val PutPartialData = UInt(1) // . . => AccessAck
|
||||||
|
val ArithmeticData = UInt(2) // . . => AccessAckData
|
||||||
|
val LogicalData = UInt(3) // . . => AccessAckData
|
||||||
|
val Get = UInt(4) // . . => AccessAckData
|
||||||
|
val Hint = UInt(5) // . . => HintAck
|
||||||
|
val Acquire = UInt(6) // . => Grant[Data]
|
||||||
|
val Probe = UInt(6) // . => ProbeAck[Data]
|
||||||
|
val AccessAck = UInt(0) // . .
|
||||||
|
val AccessAckData = UInt(1) // . .
|
||||||
|
val HintAck = UInt(2) // . .
|
||||||
|
//val PutThroughData = UInt(3) // . // future extension ?
|
||||||
|
val ProbeAck = UInt(4) // .
|
||||||
|
val ProbeAckData = UInt(5) // .
|
||||||
|
val Release = UInt(6) // . => ReleaseAck
|
||||||
|
val ReleaseData = UInt(7) // . => ReleaseAck
|
||||||
|
val Grant = UInt(4) // . => GrantAck
|
||||||
|
val GrantData = UInt(5) // . => GrantAck
|
||||||
|
val ReleaseAck = UInt(6) // .
|
||||||
|
val GrantAck = UInt(0) // .
|
||||||
|
|
||||||
|
def isA(x: UInt) = x <= Acquire
|
||||||
|
def isB(x: UInt) = x <= Probe
|
||||||
|
def isC(x: UInt) = x <= ReleaseData
|
||||||
|
def isD(x: UInt) = x <= ReleaseAck
|
||||||
|
}
|
||||||
|
|
||||||
|
object TLPermissions
|
||||||
|
{
|
||||||
|
// Cap types (Grant = new permissions, Probe = permisions <= target)
|
||||||
|
val toT = UInt(0)
|
||||||
|
val toB = UInt(1)
|
||||||
|
val toN = UInt(2)
|
||||||
|
def isCap(x: UInt) = x <= toN
|
||||||
|
|
||||||
|
// Grow types (Acquire = permissions >= target)
|
||||||
|
val NtoB = UInt(0)
|
||||||
|
val NtoT = UInt(1)
|
||||||
|
val BtoT = UInt(2)
|
||||||
|
def isGrow(x: UInt) = x <= BtoT
|
||||||
|
|
||||||
|
// Shrink types (ProbeAck, Release)
|
||||||
|
val TtoB = UInt(0)
|
||||||
|
val TtoN = UInt(1)
|
||||||
|
val BtoN = UInt(2)
|
||||||
|
def isShrink(x: UInt) = x <= BtoN
|
||||||
|
|
||||||
|
// Report types (ProbeAck)
|
||||||
|
val TtoT = UInt(3)
|
||||||
|
val BtoB = UInt(4)
|
||||||
|
val NtoN = UInt(5)
|
||||||
|
def isReport(x: UInt) = x <= NtoN
|
||||||
|
}
|
||||||
|
|
||||||
|
object TLAtomics
|
||||||
|
{
|
||||||
|
// Arithmetic types
|
||||||
|
val MIN = UInt(0)
|
||||||
|
val MAX = UInt(1)
|
||||||
|
val MINU = UInt(2)
|
||||||
|
val MAXU = UInt(3)
|
||||||
|
val ADD = UInt(4)
|
||||||
|
def isArithmetic(x: UInt) = x <= ADD
|
||||||
|
|
||||||
|
// Logical types
|
||||||
|
val XOR = UInt(0)
|
||||||
|
val OR = UInt(1)
|
||||||
|
val AND = UInt(2)
|
||||||
|
val SWAP = UInt(3)
|
||||||
|
def isLogical(x: UInt) = x <= SWAP
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed trait TLChannel
|
||||||
|
sealed trait TLDataChannel extends TLChannel
|
||||||
|
sealed trait TLAddrChannel extends TLDataChannel
|
||||||
|
|
||||||
|
final class TLBundleA(params: TLBundleParameters)
|
||||||
|
extends TLBundleBase(params) with TLAddrChannel
|
||||||
|
{
|
||||||
|
// fixed fields during multibeat:
|
||||||
|
val opcode = UInt(width = 3)
|
||||||
|
val param = UInt(width = 3) // amo_opcode || perms || hint
|
||||||
|
val size = UInt(width = params.sizeBits)
|
||||||
|
val source = UInt(width = params.sourceBits) // from
|
||||||
|
val addr_hi = UInt(width = params.addrHiBits) // to
|
||||||
|
// variable fields during multibeat:
|
||||||
|
val mask = UInt(width = params.dataBits/8)
|
||||||
|
val data = UInt(width = params.dataBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
final class TLBundleB(params: TLBundleParameters)
|
||||||
|
extends TLBundleBase(params) with TLAddrChannel
|
||||||
|
{
|
||||||
|
// fixed fields during multibeat:
|
||||||
|
val opcode = UInt(width = 3)
|
||||||
|
val param = UInt(width = 3)
|
||||||
|
val size = UInt(width = params.sizeBits)
|
||||||
|
val source = UInt(width = params.sourceBits) // to
|
||||||
|
val addr_hi = UInt(width = params.addrHiBits) // from
|
||||||
|
// variable fields during multibeat:
|
||||||
|
val mask = UInt(width = params.dataBits/8)
|
||||||
|
val data = UInt(width = params.dataBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
final class TLBundleC(params: TLBundleParameters)
|
||||||
|
extends TLBundleBase(params) with TLAddrChannel
|
||||||
|
{
|
||||||
|
// fixed fields during multibeat:
|
||||||
|
val opcode = UInt(width = 3)
|
||||||
|
val param = UInt(width = 3)
|
||||||
|
val size = UInt(width = params.sizeBits)
|
||||||
|
val source = UInt(width = params.sourceBits) // from
|
||||||
|
val addr_hi = UInt(width = params.addrHiBits) // to
|
||||||
|
val addr_lo = UInt(width = params.addrLoBits) // instead of mask
|
||||||
|
// variable fields during multibeat:
|
||||||
|
val data = UInt(width = params.dataBits)
|
||||||
|
val error = Bool() // AccessAck[Data]
|
||||||
|
}
|
||||||
|
|
||||||
|
final class TLBundleD(params: TLBundleParameters)
|
||||||
|
extends TLBundleBase(params) with TLDataChannel
|
||||||
|
{
|
||||||
|
// fixed fields during multibeat:
|
||||||
|
val opcode = UInt(width = 3)
|
||||||
|
val param = UInt(width = 2)
|
||||||
|
val size = UInt(width = params.sizeBits)
|
||||||
|
val source = UInt(width = params.sourceBits) // to
|
||||||
|
val sink = UInt(width = params.sinkBits) // from
|
||||||
|
val addr_lo = UInt(width = params.addrLoBits) // instead of mask
|
||||||
|
// variable fields during multibeat:
|
||||||
|
val data = UInt(width = params.dataBits)
|
||||||
|
val error = Bool() // AccessAck[Data], Grant[Data]
|
||||||
|
}
|
||||||
|
|
||||||
|
final class TLBundleE(params: TLBundleParameters)
|
||||||
|
extends TLBundleBase(params) with TLChannel
|
||||||
|
{
|
||||||
|
val sink = UInt(width = params.sourceBits) // to
|
||||||
|
}
|
||||||
|
|
||||||
|
class TLBundle(params: TLBundleParameters) extends TLBundleBase(params)
|
||||||
|
{
|
||||||
|
val a = Decoupled(new TLBundleA(params))
|
||||||
|
val b = Decoupled(new TLBundleB(params)).flip
|
||||||
|
val c = Decoupled(new TLBundleC(params))
|
||||||
|
val d = Decoupled(new TLBundleD(params)).flip
|
||||||
|
val e = Decoupled(new TLBundleE(params))
|
||||||
|
}
|
||||||
|
|
||||||
|
object TLBundle
|
||||||
|
{
|
||||||
|
def apply(params: TLBundleParameters) = new TLBundle(params)
|
||||||
|
}
|
606
src/main/scala/uncore/tilelink2/Edges.scala
Normal file
606
src/main/scala/uncore/tilelink2/Edges.scala
Normal file
@ -0,0 +1,606 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import chisel3.internal.sourceinfo.SourceInfo
|
||||||
|
|
||||||
|
class TLEdge(
|
||||||
|
client: TLClientPortParameters,
|
||||||
|
manager: TLManagerPortParameters)
|
||||||
|
extends TLEdgeParameters(client, manager)
|
||||||
|
{
|
||||||
|
def isHiAligned(addr_hi: UInt, lgSize: UInt): Bool = {
|
||||||
|
if (maxLgSize == 0) Bool(true) else {
|
||||||
|
val mask = UIntToOH1(lgSize, maxLgSize) >> log2Ceil(manager.beatBytes)
|
||||||
|
(addr_hi & mask) === UInt(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def isLoAligned(addr_lo: UInt, lgSize: UInt): Bool = {
|
||||||
|
if (maxLgSize == 0) Bool(true) else {
|
||||||
|
val mask = UIntToOH1(lgSize, maxLgSize)
|
||||||
|
(addr_lo & mask) === UInt(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This gets used everywhere, so make the smallest circuit possible ...
|
||||||
|
def mask(addr_lo: UInt, lgSize: UInt): UInt = {
|
||||||
|
val lgBytes = log2Ceil(manager.beatBytes)
|
||||||
|
val sizeOH = UIntToOH(lgSize, lgBytes)
|
||||||
|
def helper(i: Int): Seq[(Bool, Bool)] = {
|
||||||
|
if (i == 0) {
|
||||||
|
Seq((lgSize >= UInt(lgBytes), Bool(true)))
|
||||||
|
} else {
|
||||||
|
val sub = helper(i-1)
|
||||||
|
val size = sizeOH(lgBytes - i)
|
||||||
|
val bit = addr_lo(lgBytes - i)
|
||||||
|
val nbit = !bit
|
||||||
|
Seq.tabulate (1 << i) { j =>
|
||||||
|
val (sub_acc, sub_eq) = sub(j/2)
|
||||||
|
val eq = sub_eq && (if (j % 2 == 1) bit else nbit)
|
||||||
|
val acc = sub_acc || (size && eq)
|
||||||
|
(acc, eq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Cat(helper(lgBytes).map(_._1).reverse)
|
||||||
|
}
|
||||||
|
|
||||||
|
def addr_lo(mask: UInt): UInt = {
|
||||||
|
// Almost OHToUInt, but bits set => bits not set
|
||||||
|
def helper(mask: UInt, width: Int): UInt = {
|
||||||
|
if (width <= 1) {
|
||||||
|
UInt(0)
|
||||||
|
} else if (width == 2) {
|
||||||
|
~mask(0, 0)
|
||||||
|
} else {
|
||||||
|
val mid = 1 << (log2Up(width)-1)
|
||||||
|
val hi = mask(width-1, mid)
|
||||||
|
val lo = mask(mid-1, 0)
|
||||||
|
Cat(!lo.orR, helper(hi | lo, mid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
helper(mask, bundle.dataBits/8)
|
||||||
|
}
|
||||||
|
|
||||||
|
def staticHasData(bundle: TLChannel): Option[Boolean] = {
|
||||||
|
bundle match {
|
||||||
|
case _:TLBundleA => {
|
||||||
|
// Do there exist A messages with Data?
|
||||||
|
val aDataYes = manager.anySupportArithmetic || manager.anySupportLogical || manager.anySupportPutFull || manager.anySupportPutPartial
|
||||||
|
// Do there exist A messages without Data?
|
||||||
|
val aDataNo = manager.anySupportAcquire || manager.anySupportGet || manager.anySupportHint
|
||||||
|
// Statically optimize the case where hasData is a constant
|
||||||
|
if (!aDataYes) Some(false) else if (!aDataNo) Some(true) else None
|
||||||
|
}
|
||||||
|
case _:TLBundleB => {
|
||||||
|
// Do there exist B messages with Data?
|
||||||
|
val bDataYes = client.anySupportArithmetic || client.anySupportLogical || client.anySupportPutFull || client.anySupportPutPartial
|
||||||
|
// Do there exist B messages without Data?
|
||||||
|
val bDataNo = client.anySupportProbe || client.anySupportGet || client.anySupportHint
|
||||||
|
// Statically optimize the case where hasData is a constant
|
||||||
|
if (!bDataYes) Some(false) else if (!bDataNo) Some(true) else None
|
||||||
|
}
|
||||||
|
case _:TLBundleC => {
|
||||||
|
// Do there eixst C messages with Data?
|
||||||
|
val cDataYes = client.anySupportGet || client.anySupportArithmetic || client.anySupportLogical || client.anySupportProbe
|
||||||
|
// Do there exist C messages without Data?
|
||||||
|
val cDataNo = client.anySupportPutFull || client.anySupportPutPartial || client.anySupportHint || client.anySupportProbe
|
||||||
|
if (!cDataYes) Some(false) else if (!cDataNo) Some(true) else None
|
||||||
|
}
|
||||||
|
case _:TLBundleD => {
|
||||||
|
// Do there eixst D messages with Data?
|
||||||
|
val dDataYes = manager.anySupportGet || manager.anySupportArithmetic || manager.anySupportLogical || manager.anySupportAcquire
|
||||||
|
// Do there exist D messages without Data?
|
||||||
|
val dDataNo = manager.anySupportPutFull || manager.anySupportPutPartial || manager.anySupportHint || manager.anySupportAcquire
|
||||||
|
if (!dDataYes) Some(false) else if (!dDataNo) Some(true) else None
|
||||||
|
}
|
||||||
|
case _:TLBundleE => Some(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def hasFollowUp(x: TLChannel): Bool = {
|
||||||
|
x match {
|
||||||
|
case a: TLBundleA => Bool(true)
|
||||||
|
case b: TLBundleB => Bool(true)
|
||||||
|
case c: TLBundleC => c.opcode(2) && c.opcode(1)
|
||||||
|
// opcode === TLMessages.Release ||
|
||||||
|
// opcode === TLMessages.ReleaseData
|
||||||
|
case d: TLBundleD => d.opcode(2) && !d.opcode(1)
|
||||||
|
// opcode === TLMessages.Grant ||
|
||||||
|
// opcode === TLMessages.GrantData
|
||||||
|
case e: TLBundleE => Bool(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def hasData(x: TLChannel): Bool = {
|
||||||
|
val opdata = x match {
|
||||||
|
case a: TLBundleA => !a.opcode(2)
|
||||||
|
// opcode === TLMessages.PutFullData ||
|
||||||
|
// opcode === TLMessages.PutPartialData ||
|
||||||
|
// opcode === TLMessages.ArithmeticData ||
|
||||||
|
// opcode === TLMessages.LogicalData
|
||||||
|
case b: TLBundleB => !b.opcode(2)
|
||||||
|
// opcode === TLMessages.PutFullData ||
|
||||||
|
// opcode === TLMessages.PutPartialData ||
|
||||||
|
// opcode === TLMessages.ArithmeticData ||
|
||||||
|
// opcode === TLMessages.LogicalData
|
||||||
|
case c: TLBundleC => c.opcode(0)
|
||||||
|
// opcode === TLMessages.AccessAckData ||
|
||||||
|
// opcode === TLMessages.ProbeAckData ||
|
||||||
|
// opcode === TLMessages.ReleaseData
|
||||||
|
case d: TLBundleD => d.opcode(0)
|
||||||
|
// opcode === TLMessages.AccessAckData ||
|
||||||
|
// opcode === TLMessages.GrantData
|
||||||
|
case e: TLBundleE => Bool(false)
|
||||||
|
}
|
||||||
|
staticHasData(x).map(Bool(_)).getOrElse(opdata)
|
||||||
|
}
|
||||||
|
|
||||||
|
def size(x: TLDataChannel): UInt = {
|
||||||
|
x match {
|
||||||
|
case a: TLBundleA => a.size
|
||||||
|
case b: TLBundleB => b.size
|
||||||
|
case c: TLBundleC => c.size
|
||||||
|
case d: TLBundleD => d.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def data(x: TLDataChannel): UInt = {
|
||||||
|
x match {
|
||||||
|
case a: TLBundleA => a.data
|
||||||
|
case b: TLBundleB => b.data
|
||||||
|
case c: TLBundleC => c.data
|
||||||
|
case d: TLBundleD => d.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def mask(x: TLDataChannel): UInt = {
|
||||||
|
x match {
|
||||||
|
case a: TLBundleA => a.mask
|
||||||
|
case b: TLBundleB => b.mask
|
||||||
|
case c: TLBundleC => mask(c.addr_lo, c.size)
|
||||||
|
case d: TLBundleD => mask(d.addr_lo, d.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def addr_lo(x: TLDataChannel): UInt = {
|
||||||
|
x match {
|
||||||
|
case a: TLBundleA => addr_lo(a.mask)
|
||||||
|
case b: TLBundleB => addr_lo(b.mask)
|
||||||
|
case c: TLBundleC => c.addr_lo
|
||||||
|
case d: TLBundleD => d.addr_lo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def address(x: TLAddrChannel): UInt = {
|
||||||
|
val hi = x match {
|
||||||
|
case a: TLBundleA => a.addr_hi
|
||||||
|
case b: TLBundleB => b.addr_hi
|
||||||
|
case c: TLBundleC => c.addr_hi
|
||||||
|
}
|
||||||
|
if (manager.beatBytes == 1) hi else Cat(hi, addr_lo(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
def numBeats(x: TLChannel): UInt = {
|
||||||
|
x match {
|
||||||
|
case _: TLBundleE => UInt(1)
|
||||||
|
case bundle: TLDataChannel => {
|
||||||
|
val hasData = this.hasData(bundle)
|
||||||
|
val size = this.size(bundle)
|
||||||
|
val cutoff = log2Ceil(manager.beatBytes)
|
||||||
|
val small = if (manager.maxTransfer <= manager.beatBytes) Bool(true) else size <= UInt(cutoff)
|
||||||
|
val decode = UIntToOH(size, maxLgSize+1) >> cutoff
|
||||||
|
Mux(!hasData || small, UInt(1), decode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TLEdgeOut(
|
||||||
|
client: TLClientPortParameters,
|
||||||
|
manager: TLManagerPortParameters)
|
||||||
|
extends TLEdge(client, manager)
|
||||||
|
{
|
||||||
|
// Transfers
|
||||||
|
def Acquire(fromSource: UInt, toAddress: UInt, lgSize: UInt, growPermissions: UInt) = {
|
||||||
|
require (manager.anySupportAcquire)
|
||||||
|
val legal = manager.supportsAcquire(toAddress, lgSize)
|
||||||
|
val a = Wire(new TLBundleA(bundle))
|
||||||
|
a.opcode := TLMessages.Acquire
|
||||||
|
a.param := growPermissions
|
||||||
|
a.size := lgSize
|
||||||
|
a.source := fromSource
|
||||||
|
a.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
a.mask := SInt(-1).asUInt
|
||||||
|
a.data := UInt(0)
|
||||||
|
(legal, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
def Release(fromSource: UInt, toAddress: UInt, lgSize: UInt, shrinkPermissions: UInt) = {
|
||||||
|
require (manager.anySupportAcquire)
|
||||||
|
val legal = manager.supportsAcquire(toAddress, lgSize)
|
||||||
|
val c = Wire(new TLBundleC(bundle))
|
||||||
|
c.opcode := TLMessages.Release
|
||||||
|
c.param := shrinkPermissions
|
||||||
|
c.size := lgSize
|
||||||
|
c.source := fromSource
|
||||||
|
c.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
c.addr_lo := toAddress
|
||||||
|
c.data := UInt(0)
|
||||||
|
c.error := Bool(false)
|
||||||
|
(legal, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
def Release(fromSource: UInt, toAddress: UInt, lgSize: UInt, shrinkPermissions: UInt, data: UInt) = {
|
||||||
|
require (manager.anySupportAcquire)
|
||||||
|
val legal = manager.supportsAcquire(toAddress, lgSize)
|
||||||
|
val c = Wire(new TLBundleC(bundle))
|
||||||
|
c.opcode := TLMessages.ReleaseData
|
||||||
|
c.param := shrinkPermissions
|
||||||
|
c.size := lgSize
|
||||||
|
c.source := fromSource
|
||||||
|
c.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
c.addr_lo := toAddress
|
||||||
|
c.data := data
|
||||||
|
c.error := Bool(false)
|
||||||
|
(legal, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
def ProbeAck(fromSource: UInt, toAddress: UInt, lgSize: UInt, reportPermissions: UInt) = {
|
||||||
|
val c = Wire(new TLBundleC(bundle))
|
||||||
|
c.opcode := TLMessages.ProbeAck
|
||||||
|
c.param := reportPermissions
|
||||||
|
c.size := lgSize
|
||||||
|
c.source := fromSource
|
||||||
|
c.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
c.addr_lo := toAddress
|
||||||
|
c.data := UInt(0)
|
||||||
|
c.error := Bool(false)
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
def ProbeAck(fromSource: UInt, toAddress: UInt, lgSize: UInt, reportPermissions: UInt, data: UInt) = {
|
||||||
|
val c = Wire(new TLBundleC(bundle))
|
||||||
|
c.opcode := TLMessages.ProbeAckData
|
||||||
|
c.param := reportPermissions
|
||||||
|
c.size := lgSize
|
||||||
|
c.source := fromSource
|
||||||
|
c.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
c.addr_lo := toAddress
|
||||||
|
c.data := data
|
||||||
|
c.error := Bool(false)
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
def GrantAck(toSink: UInt) = {
|
||||||
|
val e = Wire(new TLBundleE(bundle))
|
||||||
|
e.sink := toSink
|
||||||
|
e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accesses
|
||||||
|
def Get(fromSource: UInt, toAddress: UInt, lgSize: UInt) = {
|
||||||
|
require (manager.anySupportGet)
|
||||||
|
val legal = manager.supportsGet(toAddress, lgSize)
|
||||||
|
val a = Wire(new TLBundleA(bundle))
|
||||||
|
a.opcode := TLMessages.Get
|
||||||
|
a.param := UInt(0)
|
||||||
|
a.size := lgSize
|
||||||
|
a.source := fromSource
|
||||||
|
a.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
a.mask := mask(toAddress, lgSize)
|
||||||
|
a.data := UInt(0)
|
||||||
|
(legal, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
def Put(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt) = {
|
||||||
|
require (manager.anySupportPutFull)
|
||||||
|
val legal = manager.supportsPutFull(toAddress, lgSize)
|
||||||
|
val a = Wire(new TLBundleA(bundle))
|
||||||
|
a.opcode := TLMessages.PutFullData
|
||||||
|
a.param := UInt(0)
|
||||||
|
a.size := lgSize
|
||||||
|
a.source := fromSource
|
||||||
|
a.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
a.mask := mask(toAddress, lgSize)
|
||||||
|
a.data := data
|
||||||
|
(legal, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
def Put(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, mask : UInt) = {
|
||||||
|
require (manager.anySupportPutPartial)
|
||||||
|
val legal = manager.supportsPutPartial(toAddress, lgSize)
|
||||||
|
val a = Wire(new TLBundleA(bundle))
|
||||||
|
a.opcode := TLMessages.PutPartialData
|
||||||
|
a.param := UInt(0)
|
||||||
|
a.size := lgSize
|
||||||
|
a.source := fromSource
|
||||||
|
a.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
a.mask := mask
|
||||||
|
a.data := data
|
||||||
|
(legal, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
def Arithmetic(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, atomic: UInt) = {
|
||||||
|
require (manager.anySupportArithmetic)
|
||||||
|
val legal = manager.supportsArithmetic(toAddress, lgSize)
|
||||||
|
val a = Wire(new TLBundleA(bundle))
|
||||||
|
a.opcode := TLMessages.ArithmeticData
|
||||||
|
a.param := atomic
|
||||||
|
a.size := lgSize
|
||||||
|
a.source := fromSource
|
||||||
|
a.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
a.mask := mask(toAddress, lgSize)
|
||||||
|
a.data := data
|
||||||
|
(legal, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
def Logical(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, atomic: UInt) = {
|
||||||
|
require (manager.anySupportLogical)
|
||||||
|
val legal = manager.supportsLogical(toAddress, lgSize)
|
||||||
|
val a = Wire(new TLBundleA(bundle))
|
||||||
|
a.opcode := TLMessages.LogicalData
|
||||||
|
a.param := atomic
|
||||||
|
a.size := lgSize
|
||||||
|
a.source := fromSource
|
||||||
|
a.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
a.mask := mask(toAddress, lgSize)
|
||||||
|
a.data := data
|
||||||
|
(legal, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
def Hint(fromSource: UInt, toAddress: UInt, lgSize: UInt, param: UInt) = {
|
||||||
|
require (manager.anySupportHint)
|
||||||
|
val legal = manager.supportsHint(toAddress)
|
||||||
|
val a = Wire(new TLBundleA(bundle))
|
||||||
|
a.opcode := TLMessages.Hint
|
||||||
|
a.param := param
|
||||||
|
a.size := lgSize
|
||||||
|
a.source := fromSource
|
||||||
|
a.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
a.mask := mask(toAddress, lgSize)
|
||||||
|
a.data := UInt(0)
|
||||||
|
(legal, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
def AccessAck(b: TLBundleB): TLBundleC = AccessAck(b.source, address(b), b.size)
|
||||||
|
def AccessAck(b: TLBundleB, error: Bool): TLBundleC = AccessAck(b.source, address(b), b.size, error)
|
||||||
|
def AccessAck(fromSource: UInt, toAddress: UInt, lgSize: UInt): TLBundleC = AccessAck(fromSource, toAddress, lgSize, Bool(false))
|
||||||
|
def AccessAck(fromSource: UInt, toAddress: UInt, lgSize: UInt, error: Bool) = {
|
||||||
|
val c = Wire(new TLBundleC(bundle))
|
||||||
|
c.opcode := TLMessages.AccessAck
|
||||||
|
c.param := UInt(0)
|
||||||
|
c.size := lgSize
|
||||||
|
c.source := fromSource
|
||||||
|
c.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
c.addr_lo := toAddress
|
||||||
|
c.data := UInt(0)
|
||||||
|
c.error := error
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
def AccessAck(b: TLBundleB, data: UInt): TLBundleC = AccessAck(b.source, address(b), b.size, data)
|
||||||
|
def AccessAck(b: TLBundleB, data: UInt, error: Bool): TLBundleC = AccessAck(b.source, address(b), b.size, data, error)
|
||||||
|
def AccessAck(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt): TLBundleC = AccessAck(fromSource, toAddress, lgSize, data, Bool(false))
|
||||||
|
def AccessAck(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, error: Bool) = {
|
||||||
|
val c = Wire(new TLBundleC(bundle))
|
||||||
|
c.opcode := TLMessages.AccessAckData
|
||||||
|
c.param := UInt(0)
|
||||||
|
c.size := lgSize
|
||||||
|
c.source := fromSource
|
||||||
|
c.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
c.addr_lo := toAddress
|
||||||
|
c.data := data
|
||||||
|
c.error := error
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
def HintAck(b: TLBundleB): TLBundleC = HintAck(b.source, address(b), b.size)
|
||||||
|
def HintAck(fromSource: UInt, toAddress: UInt, lgSize: UInt) = {
|
||||||
|
val c = Wire(new TLBundleC(bundle))
|
||||||
|
c.opcode := TLMessages.HintAck
|
||||||
|
c.param := UInt(0)
|
||||||
|
c.size := lgSize
|
||||||
|
c.source := fromSource
|
||||||
|
c.addr_hi := toAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
c.addr_lo := toAddress
|
||||||
|
c.data := UInt(0)
|
||||||
|
c.error := Bool(false)
|
||||||
|
c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TLEdgeIn(
|
||||||
|
client: TLClientPortParameters,
|
||||||
|
manager: TLManagerPortParameters)
|
||||||
|
extends TLEdge(client, manager)
|
||||||
|
{
|
||||||
|
// Transfers
|
||||||
|
def Probe(fromAddress: UInt, toSource: UInt, lgSize: UInt, capPermissions: UInt) = {
|
||||||
|
require (client.anySupportProbe)
|
||||||
|
val legal = client.supportsProbe(fromAddress, lgSize)
|
||||||
|
val b = Wire(new TLBundleB(bundle))
|
||||||
|
b.opcode := TLMessages.Probe
|
||||||
|
b.param := capPermissions
|
||||||
|
b.size := lgSize
|
||||||
|
b.source := toSource
|
||||||
|
b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
b.mask := SInt(-1).asUInt
|
||||||
|
b.data := UInt(0)
|
||||||
|
(legal, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
def Grant(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, capPermissions: UInt): TLBundleD = Grant(fromAddress, fromSink, toSource, lgSize, capPermissions, Bool(false))
|
||||||
|
def Grant(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, capPermissions: UInt, error: Bool) = {
|
||||||
|
val d = Wire(new TLBundleD(bundle))
|
||||||
|
d.opcode := TLMessages.Grant
|
||||||
|
d.param := capPermissions
|
||||||
|
d.size := lgSize
|
||||||
|
d.source := toSource
|
||||||
|
d.sink := fromSink
|
||||||
|
d.addr_lo := fromAddress
|
||||||
|
d.data := UInt(0)
|
||||||
|
d.error := error
|
||||||
|
d
|
||||||
|
}
|
||||||
|
|
||||||
|
def Grant(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, capPermissions: UInt, data: UInt): TLBundleD = Grant(fromAddress, fromSink, toSource, lgSize, capPermissions, data, Bool(false))
|
||||||
|
def Grant(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, capPermissions: UInt, data: UInt, error: Bool) = {
|
||||||
|
val d = Wire(new TLBundleD(bundle))
|
||||||
|
d.opcode := TLMessages.GrantData
|
||||||
|
d.param := capPermissions
|
||||||
|
d.size := lgSize
|
||||||
|
d.source := toSource
|
||||||
|
d.sink := fromSink
|
||||||
|
d.addr_lo := fromAddress
|
||||||
|
d.data := data
|
||||||
|
d.error := error
|
||||||
|
d
|
||||||
|
}
|
||||||
|
|
||||||
|
def ReleaseAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt) = {
|
||||||
|
val d = Wire(new TLBundleD(bundle))
|
||||||
|
d.opcode := TLMessages.ReleaseAck
|
||||||
|
d.param := UInt(0)
|
||||||
|
d.size := lgSize
|
||||||
|
d.source := toSource
|
||||||
|
d.sink := fromSink
|
||||||
|
d.addr_lo := fromAddress
|
||||||
|
d.data := UInt(0)
|
||||||
|
d.error := Bool(false)
|
||||||
|
d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accesses
|
||||||
|
def Get(fromAddress: UInt, toSource: UInt, lgSize: UInt) = {
|
||||||
|
require (client.anySupportGet)
|
||||||
|
val legal = client.supportsGet(toSource, lgSize)
|
||||||
|
val b = Wire(new TLBundleB(bundle))
|
||||||
|
b.opcode := TLMessages.Get
|
||||||
|
b.param := UInt(0)
|
||||||
|
b.size := lgSize
|
||||||
|
b.source := toSource
|
||||||
|
b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
b.mask := mask(fromAddress, lgSize)
|
||||||
|
b.data := UInt(0)
|
||||||
|
(legal, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
def Put(fromAddress: UInt, toSource: UInt, lgSize: UInt, data: UInt) = {
|
||||||
|
require (client.anySupportPutFull)
|
||||||
|
val legal = client.supportsPutFull(toSource, lgSize)
|
||||||
|
val b = Wire(new TLBundleB(bundle))
|
||||||
|
b.opcode := TLMessages.PutFullData
|
||||||
|
b.param := UInt(0)
|
||||||
|
b.size := lgSize
|
||||||
|
b.source := toSource
|
||||||
|
b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
b.mask := mask(fromAddress, lgSize)
|
||||||
|
b.data := data
|
||||||
|
(legal, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
def Put(fromAddress: UInt, toSource: UInt, lgSize: UInt, data: UInt, mask : UInt) = {
|
||||||
|
require (client.anySupportPutPartial)
|
||||||
|
val legal = client.supportsPutPartial(toSource, lgSize)
|
||||||
|
val b = Wire(new TLBundleB(bundle))
|
||||||
|
b.opcode := TLMessages.PutPartialData
|
||||||
|
b.param := UInt(0)
|
||||||
|
b.size := lgSize
|
||||||
|
b.source := toSource
|
||||||
|
b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
b.mask := mask
|
||||||
|
b.data := data
|
||||||
|
(legal, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
def Arithmetic(fromAddress: UInt, toSource: UInt, lgSize: UInt, data: UInt, atomic: UInt) = {
|
||||||
|
require (client.anySupportArithmetic)
|
||||||
|
val legal = client.supportsArithmetic(toSource, lgSize)
|
||||||
|
val b = Wire(new TLBundleB(bundle))
|
||||||
|
b.opcode := TLMessages.ArithmeticData
|
||||||
|
b.param := atomic
|
||||||
|
b.size := lgSize
|
||||||
|
b.source := toSource
|
||||||
|
b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
b.mask := mask(fromAddress, lgSize)
|
||||||
|
b.data := data
|
||||||
|
(legal, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
def Logical(fromAddress: UInt, toSource: UInt, lgSize: UInt, data: UInt, atomic: UInt) = {
|
||||||
|
require (client.anySupportLogical)
|
||||||
|
val legal = client.supportsLogical(toSource, lgSize)
|
||||||
|
val b = Wire(new TLBundleB(bundle))
|
||||||
|
b.opcode := TLMessages.LogicalData
|
||||||
|
b.param := atomic
|
||||||
|
b.size := lgSize
|
||||||
|
b.source := toSource
|
||||||
|
b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
b.mask := mask(fromAddress, lgSize)
|
||||||
|
b.data := data
|
||||||
|
(legal, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
def Hint(fromAddress: UInt, toSource: UInt, lgSize: UInt, param: UInt) = {
|
||||||
|
require (client.anySupportHint)
|
||||||
|
val legal = client.supportsHint(toSource)
|
||||||
|
val b = Wire(new TLBundleB(bundle))
|
||||||
|
b.opcode := TLMessages.Hint
|
||||||
|
b.param := param
|
||||||
|
b.size := lgSize
|
||||||
|
b.source := toSource
|
||||||
|
b.addr_hi := fromAddress >> log2Ceil(manager.beatBytes)
|
||||||
|
b.mask := mask(fromAddress, lgSize)
|
||||||
|
b.data := UInt(0)
|
||||||
|
(legal, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
def AccessAck(a: TLBundleA, fromSink: UInt): TLBundleD = AccessAck(address(a), fromSink, a.source, a.size)
|
||||||
|
def AccessAck(a: TLBundleA, fromSink: UInt, error: Bool): TLBundleD = AccessAck(address(a), fromSink, a.source, a.size, error)
|
||||||
|
def AccessAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt): TLBundleD = AccessAck(fromAddress, fromSink, toSource, lgSize, Bool(false))
|
||||||
|
def AccessAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, error: Bool) = {
|
||||||
|
val d = Wire(new TLBundleD(bundle))
|
||||||
|
d.opcode := TLMessages.AccessAck
|
||||||
|
d.param := UInt(0)
|
||||||
|
d.size := lgSize
|
||||||
|
d.source := toSource
|
||||||
|
d.sink := fromSink
|
||||||
|
d.addr_lo := fromAddress
|
||||||
|
d.data := UInt(0)
|
||||||
|
d.error := error
|
||||||
|
d
|
||||||
|
}
|
||||||
|
|
||||||
|
def AccessAck(a: TLBundleA, fromSink: UInt, data: UInt): TLBundleD = AccessAck(address(a), fromSink, a.source, a.size, data)
|
||||||
|
def AccessAck(a: TLBundleA, fromSink: UInt, data: UInt, error: Bool): TLBundleD = AccessAck(address(a), fromSink, a.source, a.size, data, error)
|
||||||
|
def AccessAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, data: UInt): TLBundleD = AccessAck(fromAddress, fromSink, toSource, lgSize, data, Bool(false))
|
||||||
|
def AccessAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt, data: UInt, error: Bool) = {
|
||||||
|
val d = Wire(new TLBundleD(bundle))
|
||||||
|
d.opcode := TLMessages.AccessAckData
|
||||||
|
d.param := UInt(0)
|
||||||
|
d.size := lgSize
|
||||||
|
d.source := toSource
|
||||||
|
d.sink := fromSink
|
||||||
|
d.addr_lo := fromAddress
|
||||||
|
d.data := data
|
||||||
|
d.error := error
|
||||||
|
d
|
||||||
|
}
|
||||||
|
|
||||||
|
def HintAck(a: TLBundleA, sink: UInt = UInt(0)): TLBundleD = HintAck(address(a), sink, a.source, a.size)
|
||||||
|
def HintAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt) = {
|
||||||
|
val d = Wire(new TLBundleD(bundle))
|
||||||
|
d.opcode := TLMessages.HintAck
|
||||||
|
d.param := UInt(0)
|
||||||
|
d.size := lgSize
|
||||||
|
d.source := toSource
|
||||||
|
d.sink := fromSink
|
||||||
|
d.addr_lo := fromAddress
|
||||||
|
d.data := UInt(0)
|
||||||
|
d.error := Bool(false)
|
||||||
|
d
|
||||||
|
}
|
||||||
|
}
|
249
src/main/scala/uncore/tilelink2/Fragmenter.scala
Normal file
249
src/main/scala/uncore/tilelink2/Fragmenter.scala
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import chisel3.internal.sourceinfo.SourceInfo
|
||||||
|
import scala.math.{min,max}
|
||||||
|
|
||||||
|
// minSize: minimum size of transfers supported by all outward managers
|
||||||
|
// maxSize: maximum size of transfers supported after the Fragmenter is applied
|
||||||
|
// alwaysMin: fragment all requests down to minSize (else fragment to maximum supported by manager)
|
||||||
|
// Fragmenter modifies: PutFull, PutPartial, LogicalData, Get, Hint
|
||||||
|
// Fragmenter passes: ArithmeticData (truncated to minSize if alwaysMin)
|
||||||
|
// Fragmenter breaks: Acquire (and thus cuts BCE channels)
|
||||||
|
class TLFragmenter(minSize: Int, maxSize: Int, alwaysMin: Boolean = false) extends LazyModule
|
||||||
|
{
|
||||||
|
require (isPow2 (maxSize))
|
||||||
|
require (isPow2 (minSize))
|
||||||
|
require (minSize < maxSize)
|
||||||
|
|
||||||
|
val fragmentBits = log2Ceil(maxSize / minSize)
|
||||||
|
|
||||||
|
def expandTransfer(x: TransferSizes) = if (!x) x else {
|
||||||
|
require (x.max >= minSize) // validate that we can apply the fragmenter correctly
|
||||||
|
TransferSizes(x.min, maxSize)
|
||||||
|
}
|
||||||
|
def shrinkTransfer(x: TransferSizes) =
|
||||||
|
if (!alwaysMin) x else
|
||||||
|
if (x.min <= minSize) TransferSizes(x.min, min(minSize, x.max)) else
|
||||||
|
TransferSizes.none
|
||||||
|
def mapManager(m: TLManagerParameters) = m.copy(
|
||||||
|
supportsAcquire = TransferSizes.none, // this adapter breaks acquires
|
||||||
|
supportsArithmetic = shrinkTransfer(m.supportsArithmetic),
|
||||||
|
supportsLogical = expandTransfer(m.supportsLogical),
|
||||||
|
supportsGet = expandTransfer(m.supportsGet),
|
||||||
|
supportsPutFull = expandTransfer(m.supportsPutFull),
|
||||||
|
supportsPutPartial = expandTransfer(m.supportsPutPartial))
|
||||||
|
def mapClient(c: TLClientParameters) = c.copy(
|
||||||
|
sourceId = IdRange(c.sourceId.start << fragmentBits, c.sourceId.end << fragmentBits),
|
||||||
|
// since we break Acquires, none of these work either:
|
||||||
|
supportsProbe = TransferSizes.none,
|
||||||
|
supportsArithmetic = TransferSizes.none,
|
||||||
|
supportsLogical = TransferSizes.none,
|
||||||
|
supportsGet = TransferSizes.none,
|
||||||
|
supportsPutFull = TransferSizes.none,
|
||||||
|
supportsPutPartial = TransferSizes.none,
|
||||||
|
supportsHint = false)
|
||||||
|
|
||||||
|
val node = TLAdapterNode(
|
||||||
|
clientFn = { case Seq(c) => c.copy(clients = c.clients.map(mapClient)) },
|
||||||
|
managerFn = { case Seq(m) => m.copy(managers = m.managers.map(mapManager)) })
|
||||||
|
|
||||||
|
lazy val module = new LazyModuleImp(this) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val in = node.bundleIn
|
||||||
|
val out = node.bundleOut
|
||||||
|
}
|
||||||
|
|
||||||
|
// All managers must share a common FIFO domain (responses might end up interleaved)
|
||||||
|
val edgeOut = node.edgesOut(0)
|
||||||
|
val edgeIn = node.edgesIn(0)
|
||||||
|
val manager = edgeOut.manager
|
||||||
|
val managers = manager.managers
|
||||||
|
val beatBytes = manager.beatBytes
|
||||||
|
val fifoId = managers(0).fifoId
|
||||||
|
require (fifoId.isDefined && managers.map(_.fifoId == fifoId).reduce(_ && _))
|
||||||
|
|
||||||
|
// We don't support fragmenting to sub-beat accesses
|
||||||
|
require (minSize >= beatBytes)
|
||||||
|
|
||||||
|
/* The Fragmenter is a bit tricky, because there are 5 sizes in play:
|
||||||
|
* max size -- the maximum transfer size possible
|
||||||
|
* orig size -- the original pre-fragmenter size
|
||||||
|
* frag size -- the modified post-fragmenter size
|
||||||
|
* min size -- the threshold below which frag=orig
|
||||||
|
* beat size -- the amount transfered on any given beat
|
||||||
|
*
|
||||||
|
* The relationships are as follows:
|
||||||
|
* max >= orig >= frag
|
||||||
|
* max > min >= beat
|
||||||
|
* It IS possible that orig <= min (then frag=orig; ie: no fragmentation)
|
||||||
|
*
|
||||||
|
* The fragment# (sent via TL.source) is measured in multiples of min size.
|
||||||
|
* Meanwhile, to track the progress, counters measure in multiples of beat size.
|
||||||
|
*
|
||||||
|
* Here is an example of a bus with max=256, min=8, beat=4 and a device supporting 16.
|
||||||
|
*
|
||||||
|
* in.A out.A (frag#) out.D (frag#) in.D gen# ack#
|
||||||
|
* get64 get16 6 ackD16 6 ackD64 12 15
|
||||||
|
* ackD16 6 ackD64 14
|
||||||
|
* ackD16 6 ackD64 13
|
||||||
|
* ackD16 6 ackD64 12
|
||||||
|
* get16 4 ackD16 4 ackD64 8 11
|
||||||
|
* ackD16 4 ackD64 10
|
||||||
|
* ackD16 4 ackD64 9
|
||||||
|
* ackD16 4 ackD64 8
|
||||||
|
* get16 2 ackD16 2 ackD64 4 7
|
||||||
|
* ackD16 2 ackD64 6
|
||||||
|
* ackD16 2 ackD64 5
|
||||||
|
* ackD16 2 ackD64 4
|
||||||
|
* get16 0 ackD16 0 ackD64 0 3
|
||||||
|
* ackD16 0 ackD64 2
|
||||||
|
* ackD16 0 ackD64 1
|
||||||
|
* ackD16 0 ackD64 0
|
||||||
|
*
|
||||||
|
* get8 get8 0 ackD8 0 ackD8 0 1
|
||||||
|
* ackD8 0 ackD8 0
|
||||||
|
*
|
||||||
|
* get4 get4 0 ackD4 0 ackD4 0 0
|
||||||
|
* get1 get1 0 ackD1 0 ackD1 0 0
|
||||||
|
*
|
||||||
|
* put64 put16 6 15
|
||||||
|
* put64 put16 6 14
|
||||||
|
* put64 put16 6 13
|
||||||
|
* put64 put16 6 ack16 6 12 12
|
||||||
|
* put64 put16 4 11
|
||||||
|
* put64 put16 4 10
|
||||||
|
* put64 put16 4 9
|
||||||
|
* put64 put16 4 ack16 4 8 8
|
||||||
|
* put64 put16 2 7
|
||||||
|
* put64 put16 2 6
|
||||||
|
* put64 put16 2 5
|
||||||
|
* put64 put16 2 ack16 2 4 4
|
||||||
|
* put64 put16 0 3
|
||||||
|
* put64 put16 0 2
|
||||||
|
* put64 put16 0 1
|
||||||
|
* put64 put16 0 ack16 0 ack64 0 0
|
||||||
|
*
|
||||||
|
* put8 put8 0 1
|
||||||
|
* put8 put8 0 ack8 0 ack8 0 0
|
||||||
|
*
|
||||||
|
* put4 put4 0 ack4 0 ack4 0 0
|
||||||
|
* put1 put1 0 ack1 0 ack1 0 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
val in = io.in(0)
|
||||||
|
val out = io.out(0)
|
||||||
|
|
||||||
|
val counterBits = log2Up(maxSize/beatBytes)
|
||||||
|
val maxDownSize = if (alwaysMin) minSize else manager.maxTransfer
|
||||||
|
|
||||||
|
// First, handle the return path
|
||||||
|
val acknum = RegInit(UInt(0, width = counterBits))
|
||||||
|
val dOrig = Reg(UInt())
|
||||||
|
val dFragnum = out.d.bits.source(fragmentBits-1, 0)
|
||||||
|
val dFirst = acknum === UInt(0)
|
||||||
|
val dsizeOH = UIntToOH (out.d.bits.size, log2Ceil(maxDownSize)+1)
|
||||||
|
val dsizeOH1 = UIntToOH1(out.d.bits.size, log2Ceil(maxDownSize))
|
||||||
|
val dHasData = edgeOut.hasData(out.d.bits)
|
||||||
|
|
||||||
|
// calculate new acknum
|
||||||
|
val acknum_fragment = dFragnum << log2Ceil(minSize/beatBytes)
|
||||||
|
val acknum_size = dsizeOH1 >> log2Ceil(beatBytes)
|
||||||
|
assert (!out.d.valid || (acknum_fragment & acknum_size) === UInt(0))
|
||||||
|
val dFirst_acknum = acknum_fragment | Mux(dHasData, acknum_size, UInt(0))
|
||||||
|
val ack_decrement = Mux(dHasData, UInt(1), dsizeOH >> log2Ceil(beatBytes))
|
||||||
|
// calculate the original size
|
||||||
|
val dFirst_size = OH1ToUInt((dFragnum << log2Ceil(minSize)) | dsizeOH1)
|
||||||
|
|
||||||
|
when (out.d.fire()) {
|
||||||
|
acknum := Mux(dFirst, dFirst_acknum, acknum - ack_decrement)
|
||||||
|
when (dFirst) { dOrig := dFirst_size }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swallow up non-data ack fragments
|
||||||
|
val drop = (out.d.bits.opcode === TLMessages.AccessAck) && (dFragnum =/= UInt(0))
|
||||||
|
out.d.ready := in.d.ready || drop
|
||||||
|
in.d.valid := out.d.valid && !drop
|
||||||
|
in.d.bits := out.d.bits // pass most stuff unchanged
|
||||||
|
in.d.bits.source := out.d.bits.source >> fragmentBits
|
||||||
|
in.d.bits.size := Mux(dFirst, dFirst_size, dOrig)
|
||||||
|
|
||||||
|
// What maximum transfer sizes do downstream devices support?
|
||||||
|
val maxArithmetics = managers.map(_.supportsArithmetic.max)
|
||||||
|
val maxLogicals = managers.map(_.supportsLogical.max)
|
||||||
|
val maxGets = managers.map(_.supportsGet.max)
|
||||||
|
val maxPutFulls = managers.map(_.supportsPutFull.max)
|
||||||
|
val maxPutPartials = managers.map(_.supportsPutPartial.max)
|
||||||
|
val maxHints = managers.map(m => if (m.supportsHint) maxDownSize else 0)
|
||||||
|
|
||||||
|
// We assume that the request is valid => size 0 is impossible
|
||||||
|
val lgMinSize = UInt(log2Ceil(minSize))
|
||||||
|
val maxLgArithmetics = maxArithmetics.map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
||||||
|
val maxLgLogicals = maxLogicals .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
||||||
|
val maxLgGets = maxGets .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
||||||
|
val maxLgPutFulls = maxPutFulls .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
||||||
|
val maxLgPutPartials = maxPutPartials.map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
||||||
|
val maxLgHints = maxHints .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
||||||
|
|
||||||
|
// If this is infront of a single manager, these become constants
|
||||||
|
val find = manager.find(edgeIn.address(in.a.bits))
|
||||||
|
val maxLgArithmetic = Mux1H(find, maxLgArithmetics)
|
||||||
|
val maxLgLogical = Mux1H(find, maxLgLogicals)
|
||||||
|
val maxLgGet = Mux1H(find, maxLgGets)
|
||||||
|
val maxLgPutFull = Mux1H(find, maxLgPutFulls)
|
||||||
|
val maxLgPutPartial = Mux1H(find, maxLgPutPartials)
|
||||||
|
val maxLgHint = Mux1H(find, maxLgHints)
|
||||||
|
|
||||||
|
val limit = if (alwaysMin) lgMinSize else
|
||||||
|
MuxLookup(in.a.bits.opcode, lgMinSize, Array(
|
||||||
|
TLMessages.PutFullData -> maxLgPutFull,
|
||||||
|
TLMessages.PutPartialData -> maxLgPutPartial,
|
||||||
|
TLMessages.ArithmeticData -> maxLgArithmetic,
|
||||||
|
TLMessages.LogicalData -> maxLgLogical,
|
||||||
|
TLMessages.Get -> maxLgGet,
|
||||||
|
TLMessages.Hint -> maxLgHint))
|
||||||
|
|
||||||
|
val aOrig = in.a.bits.size
|
||||||
|
val aFrag = Mux(aOrig > limit, limit, aOrig)
|
||||||
|
val aOrigOH1 = UIntToOH1(aOrig, log2Ceil(maxSize))
|
||||||
|
val aFragOH1 = UIntToOH1(aFrag, log2Ceil(maxDownSize))
|
||||||
|
val aHasData = node.edgesIn(0).hasData(in.a.bits)
|
||||||
|
val aMask = Mux(aHasData, UInt(0), aFragOH1)
|
||||||
|
|
||||||
|
val gennum = RegInit(UInt(0, width = counterBits))
|
||||||
|
val aFirst = gennum === UInt(0)
|
||||||
|
val old_gennum1 = Mux(aFirst, aOrigOH1 >> log2Ceil(beatBytes), gennum - UInt(1))
|
||||||
|
val new_gennum = ~(~old_gennum1 | (aMask >> log2Ceil(beatBytes))) // ~(~x|y) is width safe
|
||||||
|
val aFragnum = ~(~(old_gennum1 >> log2Ceil(minSize/beatBytes)) | (aFragOH1 >> log2Ceil(minSize)))
|
||||||
|
|
||||||
|
when (out.a.fire()) { gennum := new_gennum }
|
||||||
|
|
||||||
|
val delay = !aHasData && aFragnum =/= UInt(0)
|
||||||
|
out.a.valid := in.a.valid
|
||||||
|
in.a.ready := out.a.ready && !delay
|
||||||
|
out.a.bits := in.a.bits
|
||||||
|
out.a.bits.addr_hi := in.a.bits.addr_hi | (~aFragnum << log2Ceil(minSize/beatBytes) & aOrigOH1 >> log2Ceil(beatBytes))
|
||||||
|
out.a.bits.source := Cat(in.a.bits.source, aFragnum)
|
||||||
|
out.a.bits.size := aFrag
|
||||||
|
|
||||||
|
// Tie off unused channels
|
||||||
|
in.b.valid := Bool(false)
|
||||||
|
in.c.ready := Bool(true)
|
||||||
|
in.e.ready := Bool(true)
|
||||||
|
out.b.ready := Bool(true)
|
||||||
|
out.c.valid := Bool(false)
|
||||||
|
out.e.valid := Bool(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object TLFragmenter
|
||||||
|
{
|
||||||
|
// applied to the TL source node; connect (TLFragmenter(x.node, 256, 4) -> y.node)
|
||||||
|
def apply(x: TLBaseNode, minSize: Int, maxSize: Int, alwaysMin: Boolean = false)(implicit lazyModule: LazyModule, sourceInfo: SourceInfo): TLBaseNode = {
|
||||||
|
val fragmenter = LazyModule(new TLFragmenter(minSize, maxSize, alwaysMin))
|
||||||
|
lazyModule.connect(x -> fragmenter.node)
|
||||||
|
fragmenter.node
|
||||||
|
}
|
||||||
|
}
|
29
src/main/scala/uncore/tilelink2/GPIO.scala
Normal file
29
src/main/scala/uncore/tilelink2/GPIO.scala
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
|
||||||
|
case class GPIOParams(num: Int, address: BigInt)
|
||||||
|
|
||||||
|
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)
|
99
src/main/scala/uncore/tilelink2/HintHandler.scala
Normal file
99
src/main/scala/uncore/tilelink2/HintHandler.scala
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import chisel3.internal.sourceinfo.SourceInfo
|
||||||
|
|
||||||
|
// Acks Hints for managers that don't support them or Acks all Hints if !passthrough
|
||||||
|
class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = false, passthrough: Boolean = true) extends LazyModule
|
||||||
|
{
|
||||||
|
val node = TLAdapterNode(
|
||||||
|
clientFn = { case Seq(c) => if (supportClients) c.copy(clients = c.clients .map(_.copy(supportsHint = true))) else c },
|
||||||
|
managerFn = { case Seq(m) => if (supportManagers) m.copy(managers = m.managers.map(_.copy(supportsHint = true))) else m })
|
||||||
|
|
||||||
|
lazy val module = new LazyModuleImp(this) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val in = node.bundleIn
|
||||||
|
val out = node.bundleOut
|
||||||
|
}
|
||||||
|
|
||||||
|
val in = io.in(0)
|
||||||
|
val out = io.out(0)
|
||||||
|
val edgeIn = node.edgesIn(0)
|
||||||
|
val edgeOut = node.edgesOut(0)
|
||||||
|
|
||||||
|
// Don't add support for clients if there is no BCE channel
|
||||||
|
val bce = edgeOut.manager.anySupportAcquire && edgeIn.client.anySupportProbe
|
||||||
|
require (!supportClients || bce)
|
||||||
|
|
||||||
|
if (supportManagers) {
|
||||||
|
val handleA = if (passthrough) !edgeOut.manager.supportsHint(edgeIn.address(in.a.bits)) else Bool(true)
|
||||||
|
val bypassD = handleA && in.a.bits.opcode === TLMessages.Hint
|
||||||
|
|
||||||
|
// Prioritize existing D traffic over HintAck
|
||||||
|
in.d.valid := out.d.valid || (bypassD && in.a.valid)
|
||||||
|
out.d.ready := in.d.ready
|
||||||
|
in.d.bits := Mux(out.d.valid, out.d.bits, edgeIn.HintAck(in.a.bits))
|
||||||
|
|
||||||
|
in.a.ready := Mux(bypassD, in.d.ready && !out.d.valid, out.a.ready)
|
||||||
|
out.a.valid := in.a.valid && !bypassD
|
||||||
|
out.a.bits := in.a.bits
|
||||||
|
} else {
|
||||||
|
out.a.valid := in.a.valid
|
||||||
|
in.a.ready := out.a.ready
|
||||||
|
out.a.bits := in.a.bits
|
||||||
|
|
||||||
|
in.d.valid := out.d.valid
|
||||||
|
out.d.ready := in.d.ready
|
||||||
|
in.d.bits := out.d.bits
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supportClients) {
|
||||||
|
val handleB = if (passthrough) !edgeIn.client.supportsHint(out.b.bits.source) else Bool(true)
|
||||||
|
val bypassC = handleB && out.b.bits.opcode === TLMessages.Hint
|
||||||
|
|
||||||
|
// Prioritize existing C traffic over HintAck
|
||||||
|
out.c.valid := in.c.valid || (bypassC && in.b.valid)
|
||||||
|
in.c.ready := out.c.ready
|
||||||
|
out.c.bits := Mux(in.c.valid, in.c.bits, edgeOut.HintAck(out.b.bits))
|
||||||
|
|
||||||
|
out.b.ready := Mux(bypassC, out.c.ready && !in.c.valid, in.b.ready)
|
||||||
|
in.b.valid := out.b.valid && !bypassC
|
||||||
|
in.b.bits := out.b.bits
|
||||||
|
} else if (bce) {
|
||||||
|
in.b.valid := out.b.valid
|
||||||
|
out.b.ready := in.b.ready
|
||||||
|
in.b.bits := out.b.bits
|
||||||
|
|
||||||
|
out.c.valid := in.c.valid
|
||||||
|
in.c.ready := out.c.ready
|
||||||
|
out.c.bits := in.c.bits
|
||||||
|
} else {
|
||||||
|
in.b.valid := Bool(false)
|
||||||
|
in.c.ready := Bool(true)
|
||||||
|
out.b.ready := Bool(true)
|
||||||
|
out.c.valid := Bool(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bce) {
|
||||||
|
// Pass E through unchanged
|
||||||
|
out.e.valid := in.e.valid
|
||||||
|
in.e.ready := out.e.ready
|
||||||
|
out.e.bits := in.e.bits
|
||||||
|
} else {
|
||||||
|
in.e.ready := Bool(true)
|
||||||
|
out.e.valid := Bool(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object TLHintHandler
|
||||||
|
{
|
||||||
|
// applied to the TL source node; connect (TLHintHandler(x.node) -> y.node)
|
||||||
|
def apply(x: TLBaseNode, supportManagers: Boolean = true, supportClients: Boolean = false, passthrough: Boolean = true)(implicit lazyModule: LazyModule, sourceInfo: SourceInfo): TLBaseNode = {
|
||||||
|
val hints = LazyModule(new TLHintHandler(supportManagers, supportClients, passthrough))
|
||||||
|
lazyModule.connect(x -> hints.node)
|
||||||
|
hints.node
|
||||||
|
}
|
||||||
|
}
|
60
src/main/scala/uncore/tilelink2/LazyModule.scala
Normal file
60
src/main/scala/uncore/tilelink2/LazyModule.scala
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import chisel3.internal.sourceinfo._
|
||||||
|
|
||||||
|
abstract class LazyModule
|
||||||
|
{
|
||||||
|
protected[tilelink2] var bindings = List[() => Unit]()
|
||||||
|
protected[tilelink2] var children = List[LazyModule]()
|
||||||
|
protected[tilelink2] var info: SourceInfo = UnlocatableSourceInfo
|
||||||
|
protected[tilelink2] val parent = LazyModule.stack.headOption
|
||||||
|
|
||||||
|
LazyModule.stack = this :: LazyModule.stack
|
||||||
|
parent.foreach(p => p.children = this :: p.children)
|
||||||
|
|
||||||
|
// Use as: connect(source -> sink, source2 -> sink2, ...)
|
||||||
|
def connect[PO, PI, EO, EI, B <: Bundle](edges: (BaseNode[PO, PI, EO, EI, B], BaseNode[PO, PI, EO, EI, B])*)(implicit sourceInfo: SourceInfo) = {
|
||||||
|
edges.foreach { case (source, sink) =>
|
||||||
|
bindings = (source edge sink) :: bindings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def module: LazyModuleImp
|
||||||
|
implicit val lazyModule = this
|
||||||
|
|
||||||
|
protected[tilelink2] def instantiate() = {
|
||||||
|
children.reverse.foreach { c =>
|
||||||
|
// !!! fix chisel3 so we can pass the desired sourceInfo
|
||||||
|
// implicit val sourceInfo = c.module.outer.info
|
||||||
|
Module(c.module)
|
||||||
|
}
|
||||||
|
bindings.reverse.foreach { f => f () }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object LazyModule
|
||||||
|
{
|
||||||
|
protected[tilelink2] var stack = List[LazyModule]()
|
||||||
|
|
||||||
|
def apply[T <: LazyModule](bc: T)(implicit sourceInfo: SourceInfo): T = {
|
||||||
|
// Make sure the user put LazyModule around modules in the correct order
|
||||||
|
// If this require fails, probably some grandchild was missing a LazyModule
|
||||||
|
// ... or you applied LazyModule twice
|
||||||
|
require (!stack.isEmpty && (stack.head eq bc))
|
||||||
|
stack = stack.tail
|
||||||
|
bc.info = sourceInfo
|
||||||
|
bc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class LazyModuleImp(outer: LazyModule) extends Module
|
||||||
|
{
|
||||||
|
// .module had better not be accessed while LazyModules are still being built!
|
||||||
|
require (LazyModule.stack.isEmpty)
|
||||||
|
|
||||||
|
override def desiredName = outer.getClass.getName.split('.').last
|
||||||
|
outer.instantiate()
|
||||||
|
}
|
134
src/main/scala/uncore/tilelink2/Legacy.scala
Normal file
134
src/main/scala/uncore/tilelink2/Legacy.scala
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import cde.Parameters
|
||||||
|
import uncore.tilelink._
|
||||||
|
import uncore.constants._
|
||||||
|
|
||||||
|
// Instantiate 'val p' before HasTileLinkParameters tries to use it
|
||||||
|
abstract class LegacyLazyModuleImp(module: LazyModule)(implicit val p: Parameters)
|
||||||
|
extends LazyModuleImp(module) with HasTileLinkParameters
|
||||||
|
|
||||||
|
class TLLegacy(implicit val p: Parameters) extends LazyModule with HasTileLinkParameters
|
||||||
|
{
|
||||||
|
// TL legacy clients don't support anything fancy
|
||||||
|
val node = TLClientNode(TLClientParameters(
|
||||||
|
sourceId = IdRange(0, 1 << tlClientXactIdBits)))
|
||||||
|
|
||||||
|
lazy val module = new LegacyLazyModuleImp(this) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val legacy = new ClientUncachedTileLinkIO()(p).flip
|
||||||
|
val out = node.bundleOut
|
||||||
|
}
|
||||||
|
|
||||||
|
// TL legacy is dumb. All managers must support it's accesses.
|
||||||
|
val edge = node.edgesOut(0)
|
||||||
|
require (edge.manager.beatBytes == tlDataBytes)
|
||||||
|
edge.manager.managers.foreach { m =>
|
||||||
|
// If a slave supports read at all, it must support all TL Legacy requires
|
||||||
|
if (m.supportsGet) {
|
||||||
|
require (m.supportsGet.contains(TransferSizes(tlDataBytes)))
|
||||||
|
require (m.supportsGet.contains(TransferSizes(tlDataBeats * tlDataBytes)))
|
||||||
|
}
|
||||||
|
// Likewise, any put support must mean full put support
|
||||||
|
if (m.supportsPutPartial) {
|
||||||
|
require (m.supportsPutPartial.contains(TransferSizes(tlDataBytes)))
|
||||||
|
require (m.supportsPutPartial.contains(TransferSizes(tlDataBeats * tlDataBytes)))
|
||||||
|
}
|
||||||
|
// Any atomic support => must support 32-bit up to beat size of all types
|
||||||
|
if (m.supportsArithmetic || m.supportsLogical) {
|
||||||
|
require (m.supportsArithmetic.contains(TransferSizes(4, tlDataBytes)))
|
||||||
|
require (m.supportsLogical .contains(TransferSizes(4, tlDataBytes)))
|
||||||
|
}
|
||||||
|
// We straight-up require hints
|
||||||
|
require (edge.manager.allSupportHint)
|
||||||
|
}
|
||||||
|
// TL legacy will not generate PutFull
|
||||||
|
// During conversion from TL Legacy, we won't support Acquire
|
||||||
|
|
||||||
|
// Must be able to fit TL2 sink_id into TL legacy
|
||||||
|
require ((1 << tlManagerXactIdBits) >= edge.manager.endSinkId)
|
||||||
|
|
||||||
|
val out = io.out(0)
|
||||||
|
out.a.valid := io.legacy.acquire.valid
|
||||||
|
out.d.ready := io.legacy.grant .ready
|
||||||
|
io.legacy.acquire.ready := out.a.ready
|
||||||
|
io.legacy.grant .valid := out.d.valid
|
||||||
|
|
||||||
|
val source = io.legacy.acquire.bits.client_xact_id
|
||||||
|
val data = io.legacy.acquire.bits.data
|
||||||
|
val wmask = io.legacy.acquire.bits.wmask()
|
||||||
|
val address = io.legacy.acquire.bits.full_addr()
|
||||||
|
|
||||||
|
val beat = UInt(log2Ceil(tlDataBytes))
|
||||||
|
val block = UInt(log2Ceil(tlDataBytes*tlDataBeats))
|
||||||
|
|
||||||
|
// Only create atomic messages if TL2 managers support them
|
||||||
|
val atomics = if (edge.manager.anySupportLogical) {
|
||||||
|
MuxLookup(io.legacy.acquire.bits.op_code(), Wire(new TLBundleA(edge.bundle)), Array(
|
||||||
|
MemoryOpConstants.M_XA_SWAP -> edge.Logical(source, address, beat, data, TLAtomics.SWAP)._2,
|
||||||
|
MemoryOpConstants.M_XA_XOR -> edge.Logical(source, address, beat, data, TLAtomics.XOR) ._2,
|
||||||
|
MemoryOpConstants.M_XA_OR -> edge.Logical(source, address, beat, data, TLAtomics.OR) ._2,
|
||||||
|
MemoryOpConstants.M_XA_AND -> edge.Logical(source, address, beat, data, TLAtomics.AND) ._2,
|
||||||
|
MemoryOpConstants.M_XA_ADD -> edge.Arithmetic(source, address, beat, data, TLAtomics.ADD)._2,
|
||||||
|
MemoryOpConstants.M_XA_MIN -> edge.Arithmetic(source, address, beat, data, TLAtomics.MIN)._2,
|
||||||
|
MemoryOpConstants.M_XA_MAX -> edge.Arithmetic(source, address, beat, data, TLAtomics.MAX)._2,
|
||||||
|
MemoryOpConstants.M_XA_MINU -> edge.Arithmetic(source, address, beat, data, TLAtomics.MINU)._2,
|
||||||
|
MemoryOpConstants.M_XA_MAXU -> edge.Arithmetic(source, address, beat, data, TLAtomics.MAXU)._2))
|
||||||
|
} else {
|
||||||
|
Wire(new TLBundleA(edge.bundle))
|
||||||
|
}
|
||||||
|
|
||||||
|
out.a.bits := MuxLookup(io.legacy.acquire.bits.a_type, Wire(new TLBundleA(edge.bundle)), Array(
|
||||||
|
Acquire.getType -> edge.Get (source, address, beat) ._2,
|
||||||
|
Acquire.getBlockType -> edge.Get (source, address, block)._2,
|
||||||
|
Acquire.putType -> edge.Put (source, address, beat, data, wmask)._2,
|
||||||
|
Acquire.putBlockType -> edge.Put (source, address, block, data, wmask)._2,
|
||||||
|
Acquire.getPrefetchType -> edge.Hint(source, address, block, UInt(0))._2,
|
||||||
|
Acquire.putPrefetchType -> edge.Hint(source, address, block, UInt(1))._2,
|
||||||
|
Acquire.putAtomicType -> atomics))
|
||||||
|
|
||||||
|
val beatMask = UInt(tlDataBytes-1)
|
||||||
|
val blockMask = UInt(tlDataBytes*tlDataBeats-1)
|
||||||
|
val addressMask = MuxLookup(io.legacy.acquire.bits.a_type, beatMask, Array(
|
||||||
|
Acquire.getType -> beatMask,
|
||||||
|
Acquire.getBlockType -> blockMask,
|
||||||
|
Acquire.putType -> beatMask,
|
||||||
|
Acquire.putBlockType -> blockMask,
|
||||||
|
Acquire.getPrefetchType -> blockMask,
|
||||||
|
Acquire.putPrefetchType -> blockMask,
|
||||||
|
Acquire.putAtomicType -> beatMask))
|
||||||
|
|
||||||
|
// Get rid of some unneeded muxes
|
||||||
|
out.a.bits.source := source
|
||||||
|
out.a.bits.data := data
|
||||||
|
out.a.bits.addr_hi := ~(~address | addressMask) >> log2Ceil(tlDataBytes)
|
||||||
|
|
||||||
|
// TL legacy does not support bus errors
|
||||||
|
assert (!out.d.bits.error)
|
||||||
|
|
||||||
|
// Recreate the beat address counter
|
||||||
|
val beatCounter = RegInit(UInt(0, width = tlBeatAddrBits))
|
||||||
|
when (out.d.fire() && edge.hasData(out.d.bits) && out.d.bits.size === block) {
|
||||||
|
beatCounter := beatCounter + UInt(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
val grant = io.legacy.grant.bits
|
||||||
|
grant.g_type := MuxLookup(out.d.bits.opcode, Grant.prefetchAckType, Array(
|
||||||
|
TLMessages.AccessAck -> Grant.putAckType,
|
||||||
|
TLMessages.AccessAckData -> Mux(out.d.bits.size === beat, Grant.getDataBeatType, Grant.getDataBlockType),
|
||||||
|
TLMessages.HintAck -> Grant.prefetchAckType))
|
||||||
|
grant.is_builtin_type := Bool(true)
|
||||||
|
grant.client_xact_id := out.d.bits.source
|
||||||
|
grant.manager_xact_id := out.d.bits.sink
|
||||||
|
grant.data := out.d.bits.data
|
||||||
|
grant.addr_beat := beatCounter
|
||||||
|
|
||||||
|
// Tie off unused channels
|
||||||
|
out.b.ready := Bool(true)
|
||||||
|
out.c.valid := Bool(false)
|
||||||
|
out.e.valid := Bool(false)
|
||||||
|
}
|
||||||
|
}
|
411
src/main/scala/uncore/tilelink2/Monitor.scala
Normal file
411
src/main/scala/uncore/tilelink2/Monitor.scala
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import chisel3.internal.sourceinfo.{SourceInfo, SourceLine}
|
||||||
|
|
||||||
|
object TLMonitor
|
||||||
|
{
|
||||||
|
def extra(implicit sourceInfo: SourceInfo) = {
|
||||||
|
sourceInfo match {
|
||||||
|
case SourceLine(filename, line, col) => s" (connected at $filename:$line:$col)"
|
||||||
|
case _ => ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def legalizeFormatA(bundle: TLBundleA, edge: TLEdgeOut)(implicit sourceInfo: SourceInfo) = {
|
||||||
|
assert (TLMessages.isA(bundle.opcode), "'A' channel has invalid opcode" + extra)
|
||||||
|
|
||||||
|
// Reuse these subexpressions to save some firrtl lines
|
||||||
|
val source_ok = edge.client.contains(bundle.source)
|
||||||
|
val is_aligned = edge.isHiAligned(bundle.addr_hi, bundle.size)
|
||||||
|
val mask = edge.mask(edge.addr_lo(bundle.mask), bundle.size)
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.Acquire) {
|
||||||
|
assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'A' channel carries Acquire type unsupported by manager" + extra)
|
||||||
|
assert (source_ok, "'A' channel Acquire carries invalid source ID" + extra)
|
||||||
|
assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'A' channel Acquire smaller than a beat" + extra)
|
||||||
|
assert (is_aligned, "'A' channel Acquire address not aligned to size" + extra)
|
||||||
|
assert (TLPermissions.isGrow(bundle.param), "'A' channel Acquire carries invalid grow param" + extra)
|
||||||
|
assert (~bundle.mask === UInt(0), "'A' channel Acquire contains invalid mask" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.Get) {
|
||||||
|
assert (edge.manager.supportsGet(edge.address(bundle), bundle.size), "'A' channel carries Get type unsupported by manager" + extra)
|
||||||
|
assert (source_ok, "'A' channel Get carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'A' channel Get address not aligned to size" + extra)
|
||||||
|
assert (bundle.param === UInt(0), "'A' channel Get carries invalid param" + extra)
|
||||||
|
assert (bundle.mask === mask, "'A' channel Get contains invalid mask" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.PutFullData) {
|
||||||
|
assert (edge.manager.supportsPutFull(edge.address(bundle), bundle.size), "'A' channel carries PutFull type unsupported by manager" + extra)
|
||||||
|
assert (source_ok, "'A' channel PutFull carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'A' channel PutFull address not aligned to size" + extra)
|
||||||
|
assert (bundle.param === UInt(0), "'A' channel PutFull carries invalid param" + extra)
|
||||||
|
assert (bundle.mask === mask, "'A' channel PutFull contains invalid mask" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.PutPartialData) {
|
||||||
|
assert (edge.manager.supportsPutPartial(edge.address(bundle), bundle.size), "'A' channel carries PutPartial type unsupported by manager" + extra)
|
||||||
|
assert (source_ok, "'A' channel PutPartial carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'A' channel PutPartial address not aligned to size" + extra)
|
||||||
|
assert (bundle.param === UInt(0), "'A' channel PutPartial carries invalid param" + extra)
|
||||||
|
assert ((bundle.mask & ~mask) === UInt(0), "'A' channel PutPartial contains invalid mask" + extra)
|
||||||
|
assert (bundle.mask =/= UInt(0), "'A' channel PutPartial has a zero mask" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.ArithmeticData) {
|
||||||
|
assert (edge.manager.supportsArithmetic(edge.address(bundle), bundle.size), "'A' channel carries Arithmetic type unsupported by manager" + extra)
|
||||||
|
assert (source_ok, "'A' channel Arithmetic carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'A' channel Arithmetic address not aligned to size" + extra)
|
||||||
|
assert (TLAtomics.isArithmetic(bundle.param), "'A' channel Arithmetic carries invalid opcode param" + extra)
|
||||||
|
assert (bundle.mask === mask, "'A' channel Arithmetic contains invalid mask" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.LogicalData) {
|
||||||
|
assert (edge.manager.supportsLogical(edge.address(bundle), bundle.size), "'A' channel carries Logical type unsupported by manager" + extra)
|
||||||
|
assert (source_ok, "'A' channel Logical carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'A' channel Logical address not aligned to size" + extra)
|
||||||
|
assert (TLAtomics.isLogical(bundle.param), "'A' channel Logical carries invalid opcode param" + extra)
|
||||||
|
assert (bundle.mask === mask, "'A' channel Logical contains invalid mask" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.Hint) {
|
||||||
|
assert (edge.manager.supportsHint(edge.address(bundle)), "'A' channel carries Hint type unsupported by manager" + extra)
|
||||||
|
assert (source_ok, "'A' channel Hint carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'A' channel Hint address not aligned to size" + extra)
|
||||||
|
assert (bundle.mask === mask, "'A' channel Hint contains invalid mask" + extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def legalizeFormatB(bundle: TLBundleB, edge: TLEdgeIn)(implicit sourceInfo: SourceInfo) = {
|
||||||
|
assert (TLMessages.isB(bundle.opcode), "'B' channel has invalid opcode" + extra)
|
||||||
|
|
||||||
|
// Reuse these subexpressions to save some firrtl lines
|
||||||
|
val address_ok = edge.manager.contains(bundle.source)
|
||||||
|
val is_aligned = edge.isHiAligned(bundle.addr_hi, bundle.size)
|
||||||
|
val mask = edge.mask(edge.addr_lo(bundle.mask), bundle.size)
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.Probe) {
|
||||||
|
assert (edge.client.supportsProbe(bundle.source, bundle.size), "'B' channel carries Probe type unsupported by client" + extra)
|
||||||
|
assert (address_ok, "'B' channel Probe carries unmanaged address" + extra)
|
||||||
|
assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'B' channel Probe smaller than a beat" + extra)
|
||||||
|
assert (is_aligned, "'B' channel Probe address not aligned to size" + extra)
|
||||||
|
assert (TLPermissions.isCap(bundle.param), "'B' channel Probe carries invalid cap param" + extra)
|
||||||
|
assert (~bundle.mask === UInt(0).asUInt, "'B' channel Probe contains invalid mask" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.Get) {
|
||||||
|
assert (edge.client.supportsGet(bundle.source, bundle.size), "'B' channel carries Get type unsupported by client" + extra)
|
||||||
|
assert (address_ok, "'B' channel Get carries unmanaged address" + extra)
|
||||||
|
assert (is_aligned, "'B' channel Get address not aligned to size" + extra)
|
||||||
|
assert (bundle.param === UInt(0), "'B' channel Get carries invalid param" + extra)
|
||||||
|
assert (bundle.mask === mask, "'A' channel Get contains invalid mask" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.PutFullData) {
|
||||||
|
assert (edge.client.supportsPutFull(bundle.source, bundle.size), "'B' channel carries PutFull type unsupported by client" + extra)
|
||||||
|
assert (address_ok, "'B' channel PutFull carries unmanaged address" + extra)
|
||||||
|
assert (is_aligned, "'B' channel PutFull address not aligned to size" + extra)
|
||||||
|
assert (bundle.param === UInt(0), "'B' channel PutFull carries invalid param" + extra)
|
||||||
|
assert (bundle.mask === mask, "'B' channel PutFull contains invalid mask" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.PutPartialData) {
|
||||||
|
assert (edge.client.supportsPutPartial(bundle.source, bundle.size), "'B' channel carries PutPartial type unsupported by client" + extra)
|
||||||
|
assert (address_ok, "'B' channel PutPartial carries unmanaged address" + extra)
|
||||||
|
assert (is_aligned, "'B' channel PutPartial address not aligned to size" + extra)
|
||||||
|
assert (bundle.param === UInt(0), "'B' channel PutPartial carries invalid param" + extra)
|
||||||
|
assert ((bundle.mask & ~mask) === UInt(0), "'B' channel PutPartial contains invalid mask" + extra)
|
||||||
|
assert (bundle.mask =/= UInt(0), "'B' channel PutPartial has a zero mask" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.ArithmeticData) {
|
||||||
|
assert (edge.client.supportsArithmetic(bundle.source, bundle.size), "'B' channel carries Arithmetic type unsupported by client" + extra)
|
||||||
|
assert (address_ok, "'B' channel Arithmetic carries unmanaged address" + extra)
|
||||||
|
assert (is_aligned, "'B' channel Arithmetic address not aligned to size" + extra)
|
||||||
|
assert (TLAtomics.isArithmetic(bundle.param), "'B' channel Arithmetic carries invalid opcode param" + extra)
|
||||||
|
assert (bundle.mask === mask, "'B' channel Arithmetic contains invalid mask" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.LogicalData) {
|
||||||
|
assert (edge.client.supportsLogical(bundle.source, bundle.size), "'B' channel carries Logical type unsupported by client" + extra)
|
||||||
|
assert (address_ok, "'B' channel Logical carries unmanaged address" + extra)
|
||||||
|
assert (is_aligned, "'B' channel Logical address not aligned to size" + extra)
|
||||||
|
assert (TLAtomics.isLogical(bundle.param), "'B' channel Logical carries invalid opcode param" + extra)
|
||||||
|
assert (bundle.mask === mask, "'B' channel Logical contains invalid mask" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.Hint) {
|
||||||
|
assert (edge.client.supportsHint(bundle.source), "'B' channel carries Hint type unsupported by client" + extra)
|
||||||
|
assert (address_ok, "'B' channel Hint carries unmanaged address" + extra)
|
||||||
|
assert (is_aligned, "'B' channel Hint address not aligned to size" + extra)
|
||||||
|
assert (bundle.mask === mask, "'B' channel Hint contains invalid mask" + extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def legalizeFormatC(bundle: TLBundleC, edge: TLEdgeOut)(implicit sourceInfo: SourceInfo) = {
|
||||||
|
assert (TLMessages.isC(bundle.opcode), "'C' channel has invalid opcode" + extra)
|
||||||
|
|
||||||
|
val source_ok = edge.client.contains(bundle.source)
|
||||||
|
val is_aligned = edge.isHiAligned(bundle.addr_hi, bundle.size) && edge.isLoAligned(bundle.addr_lo, bundle.size)
|
||||||
|
val address_ok = edge.manager.contains(bundle.source)
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.ProbeAck) {
|
||||||
|
assert (address_ok, "'C' channel ProbeAck carries unmanaged address" + extra)
|
||||||
|
assert (source_ok, "'C' channel ProbeAck carries invalid source ID" + extra)
|
||||||
|
assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'C' channel ProbeAck smaller than a beat" + extra)
|
||||||
|
assert (is_aligned, "'C' channel ProbeAck address not aligned to size" + extra)
|
||||||
|
assert (TLPermissions.isReport(bundle.param), "'C' channel ProbeAck carries invalid report param" + extra)
|
||||||
|
assert (!bundle.error, "'C' channel Probe carries an error" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.ProbeAckData) {
|
||||||
|
assert (address_ok, "'C' channel ProbeAckData carries unmanaged address" + extra)
|
||||||
|
assert (source_ok, "'C' channel ProbeAckData carries invalid source ID" + extra)
|
||||||
|
assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'C' channel ProbeAckData smaller than a beat" + extra)
|
||||||
|
assert (is_aligned, "'C' channel ProbeAckData address not aligned to size" + extra)
|
||||||
|
assert (TLPermissions.isReport(bundle.param), "'C' channel ProbeAckData carries invalid report param" + extra)
|
||||||
|
assert (!bundle.error, "'C' channel ProbeData carries an error" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.Release) {
|
||||||
|
assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'C' channel carries Release type unsupported by manager" + extra)
|
||||||
|
assert (source_ok, "'C' channel Release carries invalid source ID" + extra)
|
||||||
|
assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'C' channel Release smaller than a beat" + extra)
|
||||||
|
assert (is_aligned, "'C' channel Release address not aligned to size" + extra)
|
||||||
|
assert (TLPermissions.isShrink(bundle.param), "'C' channel Release carries invalid shrink param" + extra)
|
||||||
|
assert (!bundle.error, "'C' channel Release carries an error" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.ReleaseData) {
|
||||||
|
assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'C' channel carries ReleaseData type unsupported by manager" + extra)
|
||||||
|
assert (source_ok, "'C' channel ReleaseData carries invalid source ID" + extra)
|
||||||
|
assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'C' channel ReleaseData smaller than a beat" + extra)
|
||||||
|
assert (is_aligned, "'C' channel ReleaseData address not aligned to size" + extra)
|
||||||
|
assert (TLPermissions.isShrink(bundle.param), "'C' channel ReleaseData carries invalid shrink param" + extra)
|
||||||
|
assert (!bundle.error, "'C' channel ReleaseData carries an error" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.AccessAck) {
|
||||||
|
assert (address_ok, "'C' channel AccessAck carries unmanaged address" + extra)
|
||||||
|
assert (source_ok, "'C' channel AccessAck carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'C' channel AccessAck address not aligned to size" + extra)
|
||||||
|
assert (bundle.param === UInt(0), "'C' channel AccessAck carries invalid param" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.AccessAckData) {
|
||||||
|
assert (address_ok, "'C' channel AccessAckData carries unmanaged address" + extra)
|
||||||
|
assert (source_ok, "'C' channel AccessAckData carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'C' channel AccessAckData address not aligned to size" + extra)
|
||||||
|
assert (bundle.param === UInt(0), "'C' channel AccessAckData carries invalid param" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.HintAck) {
|
||||||
|
assert (address_ok, "'C' channel HintAck carries unmanaged address" + extra)
|
||||||
|
assert (source_ok, "'C' channel HintAck carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'C' channel HintAck address not aligned to size" + extra)
|
||||||
|
assert (bundle.param === UInt(0), "'C' channel HintAck carries invalid param" + extra)
|
||||||
|
assert (!bundle.error, "'C' channel HintAck carries an error" + extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def legalizeFormatD(bundle: TLBundleD, edge: TLEdgeIn)(implicit sourceInfo: SourceInfo) = {
|
||||||
|
assert (TLMessages.isD(bundle.opcode), "'D' channel has invalid opcode" + extra)
|
||||||
|
|
||||||
|
val source_ok = edge.client.contains(bundle.source)
|
||||||
|
val is_aligned = edge.isLoAligned(bundle.addr_lo, bundle.size)
|
||||||
|
val sink_ok = edge.manager.containsById(bundle.sink)
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.ReleaseAck) {
|
||||||
|
assert (source_ok, "'D' channel ReleaseAck carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'D' channel ReleaseAck address not aligned to size" + extra)
|
||||||
|
assert (sink_ok, "'D' channel ReleaseAck carries invalid sink ID" + extra)
|
||||||
|
assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'D' channel ReleaseAck smaller than a beat" + extra)
|
||||||
|
assert (bundle.param === UInt(0), "'D' channel ReleaseeAck carries invalid param" + extra)
|
||||||
|
assert (!bundle.error, "'D' channel ReleaseAck carries an error" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.Grant) {
|
||||||
|
assert (source_ok, "'D' channel Grant carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'D' channel Grant address not aligned to size" + extra)
|
||||||
|
assert (sink_ok, "'D' channel Grant carries invalid sink ID" + extra)
|
||||||
|
assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'D' channel Grant smaller than a beat" + extra)
|
||||||
|
assert (TLPermissions.isCap(bundle.param), "'D' channel Grant carries invalid cap param" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.GrantData) {
|
||||||
|
assert (source_ok, "'D' channel GrantData carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'D' channel GrantData address not aligned to size" + extra)
|
||||||
|
assert (sink_ok, "'D' channel GrantData carries invalid sink ID" + extra)
|
||||||
|
assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'D' channel GrantData smaller than a beat" + extra)
|
||||||
|
assert (TLPermissions.isCap(bundle.param), "'D' channel GrantData carries invalid cap param" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.AccessAck) {
|
||||||
|
assert (source_ok, "'D' channel AccessAck carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'D' channel AccessAck address not aligned to size" + extra)
|
||||||
|
assert (sink_ok, "'D' channel AccessAck carries invalid sink ID" + extra)
|
||||||
|
// size is ignored
|
||||||
|
assert (bundle.param === UInt(0), "'D' channel AccessAck carries invalid param" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.AccessAckData) {
|
||||||
|
assert (source_ok, "'D' channel AccessAckData carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'D' channel AccessAckData address not aligned to size" + extra)
|
||||||
|
assert (sink_ok, "'D' channel AccessAckData carries invalid sink ID" + extra)
|
||||||
|
// size is ignored
|
||||||
|
assert (bundle.param === UInt(0), "'D' channel AccessAckData carries invalid param" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (bundle.opcode === TLMessages.HintAck) {
|
||||||
|
assert (source_ok, "'D' channel HintAck carries invalid source ID" + extra)
|
||||||
|
assert (is_aligned, "'D' channel HintAck address not aligned to size" + extra)
|
||||||
|
assert (sink_ok, "'D' channel HintAck carries invalid sink ID" + extra)
|
||||||
|
// size is ignored
|
||||||
|
assert (bundle.param === UInt(0), "'D' channel HintAck carries invalid param" + extra)
|
||||||
|
assert (!bundle.error, "'D' channel HintAck carries an error" + extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def legalizeFormatE(bundle: TLBundleE, edge: TLEdgeOut)(implicit sourceInfo: SourceInfo) = {
|
||||||
|
assert (edge.manager.containsById(bundle.sink), "'E' channels carries invalid sink ID" + extra)
|
||||||
|
}
|
||||||
|
|
||||||
|
def legalizeFormat(bundleOut: TLBundle, edgeOut: TLEdgeOut, bundleIn: TLBundle, edgeIn: TLEdgeIn)(implicit sourceInfo: SourceInfo) = {
|
||||||
|
when (bundleOut.a.valid) { legalizeFormatA(bundleOut.a.bits, edgeOut) }
|
||||||
|
when (bundleIn .b.valid) { legalizeFormatB(bundleIn .b.bits, edgeIn) }
|
||||||
|
when (bundleOut.c.valid) { legalizeFormatC(bundleOut.c.bits, edgeOut) }
|
||||||
|
when (bundleIn .d.valid) { legalizeFormatD(bundleIn .d.bits, edgeIn) }
|
||||||
|
when (bundleOut.e.valid) { legalizeFormatE(bundleOut.e.bits, edgeOut) }
|
||||||
|
}
|
||||||
|
|
||||||
|
def legalizeMultibeatA(a: DecoupledIO[TLBundleA], edge: TLEdgeOut)(implicit sourceInfo: SourceInfo) = {
|
||||||
|
val counter = RegInit(UInt(0, width = log2Up(edge.maxTransfer)))
|
||||||
|
val opcode = Reg(UInt())
|
||||||
|
val param = Reg(UInt())
|
||||||
|
val size = Reg(UInt())
|
||||||
|
val source = Reg(UInt())
|
||||||
|
val addr_hi = Reg(UInt())
|
||||||
|
when (a.valid && counter =/= UInt(0)) {
|
||||||
|
assert (a.bits.opcode === opcode, "'A' channel opcode changed within multibeat operation" + extra)
|
||||||
|
assert (a.bits.param === param, "'A' channel param changed within multibeat operation" + extra)
|
||||||
|
assert (a.bits.size === size, "'A' channel size changed within multibeat operation" + extra)
|
||||||
|
assert (a.bits.source === source, "'A' channel source changed within multibeat operation" + extra)
|
||||||
|
assert (a.bits.addr_hi=== addr_hi,"'A' channel addr_hi changed with multibeat operation" + extra)
|
||||||
|
}
|
||||||
|
when (a.fire()) {
|
||||||
|
counter := counter - UInt(1)
|
||||||
|
when (counter === UInt(0)) {
|
||||||
|
counter := edge.numBeats(a.bits) - UInt(1)
|
||||||
|
opcode := a.bits.opcode
|
||||||
|
param := a.bits.param
|
||||||
|
size := a.bits.size
|
||||||
|
source := a.bits.source
|
||||||
|
addr_hi := a.bits.addr_hi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def legalizeMultibeatB(b: DecoupledIO[TLBundleB], edge: TLEdgeIn)(implicit sourceInfo: SourceInfo) = {
|
||||||
|
val counter = RegInit(UInt(0, width = log2Up(edge.maxTransfer)))
|
||||||
|
val opcode = Reg(UInt())
|
||||||
|
val param = Reg(UInt())
|
||||||
|
val size = Reg(UInt())
|
||||||
|
val source = Reg(UInt())
|
||||||
|
val addr_hi = Reg(UInt())
|
||||||
|
when (b.valid && counter =/= UInt(0)) {
|
||||||
|
assert (b.bits.opcode === opcode, "'B' channel opcode changed within multibeat operation" + extra)
|
||||||
|
assert (b.bits.param === param, "'B' channel param changed within multibeat operation" + extra)
|
||||||
|
assert (b.bits.size === size, "'B' channel size changed within multibeat operation" + extra)
|
||||||
|
assert (b.bits.source === source, "'B' channel source changed within multibeat operation" + extra)
|
||||||
|
assert (b.bits.addr_hi=== addr_hi,"'B' channel addr_hi changed with multibeat operation" + extra)
|
||||||
|
}
|
||||||
|
when (b.fire()) {
|
||||||
|
counter := counter - UInt(1)
|
||||||
|
when (counter === UInt(0)) {
|
||||||
|
counter := edge.numBeats(b.bits) - UInt(1)
|
||||||
|
opcode := b.bits.opcode
|
||||||
|
param := b.bits.param
|
||||||
|
size := b.bits.size
|
||||||
|
source := b.bits.source
|
||||||
|
addr_hi := b.bits.addr_hi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def legalizeMultibeatC(c: DecoupledIO[TLBundleC], edge: TLEdgeOut)(implicit sourceInfo: SourceInfo) = {
|
||||||
|
val counter = RegInit(UInt(0, width = log2Up(edge.maxTransfer)))
|
||||||
|
val opcode = Reg(UInt())
|
||||||
|
val param = Reg(UInt())
|
||||||
|
val size = Reg(UInt())
|
||||||
|
val source = Reg(UInt())
|
||||||
|
val addr_hi = Reg(UInt())
|
||||||
|
val addr_lo = Reg(UInt())
|
||||||
|
when (c.valid && counter =/= UInt(0)) {
|
||||||
|
assert (c.bits.opcode === opcode, "'C' channel opcode changed within multibeat operation" + extra)
|
||||||
|
assert (c.bits.param === param, "'C' channel param changed within multibeat operation" + extra)
|
||||||
|
assert (c.bits.size === size, "'C' channel size changed within multibeat operation" + extra)
|
||||||
|
assert (c.bits.source === source, "'C' channel source changed within multibeat operation" + extra)
|
||||||
|
assert (c.bits.addr_hi=== addr_hi,"'C' channel addr_hi changed with multibeat operation" + extra)
|
||||||
|
assert (c.bits.addr_lo=== addr_lo,"'C' channel addr_lo changed with multibeat operation" + extra)
|
||||||
|
}
|
||||||
|
when (c.fire()) {
|
||||||
|
counter := counter - UInt(1)
|
||||||
|
when (counter === UInt(0)) {
|
||||||
|
counter := edge.numBeats(c.bits) - UInt(1)
|
||||||
|
opcode := c.bits.opcode
|
||||||
|
param := c.bits.param
|
||||||
|
size := c.bits.size
|
||||||
|
source := c.bits.source
|
||||||
|
addr_hi := c.bits.addr_hi
|
||||||
|
addr_lo := c.bits.addr_lo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def legalizeMultibeatD(d: DecoupledIO[TLBundleD], edge: TLEdgeIn)(implicit sourceInfo: SourceInfo) = {
|
||||||
|
val counter = RegInit(UInt(0, width = log2Up(edge.maxTransfer)))
|
||||||
|
val opcode = Reg(UInt())
|
||||||
|
val param = Reg(UInt())
|
||||||
|
val size = Reg(UInt())
|
||||||
|
val source = Reg(UInt())
|
||||||
|
val sink = Reg(UInt())
|
||||||
|
val addr_lo = Reg(UInt())
|
||||||
|
when (d.valid && counter =/= UInt(0)) {
|
||||||
|
assert (d.bits.opcode === opcode, "'D' channel opcode changed within multibeat operation" + extra)
|
||||||
|
assert (d.bits.param === param, "'D' channel param changed within multibeat operation" + extra)
|
||||||
|
assert (d.bits.size === size, "'D' channel size changed within multibeat operation" + extra)
|
||||||
|
assert (d.bits.source === source, "'D' channel source changed within multibeat operation" + extra)
|
||||||
|
assert (d.bits.sink === sink, "'D' channel sink changed with multibeat operation" + extra)
|
||||||
|
assert (d.bits.addr_lo=== addr_lo,"'C' channel addr_lo changed with multibeat operation" + extra)
|
||||||
|
}
|
||||||
|
when (d.fire()) {
|
||||||
|
counter := counter - UInt(1)
|
||||||
|
when (counter === UInt(0)) {
|
||||||
|
counter := edge.numBeats(d.bits) - UInt(1)
|
||||||
|
opcode := d.bits.opcode
|
||||||
|
param := d.bits.param
|
||||||
|
size := d.bits.size
|
||||||
|
source := d.bits.source
|
||||||
|
sink := d.bits.sink
|
||||||
|
addr_lo := d.bits.addr_lo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def legalizeMultibeat(bundleOut: TLBundle, edgeOut: TLEdgeOut, bundleIn: TLBundle, edgeIn: TLEdgeIn)(implicit sourceInfo: SourceInfo) = {
|
||||||
|
legalizeMultibeatA(bundleOut.a, edgeOut)
|
||||||
|
legalizeMultibeatB(bundleOut.b, edgeIn)
|
||||||
|
legalizeMultibeatC(bundleOut.c, edgeOut)
|
||||||
|
legalizeMultibeatD(bundleOut.d, edgeIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
def legalize(bundleOut: TLBundle, edgeOut: TLEdgeOut, bundleIn: TLBundle, edgeIn: TLEdgeIn)(implicit sourceInfo: SourceInfo) = {
|
||||||
|
legalizeFormat (bundleOut, edgeOut, bundleIn, edgeIn)
|
||||||
|
legalizeMultibeat(bundleOut, edgeOut, bundleIn, edgeIn)
|
||||||
|
// !!! validate source uniqueness
|
||||||
|
}
|
||||||
|
}
|
138
src/main/scala/uncore/tilelink2/Narrower.scala
Normal file
138
src/main/scala/uncore/tilelink2/Narrower.scala
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import chisel3.internal.sourceinfo.SourceInfo
|
||||||
|
import scala.math.{min,max}
|
||||||
|
|
||||||
|
// innBeatBytes => the bus width after the adapter
|
||||||
|
class TLNarrower(innerBeatBytes: Int) extends LazyModule
|
||||||
|
{
|
||||||
|
val node = TLAdapterNode(
|
||||||
|
clientFn = { case Seq(c) => c },
|
||||||
|
managerFn = { case Seq(m) => m.copy(beatBytes = innerBeatBytes) })
|
||||||
|
|
||||||
|
lazy val module = new LazyModuleImp(this) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val in = node.bundleIn
|
||||||
|
val out = node.bundleOut
|
||||||
|
}
|
||||||
|
|
||||||
|
val edgeOut = node.edgesOut(0)
|
||||||
|
val edgeIn = node.edgesIn(0)
|
||||||
|
val outerBeatBytes = edgeOut.manager.beatBytes
|
||||||
|
require (outerBeatBytes < innerBeatBytes)
|
||||||
|
|
||||||
|
val ratio = innerBeatBytes / outerBeatBytes
|
||||||
|
val bce = edgeOut.manager.anySupportAcquire && edgeIn.client.anySupportProbe
|
||||||
|
|
||||||
|
def trailingZeros(x: Int) = if (x > 0) Some(log2Ceil(x & -x)) else None
|
||||||
|
|
||||||
|
def split(edge: TLEdge, in: TLDataChannel, fire: Bool): (Bool, UInt, UInt) = {
|
||||||
|
val dataSlices = Vec.tabulate (ratio) { i => edge.data(in)((i+1)*outerBeatBytes*8-1, i*outerBeatBytes*8) }
|
||||||
|
val maskSlices = Vec.tabulate (ratio) { i => edge.mask(in)((i+1)*outerBeatBytes -1, i*outerBeatBytes) }
|
||||||
|
val filter = Reg(UInt(width = ratio), init = SInt(-1, width = ratio).asUInt)
|
||||||
|
val mask = maskSlices.map(_.orR)
|
||||||
|
val hasData = edge.hasData(in)
|
||||||
|
|
||||||
|
// decoded_size = 1111 (for smallest), 0101, 0001 (for largest)
|
||||||
|
val sizeOH1 = UIntToOH1(edge.size(in), log2Ceil(innerBeatBytes)) >> log2Ceil(outerBeatBytes)
|
||||||
|
val decoded_size = Seq.tabulate(ratio) { i => trailingZeros(i).map(!sizeOH1(_)).getOrElse(Bool(true)) }
|
||||||
|
|
||||||
|
val first = filter(ratio-1)
|
||||||
|
val new_filter = Mux(first, Cat(decoded_size.reverse), filter << 1)
|
||||||
|
val last = new_filter(ratio-1) || !hasData
|
||||||
|
when (fire) {
|
||||||
|
filter := new_filter
|
||||||
|
when (!hasData) { filter := SInt(-1, width = ratio).asUInt }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edge.staticHasData(in) == Some(false)) {
|
||||||
|
(Bool(true), UInt(0), UInt(0))
|
||||||
|
} else {
|
||||||
|
val select = Cat(mask.reverse) & new_filter
|
||||||
|
(last, Mux1H(select, dataSlices), Mux1H(select, maskSlices))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def merge(edge: TLEdge, in: TLDataChannel, fire: Bool): (Bool, UInt) = {
|
||||||
|
val count = RegInit(UInt(0, width = log2Ceil(ratio)))
|
||||||
|
val rdata = Reg(UInt(width = (ratio-1)*outerBeatBytes*8))
|
||||||
|
val data = Cat(edge.data(in), rdata)
|
||||||
|
val first = count === UInt(0)
|
||||||
|
val limit = UIntToOH1(edge.size(in), log2Ceil(innerBeatBytes)) >> log2Ceil(outerBeatBytes)
|
||||||
|
val last = count === limit || !edge.hasData(in)
|
||||||
|
|
||||||
|
when (fire) {
|
||||||
|
rdata := data >> outerBeatBytes*8
|
||||||
|
count := count + UInt(1)
|
||||||
|
when (last) { count := UInt(0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val cases = Seq.tabulate(log2Ceil(ratio)+1) { i =>
|
||||||
|
val high = innerBeatBytes*8
|
||||||
|
val take = (1 << i)*outerBeatBytes*8
|
||||||
|
Fill(1 << (log2Ceil(ratio)-i), data(high-1, high-take))
|
||||||
|
}
|
||||||
|
val mux = Vec.tabulate(log2Ceil(edge.maxTransfer)+1) { lgSize =>
|
||||||
|
cases(min(max(lgSize - log2Ceil(outerBeatBytes), 0), log2Ceil(ratio)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edge.staticHasData(in) == Some(false)) {
|
||||||
|
(Bool(true), UInt(0))
|
||||||
|
} else {
|
||||||
|
(last, mux(edge.size(in)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val in = io.in(0)
|
||||||
|
val out = io.out(0)
|
||||||
|
|
||||||
|
val (alast, adata, amask) = split(edgeIn, in.a.bits, out.a.fire())
|
||||||
|
in.a.ready := out.a.ready && alast
|
||||||
|
out.a.valid := in.a.valid
|
||||||
|
out.a.bits := in.a.bits
|
||||||
|
out.a.bits.addr_hi := Cat(in.a.bits.addr_hi, edgeIn.addr_lo(in.a.bits) >> log2Ceil(outerBeatBytes))
|
||||||
|
out.a.bits.data := adata
|
||||||
|
out.a.bits.mask := amask
|
||||||
|
|
||||||
|
val (dlast, ddata) = merge(edgeOut, out.d.bits, out.d.fire())
|
||||||
|
out.d.ready := in.d.ready || !dlast
|
||||||
|
in.d.valid := out.d.valid && dlast
|
||||||
|
in.d.bits := out.d.bits
|
||||||
|
in.d.bits.data := ddata
|
||||||
|
|
||||||
|
if (bce) {
|
||||||
|
require (false)
|
||||||
|
// C has no wmask !!!
|
||||||
|
// val (clast, cdata, cmask) = split(in.c.bits, out.c.fire())
|
||||||
|
// in.c.ready := out.c.ready && clast
|
||||||
|
// out.c.valid := in.c.valid
|
||||||
|
// out.c.bits := in.c.bits
|
||||||
|
// out.c.bits.data := cdata
|
||||||
|
// out.c.bits.mask := cmask
|
||||||
|
|
||||||
|
in.e.ready := out.e.ready
|
||||||
|
out.e.valid := in.e.valid
|
||||||
|
out.e.bits := in.e.bits
|
||||||
|
} else {
|
||||||
|
in.b.valid := Bool(false)
|
||||||
|
in.c.ready := Bool(true)
|
||||||
|
in.e.ready := Bool(true)
|
||||||
|
out.b.ready := Bool(true)
|
||||||
|
out.c.valid := Bool(false)
|
||||||
|
out.e.valid := Bool(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object TLNarrower
|
||||||
|
{
|
||||||
|
// applied to the TL source node; connect (Narrower(x.node, 16) -> y.node)
|
||||||
|
def apply(x: TLBaseNode, innerBeatBytes: Int)(implicit lazyModule: LazyModule, sourceInfo: SourceInfo): TLBaseNode = {
|
||||||
|
val narrower = LazyModule(new TLNarrower(innerBeatBytes))
|
||||||
|
lazyModule.connect(x -> narrower.node)
|
||||||
|
narrower.node
|
||||||
|
}
|
||||||
|
}
|
108
src/main/scala/uncore/tilelink2/Nodes.scala
Normal file
108
src/main/scala/uncore/tilelink2/Nodes.scala
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import scala.collection.mutable.ListBuffer
|
||||||
|
import chisel3.internal.sourceinfo.SourceInfo
|
||||||
|
|
||||||
|
// PI = PortInputParameters
|
||||||
|
// PO = PortOutputParameters
|
||||||
|
// EI = EdgeInput
|
||||||
|
// EO = EdgeOutput
|
||||||
|
abstract class NodeImp[PO, PI, EO, EI, B <: Bundle]
|
||||||
|
{
|
||||||
|
def edgeO(po: PO, pi: PI): EO
|
||||||
|
def edgeI(po: PO, pi: PI): EI
|
||||||
|
def bundleO(eo: Seq[EO]): Vec[B]
|
||||||
|
def bundleI(ei: Seq[EI]): Vec[B]
|
||||||
|
def connect(bo: B, eo: EO, bi: B, ei: EI)(implicit sourceInfo: SourceInfo): Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B])(
|
||||||
|
private val oFn: Option[Seq[PO] => PO],
|
||||||
|
private val iFn: Option[Seq[PI] => PI],
|
||||||
|
private val numPO: Range.Inclusive,
|
||||||
|
private val numPI: Range.Inclusive)
|
||||||
|
{
|
||||||
|
// At least 0 ports must be supported
|
||||||
|
require (!numPO.isEmpty)
|
||||||
|
require (!numPI.isEmpty)
|
||||||
|
require (numPO.start >= 0)
|
||||||
|
require (numPI.start >= 0)
|
||||||
|
|
||||||
|
val noOs = numPO.size == 1 && numPO.contains(0)
|
||||||
|
val noIs = numPI.size == 1 && numPI.contains(0)
|
||||||
|
|
||||||
|
require (noOs || oFn.isDefined)
|
||||||
|
require (noIs || iFn.isDefined)
|
||||||
|
|
||||||
|
private val accPO = ListBuffer[BaseNode[PO, PI, EO, EI, B]]()
|
||||||
|
private val accPI = ListBuffer[BaseNode[PO, PI, EO, EI, B]]()
|
||||||
|
private var oRealized = false
|
||||||
|
private var iRealized = false
|
||||||
|
|
||||||
|
private lazy val oPorts = { oRealized = true; require (numPO.contains(accPO.size)); accPO.result() }
|
||||||
|
private lazy val iPorts = { iRealized = true; require (numPI.contains(accPI.size)); accPI.result() }
|
||||||
|
private lazy val oParams : Option[PO] = oFn.map(_(iPorts.map(_.oParams.get)))
|
||||||
|
private lazy val iParams : Option[PI] = iFn.map(_(oPorts.map(_.iParams.get)))
|
||||||
|
|
||||||
|
lazy val edgesOut = oPorts.map { n => imp.edgeO(oParams.get, n.iParams.get) }
|
||||||
|
lazy val edgesIn = iPorts.map { n => imp.edgeI(n.oParams.get, iParams.get) }
|
||||||
|
|
||||||
|
lazy val bundleOut = imp.bundleO(edgesOut)
|
||||||
|
lazy val bundleIn = imp.bundleI(edgesIn)
|
||||||
|
|
||||||
|
def connectOut = bundleOut
|
||||||
|
def connectIn = bundleIn
|
||||||
|
|
||||||
|
// source.edge(sink)
|
||||||
|
protected[tilelink2] def edge(x: BaseNode[PO, PI, EO, EI, B])(implicit sourceInfo: SourceInfo) = {
|
||||||
|
require (!noOs)
|
||||||
|
require (!oRealized)
|
||||||
|
require (!x.noIs)
|
||||||
|
require (!x.iRealized)
|
||||||
|
val i = x.accPI.size
|
||||||
|
val o = accPO.size
|
||||||
|
accPO += x
|
||||||
|
x.accPI += this
|
||||||
|
() => {
|
||||||
|
imp.connect(connectOut(o), edgesOut(o), x.connectIn(i), x.edgesIn(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IdentityNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B])
|
||||||
|
extends BaseNode(imp)(Some{case Seq(x) => x}, Some{case Seq(x) => x}, 1 to 1, 1 to 1)
|
||||||
|
|
||||||
|
class OutputNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp)
|
||||||
|
{
|
||||||
|
override def connectOut = bundleOut
|
||||||
|
override def connectIn = bundleOut
|
||||||
|
}
|
||||||
|
|
||||||
|
class InputNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp)
|
||||||
|
{
|
||||||
|
override def connectOut = bundleIn
|
||||||
|
override def connectIn = bundleIn
|
||||||
|
}
|
||||||
|
|
||||||
|
class SourceNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B])(po: PO, num: Range.Inclusive = 1 to 1)
|
||||||
|
extends BaseNode(imp)(Some{case Seq() => po}, None, num, 0 to 0)
|
||||||
|
{
|
||||||
|
require (num.end >= 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
class SinkNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B])(pi: PI, num: Range.Inclusive = 1 to 1)
|
||||||
|
extends BaseNode(imp)(None, Some{case Seq() => pi}, 0 to 0, num)
|
||||||
|
{
|
||||||
|
require (num.end >= 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
class InteriorNode[PO, PI, EO, EI, B <: Bundle](imp: NodeImp[PO, PI, EO, EI, B])
|
||||||
|
(oFn: Seq[PO] => PO, iFn: Seq[PI] => PI, numPO: Range.Inclusive, numPI: Range.Inclusive)
|
||||||
|
extends BaseNode(imp)(Some(oFn), Some(iFn), numPO, numPI)
|
||||||
|
{
|
||||||
|
require (numPO.end >= 1)
|
||||||
|
require (numPI.end >= 1)
|
||||||
|
}
|
330
src/main/scala/uncore/tilelink2/Parameters.scala
Normal file
330
src/main/scala/uncore/tilelink2/Parameters.scala
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import scala.math.max
|
||||||
|
|
||||||
|
/** Options for memory regions */
|
||||||
|
object RegionType {
|
||||||
|
sealed trait T
|
||||||
|
case object CACHED extends T
|
||||||
|
case object TRACKED extends T
|
||||||
|
case object UNCACHED extends T
|
||||||
|
case object PUT_EFFECTS extends T
|
||||||
|
case object GET_EFFECTS extends T // GET_EFFECTS => PUT_EFFECTS
|
||||||
|
val cases = Seq(CACHED, TRACKED, UNCACHED, PUT_EFFECTS, GET_EFFECTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A non-empty half-open range; [start, end)
|
||||||
|
case class IdRange(start: Int, end: Int)
|
||||||
|
{
|
||||||
|
require (start >= 0)
|
||||||
|
require (start < end) // not empty
|
||||||
|
|
||||||
|
// This is a strict partial ordering
|
||||||
|
def <(x: IdRange) = end <= x.start
|
||||||
|
def >(x: IdRange) = x < this
|
||||||
|
|
||||||
|
def overlaps(x: IdRange) = start < x.end && x.start < end
|
||||||
|
def contains(x: IdRange) = start <= x.start && x.end <= end
|
||||||
|
// contains => overlaps (because empty is forbidden)
|
||||||
|
|
||||||
|
def contains(x: Int) = start <= x && x < end
|
||||||
|
def contains(x: UInt) =
|
||||||
|
if (start+1 == end) { UInt(start) === x }
|
||||||
|
else if (isPow2(end-start) && ((end | start) & (end-start-1)) == 0)
|
||||||
|
{ ~(~(UInt(start) ^ x) | UInt(end-start-1)) === UInt(0) }
|
||||||
|
else { UInt(start) <= x && x < UInt(end) }
|
||||||
|
|
||||||
|
def shift(x: Int) = IdRange(start+x, end+x)
|
||||||
|
def size = end - start
|
||||||
|
}
|
||||||
|
|
||||||
|
// An potentially empty inclusive range of 2-powers [min, max] (in bytes)
|
||||||
|
case class TransferSizes(min: Int, max: Int)
|
||||||
|
{
|
||||||
|
def this(x: Int) = this(x, x)
|
||||||
|
|
||||||
|
require (min <= max)
|
||||||
|
require (min >= 0 && max >= 0)
|
||||||
|
require (max == 0 || isPow2(max))
|
||||||
|
require (min == 0 || isPow2(min))
|
||||||
|
require (max == 0 || min != 0) // 0 is forbidden unless (0,0)
|
||||||
|
|
||||||
|
def none = min == 0
|
||||||
|
def contains(x: Int) = isPow2(x) && min <= x && x <= max
|
||||||
|
def containsLg(x: Int) = contains(1 << x)
|
||||||
|
def containsLg(x: UInt) =
|
||||||
|
if (none) Bool(false)
|
||||||
|
else if (min == max) { UInt(log2Ceil(min)) === x }
|
||||||
|
else { UInt(log2Ceil(min)) <= x && x <= UInt(log2Ceil(max)) }
|
||||||
|
|
||||||
|
def contains(x: TransferSizes) = x.none || (min <= x.min && x.max <= max)
|
||||||
|
|
||||||
|
def intersect(x: TransferSizes) =
|
||||||
|
if (x.max < min || min < x.max) TransferSizes.none
|
||||||
|
else TransferSizes(scala.math.max(min, x.min), scala.math.min(max, x.max))
|
||||||
|
}
|
||||||
|
|
||||||
|
object TransferSizes {
|
||||||
|
def apply(x: Int) = new TransferSizes(x)
|
||||||
|
val none = new TransferSizes(0)
|
||||||
|
|
||||||
|
implicit def asBool(x: TransferSizes) = !x.none
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressSets specify the address space managed by the manager
|
||||||
|
// Base is the base address, and mask are the bits consumed by the manager
|
||||||
|
// e.g: base=0x200, mask=0xff describes a device managing 0x200-0x2ff
|
||||||
|
// e.g: base=0x1000, mask=0xf0f decribes a device managing 0x1000-0x100f, 0x1100-0x110f, ...
|
||||||
|
case class AddressSet(base: BigInt, mask: BigInt)
|
||||||
|
{
|
||||||
|
// Forbid misaligned base address (and empty sets)
|
||||||
|
require ((base & mask) == 0)
|
||||||
|
|
||||||
|
def contains(x: BigInt) = ~(~(x ^ base) | mask) == 0
|
||||||
|
def contains(x: UInt) = ~(~(x ^ UInt(base)) | UInt(mask)) === UInt(0)
|
||||||
|
|
||||||
|
// overlap iff bitwise: both care (~mask0 & ~mask1) => both equal (base0=base1)
|
||||||
|
def overlaps(x: AddressSet) = (~(mask | x.mask) & (base ^ x.base)) == 0
|
||||||
|
|
||||||
|
// contains iff bitwise: x.mask => mask && contains(x.base)
|
||||||
|
def contains(x: AddressSet) = ((x.mask | (base ^ x.base)) & ~mask) == 0
|
||||||
|
// 1 less than the number of bytes to which the manager should be aligned
|
||||||
|
def alignment1 = ((mask + 1) & ~mask) - 1
|
||||||
|
def max = base | mask
|
||||||
|
|
||||||
|
// A strided slave serves discontiguous ranges
|
||||||
|
def strided = alignment1 != mask
|
||||||
|
}
|
||||||
|
|
||||||
|
case class TLManagerParameters(
|
||||||
|
address: Seq[AddressSet],
|
||||||
|
sinkId: IdRange = IdRange(0, 1),
|
||||||
|
regionType: RegionType.T = RegionType.GET_EFFECTS,
|
||||||
|
// Supports both Acquire+Release+Finish of these sizes
|
||||||
|
supportsAcquire: TransferSizes = TransferSizes.none,
|
||||||
|
supportsArithmetic: TransferSizes = TransferSizes.none,
|
||||||
|
supportsLogical: TransferSizes = TransferSizes.none,
|
||||||
|
supportsGet: TransferSizes = TransferSizes.none,
|
||||||
|
supportsPutFull: TransferSizes = TransferSizes.none,
|
||||||
|
supportsPutPartial: TransferSizes = TransferSizes.none,
|
||||||
|
supportsHint: Boolean = false,
|
||||||
|
// If fifoId=Some, all accesses sent to the same fifoId are executed and ACK'd in FIFO order
|
||||||
|
fifoId: Option[Int] = None)
|
||||||
|
{
|
||||||
|
address.combinations(2).foreach({ case Seq(x,y) =>
|
||||||
|
require (!x.overlaps(y))
|
||||||
|
})
|
||||||
|
address.foreach({ case a =>
|
||||||
|
require (supportsAcquire.none || a.alignment1 >= supportsAcquire.max-1)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Largest support transfer of all types
|
||||||
|
val maxTransfer = List(
|
||||||
|
supportsAcquire.max,
|
||||||
|
supportsArithmetic.max,
|
||||||
|
supportsLogical.max,
|
||||||
|
supportsGet.max,
|
||||||
|
supportsPutFull.max,
|
||||||
|
supportsPutPartial.max).max
|
||||||
|
|
||||||
|
// The device had better not support a transfer larger than it's alignment
|
||||||
|
address.foreach({ case a =>
|
||||||
|
require (a.alignment1 >= maxTransfer-1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes: Int)
|
||||||
|
{
|
||||||
|
require (!managers.isEmpty)
|
||||||
|
require (isPow2(beatBytes))
|
||||||
|
|
||||||
|
// Require disjoint ranges for Ids and addresses
|
||||||
|
managers.combinations(2).foreach({ case Seq(x,y) =>
|
||||||
|
require (!x.sinkId.overlaps(y.sinkId))
|
||||||
|
x.address.foreach({ a => y.address.foreach({ b =>
|
||||||
|
require (!a.overlaps(b))
|
||||||
|
})})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Bounds on required sizes
|
||||||
|
def endSinkId = managers.map(_.sinkId.end).max
|
||||||
|
def maxAddress = managers.map(_.address.map(_.max).max).max
|
||||||
|
def maxTransfer = managers.map(_.maxTransfer).max
|
||||||
|
|
||||||
|
// Operation sizes supported by all outward Managers
|
||||||
|
val allSupportAcquire = managers.map(_.supportsAcquire) .reduce(_ intersect _)
|
||||||
|
val allSupportArithmetic = managers.map(_.supportsArithmetic).reduce(_ intersect _)
|
||||||
|
val allSupportLogical = managers.map(_.supportsLogical) .reduce(_ intersect _)
|
||||||
|
val allSupportGet = managers.map(_.supportsGet) .reduce(_ intersect _)
|
||||||
|
val allSupportPutFull = managers.map(_.supportsPutFull) .reduce(_ intersect _)
|
||||||
|
val allSupportPutPartial = managers.map(_.supportsPutPartial).reduce(_ intersect _)
|
||||||
|
val allSupportHint = managers.map(_.supportsHint) .reduce(_ && _)
|
||||||
|
|
||||||
|
// Operation supported by at least one outward Managers
|
||||||
|
val anySupportAcquire = managers.map(!_.supportsAcquire.none) .reduce(_ || _)
|
||||||
|
val anySupportArithmetic = managers.map(!_.supportsArithmetic.none).reduce(_ || _)
|
||||||
|
val anySupportLogical = managers.map(!_.supportsLogical.none) .reduce(_ || _)
|
||||||
|
val anySupportGet = managers.map(!_.supportsGet.none) .reduce(_ || _)
|
||||||
|
val anySupportPutFull = managers.map(!_.supportsPutFull.none) .reduce(_ || _)
|
||||||
|
val anySupportPutPartial = managers.map(!_.supportsPutPartial.none).reduce(_ || _)
|
||||||
|
val anySupportHint = managers.map( _.supportsHint) .reduce(_ || _)
|
||||||
|
|
||||||
|
// These return Option[TLManagerParameters] for your convenience
|
||||||
|
def find(address: BigInt) = managers.find(_.address.exists(_.contains(address)))
|
||||||
|
def findById(id: Int) = managers.find(_.sinkId.contains(id))
|
||||||
|
|
||||||
|
// Synthesizable lookup methods
|
||||||
|
def find(address: UInt) = Vec(managers.map(_.address.map(_.contains(address)).reduce(_ || _)))
|
||||||
|
def findById(id: UInt) = Vec(managers.map(_.sinkId.contains(id)))
|
||||||
|
|
||||||
|
// !!! need a cheaper version of find, where we assume a valid address match exists
|
||||||
|
|
||||||
|
// Does this Port manage this ID/address?
|
||||||
|
def contains(address: UInt) = find(address).reduce(_ || _)
|
||||||
|
def containsById(id: UInt) = findById(id).reduce(_ || _)
|
||||||
|
|
||||||
|
private def safety_helper(member: TLManagerParameters => TransferSizes)(address: UInt, lgSize: UInt) = {
|
||||||
|
val allSame = managers.map(member(_) == member(managers(0))).reduce(_ && _)
|
||||||
|
if (allSame) member(managers(0)).containsLg(lgSize) else {
|
||||||
|
Mux1H(find(address), managers.map(member(_).containsLg(lgSize)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for support of a given operation at a specific address
|
||||||
|
val supportsAcquire = safety_helper(_.supportsAcquire) _
|
||||||
|
val supportsArithmetic = safety_helper(_.supportsArithmetic) _
|
||||||
|
val supportsLogical = safety_helper(_.supportsLogical) _
|
||||||
|
val supportsGet = safety_helper(_.supportsGet) _
|
||||||
|
val supportsPutFull = safety_helper(_.supportsPutFull) _
|
||||||
|
val supportsPutPartial = safety_helper(_.supportsPutPartial) _
|
||||||
|
def supportsHint(address: UInt) = {
|
||||||
|
if (allSupportHint) Bool(true) else {
|
||||||
|
Mux1H(find(address), managers.map(m => Bool(m.supportsHint)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class TLClientParameters(
|
||||||
|
sourceId: IdRange = IdRange(0,1),
|
||||||
|
// Supports both Probe+Grant of these sizes
|
||||||
|
supportsProbe: TransferSizes = TransferSizes.none,
|
||||||
|
supportsArithmetic: TransferSizes = TransferSizes.none,
|
||||||
|
supportsLogical: TransferSizes = TransferSizes.none,
|
||||||
|
supportsGet: TransferSizes = TransferSizes.none,
|
||||||
|
supportsPutFull: TransferSizes = TransferSizes.none,
|
||||||
|
supportsPutPartial: TransferSizes = TransferSizes.none,
|
||||||
|
supportsHint: Boolean = false)
|
||||||
|
{
|
||||||
|
val maxTransfer = List(
|
||||||
|
supportsProbe.max,
|
||||||
|
supportsArithmetic.max,
|
||||||
|
supportsLogical.max,
|
||||||
|
supportsGet.max,
|
||||||
|
supportsPutFull.max,
|
||||||
|
supportsPutPartial.max).max
|
||||||
|
}
|
||||||
|
|
||||||
|
case class TLClientPortParameters(clients: Seq[TLClientParameters]) {
|
||||||
|
require (!clients.isEmpty)
|
||||||
|
|
||||||
|
// Require disjoint ranges for Ids
|
||||||
|
clients.combinations(2).foreach({ case Seq(x,y) =>
|
||||||
|
require (!x.sourceId.overlaps(y.sourceId))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Bounds on required sizes
|
||||||
|
def endSourceId = clients.map(_.sourceId.end).max
|
||||||
|
def maxTransfer = clients.map(_.maxTransfer).max
|
||||||
|
|
||||||
|
// Operation sizes supported by all inward Clients
|
||||||
|
val allSupportProbe = clients.map(_.supportsProbe) .reduce(_ intersect _)
|
||||||
|
val allSupportArithmetic = clients.map(_.supportsArithmetic).reduce(_ intersect _)
|
||||||
|
val allSupportLogical = clients.map(_.supportsLogical) .reduce(_ intersect _)
|
||||||
|
val allSupportGet = clients.map(_.supportsGet) .reduce(_ intersect _)
|
||||||
|
val allSupportPutFull = clients.map(_.supportsPutFull) .reduce(_ intersect _)
|
||||||
|
val allSupportPutPartial = clients.map(_.supportsPutPartial).reduce(_ intersect _)
|
||||||
|
val allSupportHint = clients.map(_.supportsHint) .reduce(_ && _)
|
||||||
|
|
||||||
|
// Operation is supported by at least one client
|
||||||
|
val anySupportProbe = clients.map(!_.supportsProbe.none) .reduce(_ || _)
|
||||||
|
val anySupportArithmetic = clients.map(!_.supportsArithmetic.none).reduce(_ || _)
|
||||||
|
val anySupportLogical = clients.map(!_.supportsLogical.none) .reduce(_ || _)
|
||||||
|
val anySupportGet = clients.map(!_.supportsGet.none) .reduce(_ || _)
|
||||||
|
val anySupportPutFull = clients.map(!_.supportsPutFull.none) .reduce(_ || _)
|
||||||
|
val anySupportPutPartial = clients.map(!_.supportsPutPartial.none).reduce(_ || _)
|
||||||
|
val anySupportHint = clients.map( _.supportsHint) .reduce(_ || _)
|
||||||
|
|
||||||
|
// These return Option[TLClientParameters] for your convenience
|
||||||
|
def find(id: Int) = clients.find(_.sourceId.contains(id))
|
||||||
|
|
||||||
|
// Synthesizable lookup methods
|
||||||
|
def find(id: UInt) = Vec(clients.map(_.sourceId.contains(id)))
|
||||||
|
def contains(id: UInt) = find(id).reduce(_ || _)
|
||||||
|
|
||||||
|
private def safety_helper(member: TLClientParameters => TransferSizes)(id: UInt, lgSize: UInt) = {
|
||||||
|
val allSame = clients.map(member(_) == member(clients(0))).reduce(_ && _)
|
||||||
|
if (allSame) member(clients(0)).containsLg(lgSize) else {
|
||||||
|
Mux1H(find(id), clients.map(member(_).containsLg(lgSize)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for support of a given operation at a specific id
|
||||||
|
val supportsProbe = safety_helper(_.supportsProbe) _
|
||||||
|
val supportsArithmetic = safety_helper(_.supportsArithmetic) _
|
||||||
|
val supportsLogical = safety_helper(_.supportsLogical) _
|
||||||
|
val supportsGet = safety_helper(_.supportsGet) _
|
||||||
|
val supportsPutFull = safety_helper(_.supportsPutFull) _
|
||||||
|
val supportsPutPartial = safety_helper(_.supportsPutPartial) _
|
||||||
|
def supportsHint(id: UInt) = {
|
||||||
|
if (allSupportHint) Bool(true) else {
|
||||||
|
Mux1H(find(id), clients.map(c => Bool(c.supportsHint)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class TLBundleParameters(
|
||||||
|
addrHiBits: Int,
|
||||||
|
dataBits: Int,
|
||||||
|
sourceBits: Int,
|
||||||
|
sinkBits: Int,
|
||||||
|
sizeBits: Int)
|
||||||
|
{
|
||||||
|
// Chisel has issues with 0-width wires
|
||||||
|
require (addrHiBits >= 1)
|
||||||
|
require (dataBits >= 8)
|
||||||
|
require (sourceBits >= 1)
|
||||||
|
require (sinkBits >= 1)
|
||||||
|
require (sizeBits >= 1)
|
||||||
|
require (isPow2(dataBits))
|
||||||
|
|
||||||
|
val addrLoBits = log2Up(dataBits/8)
|
||||||
|
|
||||||
|
def union(x: TLBundleParameters) =
|
||||||
|
TLBundleParameters(
|
||||||
|
max(addrHiBits, x.addrHiBits),
|
||||||
|
max(dataBits, x.dataBits),
|
||||||
|
max(sourceBits, x.sourceBits),
|
||||||
|
max(sinkBits, x.sinkBits),
|
||||||
|
max(sizeBits, x.sizeBits))
|
||||||
|
}
|
||||||
|
|
||||||
|
case class TLEdgeParameters(
|
||||||
|
client: TLClientPortParameters,
|
||||||
|
manager: TLManagerPortParameters)
|
||||||
|
{
|
||||||
|
val maxTransfer = max(client.maxTransfer, manager.maxTransfer)
|
||||||
|
val maxLgSize = log2Up(maxTransfer)
|
||||||
|
|
||||||
|
// Sanity check the link...
|
||||||
|
require (maxTransfer >= manager.beatBytes)
|
||||||
|
|
||||||
|
val bundle = TLBundleParameters(
|
||||||
|
addrHiBits = log2Up(manager.maxAddress + 1) - log2Up(manager.beatBytes),
|
||||||
|
dataBits = manager.beatBytes * 8,
|
||||||
|
sourceBits = log2Up(client.endSourceId),
|
||||||
|
sinkBits = log2Up(manager.endSinkId),
|
||||||
|
sizeBits = log2Up(maxLgSize+1))
|
||||||
|
}
|
85
src/main/scala/uncore/tilelink2/RegField.scala
Normal file
85
src/main/scala/uncore/tilelink2/RegField.scala
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
|
||||||
|
case class RegReadFn private(combinational: Boolean, fn: (Bool, Bool) => (Bool, Bool, UInt))
|
||||||
|
object RegReadFn
|
||||||
|
{
|
||||||
|
// (ivalid: Bool, oready: Bool) => (iready: Bool, ovalid: Bool, data: UInt)
|
||||||
|
// iready may combinationally depend on oready
|
||||||
|
// all other combinational dependencies forbidden (e.g. ovalid <= ivalid)
|
||||||
|
// iready must eventually go high without requiring ivalid to go high
|
||||||
|
// ovalid must eventually go high without requiring oready to go high
|
||||||
|
// effects must become visible on the cycle after ovalid && oready
|
||||||
|
implicit def apply(x: (Bool, Bool) => (Bool, Bool, UInt)) =
|
||||||
|
new RegReadFn(false, x)
|
||||||
|
// (ready: Bool) => (valid: Bool, data: UInt)
|
||||||
|
// valid must not combinationally depend on ready
|
||||||
|
// valid must eventually go high without requiring ready to go high
|
||||||
|
// => this means that reading cannot trigger creation of the output data
|
||||||
|
// if you need this, use the more general i&o ready-valid method above
|
||||||
|
// effects must become visible on the cycle after valid && ready
|
||||||
|
implicit def apply(x: Bool => (Bool, UInt)) =
|
||||||
|
new RegReadFn(true, { case (_, oready) =>
|
||||||
|
val (ovalid, data) = x(oready)
|
||||||
|
(Bool(true), ovalid, data)
|
||||||
|
})
|
||||||
|
// read from a DecoupledIO (only safe if there is a consistent source of data)
|
||||||
|
implicit def apply(x: DecoupledIO[UInt]):RegReadFn = RegReadFn(ready => { x.ready := ready; (x.valid, x.bits) })
|
||||||
|
// read from a register
|
||||||
|
implicit def apply(x: UInt):RegReadFn = RegReadFn(ready => (Bool(true), x))
|
||||||
|
// noop
|
||||||
|
implicit def apply(x: Unit):RegReadFn = RegReadFn(UInt(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
case class RegWriteFn private(combinational: Boolean, fn: (Bool, Bool, UInt) => (Bool, Bool))
|
||||||
|
object RegWriteFn
|
||||||
|
{
|
||||||
|
// (ivalid: Bool, oready: Bool, data: UInt) => (iready: Bool, ovalid: Bool)
|
||||||
|
// iready may combinationally depend on both oready and data
|
||||||
|
// all other combinational dependencies forbidden (e.g. ovalid <= ivalid)
|
||||||
|
// iready must eventually go high without requiring ivalid to go high
|
||||||
|
// ovalid must eventually go high without requiring oready to go high
|
||||||
|
// effects must become visible on the cycle after ovalid && oready
|
||||||
|
implicit def apply(x: (Bool, Bool, UInt) => (Bool, Bool)) =
|
||||||
|
new RegWriteFn(false, x)
|
||||||
|
// (valid: Bool, data: UInt) => (ready: Bool)
|
||||||
|
// ready may combinationally depend on data (but not valid)
|
||||||
|
// ready must eventually go high without requiring valid to go high
|
||||||
|
// effects must become visible on the cycle after valid && ready
|
||||||
|
implicit def apply(x: (Bool, UInt) => Bool) =
|
||||||
|
// combinational => data valid on oready
|
||||||
|
new RegWriteFn(true, { case (_, oready, data) =>
|
||||||
|
(Bool(true), x(oready, data))
|
||||||
|
})
|
||||||
|
// write to a DecoupledIO (only safe if there is a consistent sink draining data)
|
||||||
|
implicit def apply(x: DecoupledIO[UInt]): RegWriteFn = RegWriteFn((valid, data) => { x.valid := valid; x.bits := data; x.ready })
|
||||||
|
// updates a register
|
||||||
|
implicit def apply(x: UInt): RegWriteFn = RegWriteFn((valid, data) => { when (valid) { x := data }; Bool(true) })
|
||||||
|
// noop
|
||||||
|
implicit def apply(x: Unit): RegWriteFn = RegWriteFn((valid, data) => { Bool(true) })
|
||||||
|
}
|
||||||
|
|
||||||
|
case class RegField(width: Int, read: RegReadFn, write: RegWriteFn)
|
||||||
|
{
|
||||||
|
require (width > 0)
|
||||||
|
def pipelined = !read.combinational || !write.combinational
|
||||||
|
}
|
||||||
|
|
||||||
|
object RegField
|
||||||
|
{
|
||||||
|
type Map = (Int, Seq[RegField])
|
||||||
|
def apply(n: Int) : RegField = apply(n, (), ())
|
||||||
|
def apply(n: Int, rw: UInt) : RegField = apply(n, rw, rw)
|
||||||
|
def R(n: Int, r: RegReadFn) : RegField = apply(n, r, ())
|
||||||
|
def W(n: Int, w: RegWriteFn) : RegField = apply(n, (), w)
|
||||||
|
}
|
||||||
|
|
||||||
|
trait HasRegMap
|
||||||
|
{
|
||||||
|
def regmap(mapping: RegField.Map*): Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
// See GPIO.scala for an example of how to use regmap
|
148
src/main/scala/uncore/tilelink2/RegMapper.scala
Normal file
148
src/main/scala/uncore/tilelink2/RegMapper.scala
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
|
||||||
|
// A bus agnostic register interface to a register-based device
|
||||||
|
|
||||||
|
case class RegMapperParams(indexBits: Int, maskBits: Int, extraBits: Int)
|
||||||
|
|
||||||
|
class RegMapperInput(params: RegMapperParams) extends GenericParameterizedBundle(params)
|
||||||
|
{
|
||||||
|
val read = Bool()
|
||||||
|
val index = UInt(width = params.indexBits)
|
||||||
|
val data = UInt(width = params.maskBits*8)
|
||||||
|
val mask = UInt(width = params.maskBits)
|
||||||
|
val extra = UInt(width = params.extraBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
class RegMapperOutput(params: RegMapperParams) extends GenericParameterizedBundle(params)
|
||||||
|
{
|
||||||
|
val read = Bool()
|
||||||
|
val data = UInt(width = params.maskBits*8)
|
||||||
|
val extra = UInt(width = params.extraBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
require (!regmap.isEmpty)
|
||||||
|
|
||||||
|
// Ensure no register appears twice
|
||||||
|
regmap.combinations(2).foreach { case Seq((reg1, _), (reg2, _)) =>
|
||||||
|
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)
|
||||||
|
|
||||||
|
val endIndex = 1 << log2Ceil(regmap.map(_._1).max+1)
|
||||||
|
val params = RegMapperParams(log2Up(endIndex), bytes, in.bits.params.extraBits)
|
||||||
|
|
||||||
|
val out = Wire(Decoupled(new RegMapperOutput(params)))
|
||||||
|
val front = Wire(Decoupled(new RegMapperInput(params)))
|
||||||
|
front.bits := in.bits
|
||||||
|
|
||||||
|
// Must this device pipeline the control channel?
|
||||||
|
val pipelined = flat.map(_._3.pipelined).reduce(_ || _)
|
||||||
|
val depth = concurrency.getOrElse(if (pipelined) 1 else 0)
|
||||||
|
require (depth >= 0)
|
||||||
|
require (!pipelined || depth > 0)
|
||||||
|
val back = if (depth > 0) Queue(front, depth, pipe = depth == 1) else front
|
||||||
|
|
||||||
|
// Forward declaration of all flow control signals
|
||||||
|
val rivalid = Wire(Vec(flat.size, Bool()))
|
||||||
|
val wivalid = Wire(Vec(flat.size, Bool()))
|
||||||
|
val riready = Wire(Vec(flat.size, Bool()))
|
||||||
|
val wiready = Wire(Vec(flat.size, Bool()))
|
||||||
|
val rovalid = Wire(Vec(flat.size, Bool()))
|
||||||
|
val wovalid = Wire(Vec(flat.size, Bool()))
|
||||||
|
val roready = Wire(Vec(flat.size, Bool()))
|
||||||
|
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)) }
|
||||||
|
|
||||||
|
// The output values for each register
|
||||||
|
val dataOut = Array.tabulate(endIndex) { _ => UInt(0) }
|
||||||
|
|
||||||
|
// Which bits are touched?
|
||||||
|
val frontMask = FillInterleaved(8, front.bits.mask)
|
||||||
|
val backMask = FillInterleaved(8, back .bits.mask)
|
||||||
|
|
||||||
|
// Connect the fields
|
||||||
|
for (i <- 0 until flat.size) {
|
||||||
|
val (reg, low, field) = flat(i)
|
||||||
|
val high = low + field.width - 1
|
||||||
|
// Confirm that no register is too big
|
||||||
|
require (high < 8*bytes)
|
||||||
|
val rimask = frontMask(high, low).orR()
|
||||||
|
val wimask = frontMask(high, low).andR()
|
||||||
|
val romask = backMask(high, low).orR()
|
||||||
|
val womask = backMask(high, low).andR()
|
||||||
|
val data = if (field.write.combinational) back.bits.data else front.bits.data
|
||||||
|
val (f_riready, f_rovalid, f_data) = field.read.fn(rivalid(i) && rimask, roready(i) && romask)
|
||||||
|
val (f_wiready, f_wovalid) = field.write.fn(wivalid(i) && wimask, woready(i) && womask, data(high, low))
|
||||||
|
riready(i) := f_riready || !rimask
|
||||||
|
wiready(i) := f_wiready || !wimask
|
||||||
|
rovalid(i) := f_rovalid || !romask
|
||||||
|
wovalid(i) := f_wovalid || !womask
|
||||||
|
rifire(reg) = riready(i) +: rifire(reg)
|
||||||
|
wifire(reg) = wiready(i) +: wifire(reg)
|
||||||
|
rofire(reg) = rovalid(i) +: rofire(reg)
|
||||||
|
wofire(reg) = wovalid(i) +: wofire(reg)
|
||||||
|
dataOut(reg) = dataOut(reg) | ((f_data << low) & (~UInt(0, width = high+1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the selected register ready?
|
||||||
|
val rifireMux = Vec(rifire.map(_.reduce(_ && _)))
|
||||||
|
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))
|
||||||
|
|
||||||
|
// Connect the pipeline
|
||||||
|
in.ready := front.ready && iready
|
||||||
|
front.valid := in.valid && iready
|
||||||
|
back.ready := out.ready && oready
|
||||||
|
out.valid := back.valid && oready
|
||||||
|
|
||||||
|
// Which register is touched?
|
||||||
|
val frontSel = UIntToOH(front.bits.index)
|
||||||
|
val backSel = UIntToOH(back.bits.index)
|
||||||
|
|
||||||
|
// Include the per-register one-hot selected criteria
|
||||||
|
for (reg <- 0 until endIndex) {
|
||||||
|
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)
|
||||||
|
wofire(reg) = (back.valid && out.ready && !back .bits.read && backSel (reg)) +: wofire(reg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect the field's ivalid and oready
|
||||||
|
for (i <- 0 until flat.size) {
|
||||||
|
val (reg, _, _ ) = flat(i)
|
||||||
|
rivalid(i) := rifire(reg).filter(_ ne riready(i)).reduce(_ && _)
|
||||||
|
wivalid(i) := wifire(reg).filter(_ ne wiready(i)).reduce(_ && _)
|
||||||
|
roready(i) := rofire(reg).filter(_ ne rovalid(i)).reduce(_ && _)
|
||||||
|
woready(i) := wofire(reg).filter(_ ne wovalid(i)).reduce(_ && _)
|
||||||
|
}
|
||||||
|
|
||||||
|
out.bits.read := back.bits.read
|
||||||
|
out.bits.data := Vec(dataOut)(back.bits.index)
|
||||||
|
out.bits.extra := back.bits.extra
|
||||||
|
|
||||||
|
(endIndex, out)
|
||||||
|
}
|
||||||
|
}
|
103
src/main/scala/uncore/tilelink2/RegisterRouter.scala
Normal file
103
src/main/scala/uncore/tilelink2/RegisterRouter.scala
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
|
||||||
|
class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, 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 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 (addrLoEnd, addrLoOff) = (log2Ceil(beatBytes) + sourceEnd, sourceEnd)
|
||||||
|
|
||||||
|
val params = RegMapperParams(log2Up(address.mask+1), beatBytes, addrLoEnd)
|
||||||
|
val in = Wire(Decoupled(new RegMapperInput(params)))
|
||||||
|
in.bits.read := a.bits.opcode === TLMessages.Get
|
||||||
|
in.bits.index := a.bits.addr_hi
|
||||||
|
in.bits.data := a.bits.data
|
||||||
|
in.bits.mask := a.bits.mask
|
||||||
|
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:_*)
|
||||||
|
|
||||||
|
// All registers must fit inside the device address space
|
||||||
|
require (address.mask >= (endIndex-1)*beatBytes)
|
||||||
|
|
||||||
|
// 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 and addr_lo to enable width adapters to work
|
||||||
|
d.bits := edge.AccessAck(
|
||||||
|
fromAddress = out.bits.extra(addrLoEnd-1, addrLoOff),
|
||||||
|
fromSink = UInt(0), // our unique sink id
|
||||||
|
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: AddressSet, concurrency: Option[Int] = None, beatBytes: Int = 4) =
|
||||||
|
new TLRegisterNode(address, concurrency, 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 TLRegisterRouterBase(address: AddressSet, concurrency: Option[Int], beatBytes: Int) extends LazyModule
|
||||||
|
{
|
||||||
|
val node = TLRegisterNode(address, concurrency, beatBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
class TLRegBundle[P](val params: P, val in: Vec[TLBundle]) extends Bundle
|
||||||
|
|
||||||
|
class TLRegModule[P, B <: Bundle](val params: P, bundleBuilder: => B, router: TLRegisterRouterBase)
|
||||||
|
extends LazyModuleImp(router) with HasRegMap
|
||||||
|
{
|
||||||
|
val io = bundleBuilder
|
||||||
|
def regmap(mapping: RegField.Map*) = router.node.regmap(mapping:_*)
|
||||||
|
}
|
||||||
|
|
||||||
|
class TLRegisterRouter[B <: Bundle, M <: LazyModuleImp]
|
||||||
|
(base: BigInt, size: BigInt = 4096, concurrency: Option[Int] = None, beatBytes: Int = 4)
|
||||||
|
(bundleBuilder: Vec[TLBundle] => B)
|
||||||
|
(moduleBuilder: (=> B, TLRegisterRouterBase) => M)
|
||||||
|
extends TLRegisterRouterBase(AddressSet(base, size-1), concurrency, beatBytes)
|
||||||
|
{
|
||||||
|
require (size % 4096 == 0) // devices should be 4K aligned
|
||||||
|
require (isPow2(size))
|
||||||
|
require (size >= 4096)
|
||||||
|
|
||||||
|
lazy val module = moduleBuilder(bundleBuilder(node.bundleIn), this)
|
||||||
|
}
|
74
src/main/scala/uncore/tilelink2/SRAM.scala
Normal file
74
src/main/scala/uncore/tilelink2/SRAM.scala
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
|
||||||
|
class TLRAM(address: AddressSet, beatBytes: Int = 4) extends LazyModule
|
||||||
|
{
|
||||||
|
val node = TLManagerNode(beatBytes, TLManagerParameters(
|
||||||
|
address = List(address),
|
||||||
|
regionType = RegionType.UNCACHED,
|
||||||
|
supportsGet = TransferSizes(1, beatBytes),
|
||||||
|
supportsPutPartial = TransferSizes(1, beatBytes),
|
||||||
|
supportsPutFull = TransferSizes(1, beatBytes),
|
||||||
|
fifoId = Some(0))) // requests are handled in order
|
||||||
|
|
||||||
|
// We require the address range to include an entire beat (for the write mask)
|
||||||
|
require ((address.mask & (beatBytes-1)) == beatBytes-1)
|
||||||
|
|
||||||
|
lazy val module = new LazyModuleImp(this) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val in = node.bundleIn
|
||||||
|
}
|
||||||
|
|
||||||
|
def bigBits(x: BigInt, tail: List[Boolean] = List.empty[Boolean]): List[Boolean] =
|
||||||
|
if (x == 0) tail.reverse else bigBits(x >> 1, ((x & 1) == 1) :: tail)
|
||||||
|
val mask = bigBits(address.mask >> log2Ceil(beatBytes))
|
||||||
|
|
||||||
|
val in = io.in(0)
|
||||||
|
val addrBits = (mask zip in.a.bits.addr_hi.toBools).filter(_._1).map(_._2)
|
||||||
|
val memAddress = Cat(addrBits.reverse)
|
||||||
|
val mem = SeqMem(1 << addrBits.size, Vec(beatBytes, Bits(width = 8)))
|
||||||
|
|
||||||
|
val d_full = RegInit(Bool(false))
|
||||||
|
val d_read = Reg(Bool())
|
||||||
|
val d_size = Reg(UInt())
|
||||||
|
val d_source = Reg(UInt())
|
||||||
|
val d_addr = Reg(UInt())
|
||||||
|
val d_data = Wire(UInt())
|
||||||
|
|
||||||
|
// Flow control
|
||||||
|
when (in.d.fire()) { d_full := Bool(false) }
|
||||||
|
when (in.a.fire()) { d_full := Bool(true) }
|
||||||
|
in.d.valid := d_full
|
||||||
|
in.a.ready := in.d.ready || !d_full
|
||||||
|
|
||||||
|
val edge = node.edgesIn(0)
|
||||||
|
in.d.bits := edge.AccessAck(d_addr, UInt(0), d_source, d_size)
|
||||||
|
// avoid data-bus Mux
|
||||||
|
in.d.bits.data := d_data
|
||||||
|
in.d.bits.opcode := Mux(d_read, TLMessages.AccessAckData, TLMessages.AccessAck)
|
||||||
|
|
||||||
|
val read = in.a.bits.opcode === TLMessages.Get
|
||||||
|
val rdata = Wire(Vec(beatBytes, Bits(width = 8)))
|
||||||
|
val wdata = Vec.tabulate(beatBytes) { i => in.a.bits.data(8*(i+1)-1, 8*i) }
|
||||||
|
d_data := Cat(rdata.reverse)
|
||||||
|
when (in.a.fire()) {
|
||||||
|
d_read := read
|
||||||
|
d_size := in.a.bits.size
|
||||||
|
d_source := in.a.bits.source
|
||||||
|
d_addr := edge.addr_lo(in.a.bits)
|
||||||
|
when (read) {
|
||||||
|
rdata := mem.read(memAddress)
|
||||||
|
} .otherwise {
|
||||||
|
mem.write(memAddress, wdata, in.a.bits.mask.toBools)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tie off unused channels
|
||||||
|
in.b.valid := Bool(false)
|
||||||
|
in.c.ready := Bool(true)
|
||||||
|
in.e.ready := Bool(true)
|
||||||
|
}
|
||||||
|
}
|
44
src/main/scala/uncore/tilelink2/TLNodes.scala
Normal file
44
src/main/scala/uncore/tilelink2/TLNodes.scala
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import scala.collection.mutable.ListBuffer
|
||||||
|
import chisel3.internal.sourceinfo.SourceInfo
|
||||||
|
|
||||||
|
object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle]
|
||||||
|
{
|
||||||
|
def edgeO(po: TLClientPortParameters, pi: TLManagerPortParameters): TLEdgeOut = new TLEdgeOut(po, pi)
|
||||||
|
def edgeI(po: TLClientPortParameters, pi: TLManagerPortParameters): TLEdgeIn = new TLEdgeIn(po, pi)
|
||||||
|
def bundleO(eo: Seq[TLEdgeOut]): Vec[TLBundle] = {
|
||||||
|
require (!eo.isEmpty)
|
||||||
|
Vec(eo.size, TLBundle(eo.map(_.bundle).reduce(_.union(_))))
|
||||||
|
}
|
||||||
|
def bundleI(ei: Seq[TLEdgeIn]): Vec[TLBundle] = {
|
||||||
|
require (!ei.isEmpty)
|
||||||
|
Vec(ei.size, TLBundle(ei.map(_.bundle).reduce(_.union(_)))).flip
|
||||||
|
}
|
||||||
|
|
||||||
|
def connect(bo: TLBundle, eo: TLEdgeOut, bi: TLBundle, ei: TLEdgeIn)(implicit sourceInfo: SourceInfo): Unit = {
|
||||||
|
require (eo.asInstanceOf[TLEdgeParameters] == ei.asInstanceOf[TLEdgeParameters])
|
||||||
|
TLMonitor.legalize(bo, eo, bi, ei)
|
||||||
|
bi <> bo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class TLIdentityNode() extends IdentityNode(TLImp)
|
||||||
|
case class TLOutputNode() extends OutputNode(TLImp)
|
||||||
|
case class TLInputNode() extends InputNode(TLImp)
|
||||||
|
|
||||||
|
case class TLClientNode(params: TLClientParameters, numPorts: Range.Inclusive = 1 to 1)
|
||||||
|
extends SourceNode(TLImp)(TLClientPortParameters(Seq(params)), numPorts)
|
||||||
|
|
||||||
|
case class TLManagerNode(beatBytes: Int, params: TLManagerParameters, numPorts: Range.Inclusive = 1 to 1)
|
||||||
|
extends SinkNode(TLImp)(TLManagerPortParameters(Seq(params), beatBytes), numPorts)
|
||||||
|
|
||||||
|
case class TLAdapterNode(
|
||||||
|
clientFn: Seq[TLClientPortParameters] => TLClientPortParameters,
|
||||||
|
managerFn: Seq[TLManagerPortParameters] => TLManagerPortParameters,
|
||||||
|
numClientPorts: Range.Inclusive = 1 to 1,
|
||||||
|
numManagerPorts: Range.Inclusive = 1 to 1)
|
||||||
|
extends InteriorNode(TLImp)(clientFn, managerFn, numClientPorts, numManagerPorts)
|
218
src/main/scala/uncore/tilelink2/Xbar.scala
Normal file
218
src/main/scala/uncore/tilelink2/Xbar.scala
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package uncore.tilelink2
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
|
||||||
|
object TLXbar
|
||||||
|
{
|
||||||
|
def lowestIndex(requests: Vec[Bool], execute: Bool) = {
|
||||||
|
// lowest-index first is stateless; ignore execute
|
||||||
|
val ors = Vec(requests.scanLeft(Bool(false))(_ || _).init) // prefix-OR
|
||||||
|
Vec((ors zip requests) map { case (o, r) => !o && r })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TLXbar(policy: (Vec[Bool], Bool) => Seq[Bool] = TLXbar.lowestIndex) extends LazyModule
|
||||||
|
{
|
||||||
|
def mapInputIds (ports: Seq[TLClientPortParameters ]) = assignRanges(ports.map(_.endSourceId))
|
||||||
|
def mapOutputIds(ports: Seq[TLManagerPortParameters]) = assignRanges(ports.map(_.endSinkId))
|
||||||
|
|
||||||
|
def assignRanges(sizes: Seq[Int]) = {
|
||||||
|
val pow2Sizes = sizes.map(1 << log2Ceil(_))
|
||||||
|
val tuples = pow2Sizes.zipWithIndex.sortBy(_._1) // record old index, then sort by increasing size
|
||||||
|
val starts = tuples.scanRight(0)(_._1 + _).tail // suffix-sum of the sizes = the start positions
|
||||||
|
val ranges = (tuples zip starts) map { case ((sz, i), st) => (IdRange(st, st+sz), i) }
|
||||||
|
ranges.sortBy(_._2).map(_._1) // Restore orignal order
|
||||||
|
}
|
||||||
|
|
||||||
|
def relabeler() = {
|
||||||
|
var idFactory = 0
|
||||||
|
() => {
|
||||||
|
val fifoMap = scala.collection.mutable.HashMap.empty[Int, Int]
|
||||||
|
(x: Int) => {
|
||||||
|
if (fifoMap.contains(x)) fifoMap(x) else {
|
||||||
|
val out = idFactory
|
||||||
|
idFactory = idFactory + 1
|
||||||
|
fifoMap += (x -> out)
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val node = TLAdapterNode(
|
||||||
|
numClientPorts = 1 to 32,
|
||||||
|
numManagerPorts = 1 to 32,
|
||||||
|
clientFn = { seq =>
|
||||||
|
val clients = (mapInputIds(seq) zip seq) flatMap { case (range, port) =>
|
||||||
|
port.clients map { client => client.copy(
|
||||||
|
sourceId = client.sourceId.shift(range.start)
|
||||||
|
)}
|
||||||
|
}
|
||||||
|
TLClientPortParameters(clients)
|
||||||
|
},
|
||||||
|
managerFn = { seq =>
|
||||||
|
val fifoIdFactory = relabeler()
|
||||||
|
val managers = (mapOutputIds(seq) zip seq) flatMap { case (range, port) =>
|
||||||
|
require (port.beatBytes == seq(0).beatBytes)
|
||||||
|
val fifoIdMapper = fifoIdFactory()
|
||||||
|
port.managers map { manager => manager.copy(
|
||||||
|
sinkId = manager.sinkId.shift(range.start),
|
||||||
|
fifoId = manager.fifoId.map(fifoIdMapper(_))
|
||||||
|
)}
|
||||||
|
}
|
||||||
|
TLManagerPortParameters(managers, seq(0).beatBytes)
|
||||||
|
})
|
||||||
|
|
||||||
|
lazy val module = new LazyModuleImp(this) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val in = node.bundleIn
|
||||||
|
val out = node.bundleOut
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the port ID mapping
|
||||||
|
val inputIdRanges = mapInputIds(node.edgesIn.map(_.client))
|
||||||
|
val outputIdRanges = mapOutputIds(node.edgesOut.map(_.manager))
|
||||||
|
|
||||||
|
// We need an intermediate size of bundle with the widest possible identifiers
|
||||||
|
val wide_bundle = io.in(0).params.union(io.out(0).params)
|
||||||
|
|
||||||
|
// Handle size = 1 gracefully (Chisel3 empty range is broken)
|
||||||
|
def trim(id: UInt, size: Int) = if (size <= 1) UInt(0) else id(log2Ceil(size)-1, 0)
|
||||||
|
def transpose(x: Seq[Seq[Bool]]) = Vec.tabulate(x(0).size) { i => Vec.tabulate(x.size) { j => x(j)(i) } }
|
||||||
|
|
||||||
|
// Transform input bundle sources (sinks use global namespace on both sides)
|
||||||
|
val in = Wire(Vec(io.in.size, TLBundle(wide_bundle)))
|
||||||
|
for (i <- 0 until in.size) {
|
||||||
|
val r = inputIdRanges(i)
|
||||||
|
in(i) <> io.in(i)
|
||||||
|
// prefix sources
|
||||||
|
in(i).a.bits.source := io.in(i).a.bits.source | UInt(r.start)
|
||||||
|
in(i).c.bits.source := io.in(i).c.bits.source | UInt(r.start)
|
||||||
|
// defix sources
|
||||||
|
io.in(i).b.bits.source := trim(in(i).b.bits.source, r.size)
|
||||||
|
io.in(i).d.bits.source := trim(in(i).d.bits.source, r.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform output bundle sinks (sources use global namespace on both sides)
|
||||||
|
val out = Wire(Vec(io.out.size, TLBundle(wide_bundle)))
|
||||||
|
for (i <- 0 until out.size) {
|
||||||
|
val r = outputIdRanges(i)
|
||||||
|
io.out(i) <> out(i)
|
||||||
|
// prefix sinks
|
||||||
|
out(i).d.bits.sink := io.out(i).d.bits.sink | UInt(r.start)
|
||||||
|
// defix sinks
|
||||||
|
io.out(i).e.bits.sink := trim(out(i).e.bits.sink, r.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The crossbar cross-connection state; defined later
|
||||||
|
val grantedAIO = Wire(Vec(in .size, Vec(out.size, Bool())))
|
||||||
|
val grantedBOI = Wire(Vec(out.size, Vec(in .size, Bool())))
|
||||||
|
val grantedCIO = Wire(Vec(in .size, Vec(out.size, Bool())))
|
||||||
|
val grantedDOI = Wire(Vec(out.size, Vec(in .size, Bool())))
|
||||||
|
val grantedEIO = Wire(Vec(in .size, Vec(out.size, Bool())))
|
||||||
|
|
||||||
|
val grantedAOI = transpose(grantedAIO)
|
||||||
|
val grantedBIO = transpose(grantedBOI)
|
||||||
|
val grantedCOI = transpose(grantedCIO)
|
||||||
|
val grantedDIO = transpose(grantedDOI)
|
||||||
|
val grantedEOI = transpose(grantedEIO)
|
||||||
|
|
||||||
|
// Mux1H passes a single-source through unmasked. That's bad for control.
|
||||||
|
def Mux1C(sel: Seq[Bool], ctl: Seq[Bool]) = (sel zip ctl).map{ case (a,b) => a && b }.reduce(_ || _)
|
||||||
|
|
||||||
|
// Mux clients to managers
|
||||||
|
for (o <- 0 until out.size) {
|
||||||
|
out(o).a.valid := Mux1C(grantedAOI(o), in.map(_.a.valid))
|
||||||
|
out(o).a.bits := Mux1H(grantedAOI(o), in.map(_.a.bits))
|
||||||
|
out(o).b.ready := Mux1C(grantedBOI(o), in.map(_.b.ready))
|
||||||
|
out(o).c.valid := Mux1C(grantedCOI(o), in.map(_.c.valid))
|
||||||
|
out(o).c.bits := Mux1H(grantedCOI(o), in.map(_.c.bits))
|
||||||
|
out(o).d.ready := Mux1C(grantedDOI(o), in.map(_.d.ready))
|
||||||
|
out(o).e.valid := Mux1C(grantedEOI(o), in.map(_.e.valid))
|
||||||
|
out(o).e.bits := Mux1H(grantedEOI(o), in.map(_.e.bits))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mux managers to clients
|
||||||
|
for (i <- 0 until in.size) {
|
||||||
|
in(i).a.ready := Mux1C(grantedAIO(i), out.map(_.a.ready))
|
||||||
|
in(i).b.valid := Mux1C(grantedBIO(i), out.map(_.b.valid))
|
||||||
|
in(i).b.bits := Mux1H(grantedBIO(i), out.map(_.b.bits))
|
||||||
|
in(i).c.ready := Mux1C(grantedCIO(i), out.map(_.c.ready))
|
||||||
|
in(i).d.valid := Mux1C(grantedDIO(i), out.map(_.d.valid))
|
||||||
|
in(i).d.bits := Mux1H(grantedDIO(i), out.map(_.d.bits))
|
||||||
|
in(i).e.ready := Mux1C(grantedEIO(i), out.map(_.e.ready))
|
||||||
|
}
|
||||||
|
|
||||||
|
val requestAIO = Vec(in.map { i => Vec(node.edgesOut.map { o => i.a.valid && o.manager.contains(o.address(i.a.bits)) }) })
|
||||||
|
val requestBOI = Vec(out.map { o => Vec(inputIdRanges.map { i => o.b.valid && i .contains(o.b.bits.source) }) })
|
||||||
|
val requestCIO = Vec(in.map { i => Vec(node.edgesOut.map { o => i.c.valid && o.manager.contains(o.address(i.c.bits)) }) })
|
||||||
|
val requestDOI = Vec(out.map { o => Vec(inputIdRanges.map { i => o.d.valid && i .contains(o.d.bits.source) }) })
|
||||||
|
val requestEIO = Vec(in.map { i => Vec(outputIdRanges.map { o => i.e.valid && o .contains(i.e.bits.sink) }) })
|
||||||
|
|
||||||
|
val beatsA = Vec((in zip node.edgesIn) map { case (i, e) => e.numBeats(i.a.bits) })
|
||||||
|
val beatsB = Vec((out zip node.edgesOut) map { case (o, e) => e.numBeats(o.b.bits) })
|
||||||
|
val beatsC = Vec((in zip node.edgesIn) map { case (i, e) => e.numBeats(i.c.bits) })
|
||||||
|
val beatsD = Vec((out zip node.edgesOut) map { case (o, e) => e.numBeats(o.d.bits) })
|
||||||
|
val beatsE = Vec((in zip node.edgesIn) map { case (i, e) => e.numBeats(i.e.bits) })
|
||||||
|
|
||||||
|
// Which pairs support support transfers
|
||||||
|
val maskIO = Vec.tabulate(in.size) { i => Vec.tabulate(out.size) { o =>
|
||||||
|
Bool(node.edgesIn(i).client.anySupportProbe && node.edgesOut(o).manager.anySupportAcquire)
|
||||||
|
} }
|
||||||
|
val maskOI = transpose(maskIO)
|
||||||
|
|
||||||
|
// Mask out BCE channel connections (to be optimized away) for transfer-incapable pairings
|
||||||
|
def mask(a: Seq[Seq[Bool]], b: Seq[Seq[Bool]]) =
|
||||||
|
Vec((a zip b) map { case (x, y) => Vec((x zip y) map { case (a, b) => a && b }) })
|
||||||
|
|
||||||
|
grantedAIO := arbitrate( requestAIO, beatsA, out.map(_.a.fire()))
|
||||||
|
grantedBOI := mask(arbitrate(mask(requestBOI, maskOI), beatsB, in .map(_.b.fire())), maskOI)
|
||||||
|
grantedCIO := mask(arbitrate(mask(requestCIO, maskIO), beatsC, out.map(_.c.fire())), maskIO)
|
||||||
|
grantedDOI := arbitrate( requestDOI, beatsD, in .map(_.d.fire()))
|
||||||
|
grantedEIO := mask(arbitrate(mask(requestEIO, maskIO), beatsE, out.map(_.e.fire())), maskIO)
|
||||||
|
|
||||||
|
def arbitrate(request: Seq[Seq[Bool]], beats: Seq[UInt], progress: Seq[Bool]) = {
|
||||||
|
request foreach { row => require (row.size == progress.size) } // consistent # of resources
|
||||||
|
request foreach { resources => // only one resource is requested
|
||||||
|
val prefixOR = resources.scanLeft(Bool(false))(_ || _).init
|
||||||
|
assert (!(prefixOR zip resources).map{case (a, b) => a && b}.reduce(_ || _))
|
||||||
|
}
|
||||||
|
transpose((transpose(request) zip progress).map { case (r,p) => arbitrate1(r, beats, p) })
|
||||||
|
}
|
||||||
|
|
||||||
|
def arbitrate1(requests: Vec[Bool], beats: Seq[UInt], progress: Bool) = {
|
||||||
|
require (requests.size == beats.size) // consistent # of requesters
|
||||||
|
|
||||||
|
val beatsLeft = RegInit(UInt(0))
|
||||||
|
val idle = beatsLeft === UInt(0)
|
||||||
|
|
||||||
|
// Apply policy to select which requester wins
|
||||||
|
val winners = Vec(policy(requests, idle))
|
||||||
|
|
||||||
|
// Winners must be a subset of requests
|
||||||
|
assert ((winners zip requests).map { case (w,r) => !w || r } .reduce(_ && _))
|
||||||
|
// There must be only one winner
|
||||||
|
val prefixOR = winners.scanLeft(Bool(false))(_ || _).init
|
||||||
|
assert ((prefixOR zip winners).map { case (p,w) => !p || !w }.reduce(_ && _))
|
||||||
|
|
||||||
|
// Supposing we take the winner as input, how many beats must be sent?
|
||||||
|
val maskedBeats = (winners zip beats).map { case (w,b) => Mux(w, b, UInt(0)) }
|
||||||
|
val initBeats = maskedBeats.reduceLeft(_ | _) // no winner => 0 beats
|
||||||
|
// What is the counter state before progress?
|
||||||
|
val todoBeats = Mux(idle, initBeats, beatsLeft)
|
||||||
|
// Apply progress and register the result
|
||||||
|
beatsLeft := todoBeats - progress.asUInt
|
||||||
|
assert (!progress || todoBeats =/= UInt(0)) // underflow should be impossible
|
||||||
|
|
||||||
|
// The previous arbitration state of the resource
|
||||||
|
val state = RegInit(Vec.fill(requests.size)(Bool(false)))
|
||||||
|
// Only take a new value while idle
|
||||||
|
val muxState = Mux(idle, winners, state)
|
||||||
|
state := muxState
|
||||||
|
|
||||||
|
muxState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
src/main/scala/uncore/tilelink2/package.scala
Normal file
10
src/main/scala/uncore/tilelink2/package.scala
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package uncore
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
|
||||||
|
package object tilelink2
|
||||||
|
{
|
||||||
|
type TLBaseNode = BaseNode[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle]
|
||||||
|
def OH1ToUInt(x: UInt) = OHToUInt((x << 1 | UInt(1)) ^ x)
|
||||||
|
def UIntToOH1(x: UInt, width: Int) = ~(SInt(-1, width=width).asUInt << x)(width-1, 0)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user