1
0
rocket-chip/uncore/src/main/scala/tilelink.scala

574 lines
19 KiB
Scala

// See LICENSE for license details.
package uncore
import Chisel._
import scala.math.max
// Parameters exposed to the top-level design, set based on
// external requirements or design space exploration
//
case object TLId extends Field[String] // Unique name per network
case object TLCoherence extends Field[CoherencePolicy]
case object TLAddrBits extends Field[Int]
case object TLManagerXactIdBits extends Field[Int]
case object TLClientXactIdBits extends Field[Int]
case object TLDataBits extends Field[Int]
case object TLDataBeats extends Field[Int]
abstract trait TileLinkParameters extends UsesParameters {
val tlBlockAddrBits = params(TLAddrBits)
val tlClientXactIdBits = params(TLClientXactIdBits)
val tlManagerXactIdBits = params(TLManagerXactIdBits)
val tlDataBits = params(TLDataBits)
val tlDataBeats = params(TLDataBeats)
val tlWriteMaskBits = if(tlDataBits/8 < 1) 1 else tlDataBits/8
val tlBeatAddrBits = log2Up(tlDataBeats)
val tlByteAddrBits = log2Up(tlWriteMaskBits)
val tlAtomicOpcodeBits = M_SZ
val tlUncachedOperandSizeBits = MT_SZ
val tlSubblockUnionBits = max(tlWriteMaskBits,
(tlByteAddrBits +
tlUncachedOperandSizeBits +
tlAtomicOpcodeBits)) + 1
val co = params(TLCoherence)
val networkPreservesPointToPointOrdering = false //TODO: check physical network type
}
abstract class TLBundle extends Bundle with TileLinkParameters
abstract class TLModule extends Module with TileLinkParameters
// Directionality of message channel
// Used to hook up logical network ports to physical network ports
trait TileLinkChannel extends TLBundle
trait ClientToManagerChannel extends TileLinkChannel
trait ManagerToClientChannel extends TileLinkChannel
trait ClientToClientChannel extends TileLinkChannel // Unused for now
// Common signals that are used in multiple channels.
// These traits are useful for type parameterization.
//
trait HasCacheBlockAddress extends TLBundle {
val addr_block = UInt(width = tlBlockAddrBits)
}
trait HasTileLinkBeatId extends TLBundle {
val addr_beat = UInt(width = tlBeatAddrBits)
}
trait HasClientTransactionId extends TLBundle {
val client_xact_id = Bits(width = tlClientXactIdBits)
}
trait HasManagerTransactionId extends TLBundle {
val manager_xact_id = Bits(width = tlManagerXactIdBits)
}
abstract trait HasTileLinkData extends HasTileLinkBeatId {
val data = UInt(width = tlDataBits)
def hasData(dummy: Int = 0): Bool
def hasMultibeatData(dummy: Int = 0): Bool
}
// Actual TileLink channel bundle definitions
class Acquire extends ClientToManagerChannel
with HasCacheBlockAddress
with HasClientTransactionId
with HasTileLinkData {
// Actual bundle fields
val uncached = Bool()
val a_type = UInt(width = max(log2Up(Acquire.nBuiltinAcquireTypes), co.acquireTypeWidth))
val subblock = Bits(width = tlSubblockUnionBits)
// Utility funcs for accessing uncached/subblock union
val opSizeOff = tlByteAddrBits + 1
val opCodeOff = tlUncachedOperandSizeBits + opSizeOff
val opMSB = tlAtomicOpcodeBits + opCodeOff
def allocate(dummy: Int = 0) = subblock(0)
def addr_byte(dummy: Int = 0) = subblock(opSizeOff-1, 1)
def op_size(dummy: Int = 0) = subblock(opCodeOff-1, opSizeOff)
def op_code(dummy: Int = 0) = subblock(opMSB-1, opCodeOff)
def write_mask(dummy: Int = 0) = subblock(tlWriteMaskBits, 1)
def addr(dummy: Int = 0) = Cat(addr_block, addr_beat, this.addr_byte(0))
// Other helper funcs
def is(t: UInt) = a_type === t
def hasData(dummy: Int = 0): Bool = uncached && Acquire.typesWithData.contains(a_type)
def hasMultibeatData(dummy: Int = 0): Bool = Bool(tlDataBeats > 1) && uncached &&
Acquire.typesWithMultibeatData.contains(a_type)
//TODO: This function is a hack to support Rocket icache snooping Rocket nbdcache:
def requiresSelfProbe(dummy: Int = 0) = uncached && Acquire.requiresSelfProbe(a_type)
def makeProbe(meta: ManagerMetadata = co.managerMetadataOnFlush): Probe =
Probe(co.getProbeType(this, meta), this.addr_block)
def makeGrant(
manager_xact_id: UInt,
meta: ManagerMetadata = co.managerMetadataOnFlush,
addr_beat: UInt = UInt(0),
data: UInt = UInt(0)): Grant = {
Grant(
uncached = this.uncached,
g_type = co.getGrantType(this, meta),
client_xact_id = this.client_xact_id,
manager_xact_id = manager_xact_id,
addr_beat = addr_beat,
data = data
)
}
}
object Acquire {
val nBuiltinAcquireTypes = 5
//TODO: Use Enum
def uncachedRead = UInt(0)
def uncachedReadBlock = UInt(1)
def uncachedWrite = UInt(2)
def uncachedWriteBlock = UInt(3)
def uncachedAtomic = UInt(4)
def typesWithData = Vec(uncachedWrite, uncachedWriteBlock, uncachedAtomic)
def typesWithMultibeatData = Vec(uncachedWriteBlock)
def requiresOuterRead(a_type: UInt) = a_type != uncachedWriteBlock
def requiresOuterWrite(a_type: UInt) = typesWithData.contains(a_type)
//TODO: This function is a hack to support Rocket icache snooping Rocket nbdcache:
def requiresSelfProbe(a_type: UInt) = a_type === uncachedReadBlock
def fullWriteMask = SInt(-1, width = new Acquire().tlWriteMaskBits).toUInt
// Most generic constructor
def apply(
uncached: Bool,
a_type: Bits,
client_xact_id: UInt,
addr_block: UInt,
addr_beat: UInt = UInt(0),
data: UInt = UInt(0),
subblock: UInt = UInt(0)): Acquire = {
val acq = new Acquire
acq.uncached := uncached
acq.a_type := a_type
acq.client_xact_id := client_xact_id
acq.addr_block := addr_block
acq.addr_beat := addr_beat
acq.data := data
acq.subblock := subblock
acq
}
// For cached types
def apply(a_type: Bits, client_xact_id: UInt, addr_block: UInt): Acquire = {
apply(
uncached = Bool(false),
a_type = a_type,
client_xact_id = client_xact_id,
addr_block = addr_block)
}
// Copy constructor
def apply(a: Acquire): Acquire = {
val acq = new Acquire
acq := a
acq
}
}
// Asks for a single TileLink beat of data
object UncachedRead {
def apply(
client_xact_id: UInt,
addr_block: UInt,
addr_beat: UInt,
alloc: Bool = Bool(true)): Acquire = {
Acquire(
uncached = Bool(true),
a_type = Acquire.uncachedRead,
client_xact_id = client_xact_id,
addr_block = addr_block,
addr_beat = addr_beat,
subblock = alloc)
}
}
// Asks for an entire cache block of data
object UncachedReadBlock {
def apply(
client_xact_id: UInt = UInt(0),
addr_block: UInt,
alloc: Bool = Bool(true)): Acquire = {
Acquire(
uncached = Bool(true),
a_type = Acquire.uncachedReadBlock,
client_xact_id = client_xact_id,
addr_block = addr_block,
subblock = alloc.toUInt)
}
}
object UncachedWrite {
def apply(
client_xact_id: UInt,
addr_block: UInt,
addr_beat: UInt,
data: UInt,
write_mask: UInt = Acquire.fullWriteMask,
alloc: Bool = Bool(true)): Acquire = {
Acquire(
uncached = Bool(true),
a_type = Acquire.uncachedWrite,
addr_block = addr_block,
addr_beat = addr_beat,
client_xact_id = client_xact_id,
data = data,
subblock = Cat(write_mask, alloc))
}
}
// For full block of data
object UncachedWriteBlock {
def apply(
client_xact_id: UInt,
addr_block: UInt,
addr_beat: UInt,
data: UInt,
alloc: Bool = Bool(true)): Acquire = {
Acquire(
uncached = Bool(true),
a_type = Acquire.uncachedWriteBlock,
client_xact_id = client_xact_id,
addr_block = addr_block,
addr_beat = addr_beat,
data = data,
subblock = Cat(Acquire.fullWriteMask, alloc))
}
}
object UncachedAtomic {
def apply(
client_xact_id: UInt,
addr_block: UInt,
addr_beat: UInt,
addr_byte: UInt,
atomic_opcode: UInt,
operand_size: UInt,
data: UInt): Acquire = {
Acquire(
uncached = Bool(true),
a_type = Acquire.uncachedAtomic,
client_xact_id = client_xact_id,
addr_block = addr_block,
addr_beat = addr_beat,
data = data,
subblock = Cat(atomic_opcode, operand_size, addr_byte, Bool(true)))
}
}
class Probe extends ManagerToClientChannel
with HasCacheBlockAddress {
val p_type = UInt(width = co.probeTypeWidth)
def is(t: UInt) = p_type === t
def makeRelease(
client_xact_id: UInt,
meta: ClientMetadata = co.clientMetadataOnFlush,
addr_beat: UInt = UInt(0),
data: UInt = UInt(0)): Release = {
Release(
voluntary = Bool(false),
r_type = co.getReleaseType(this, meta),
client_xact_id = client_xact_id,
addr_block = this.addr_block,
addr_beat = addr_beat,
data = data)
}
}
object Probe {
val co = new Probe().co
def apply(p_type: UInt, addr_block: UInt) = {
val prb = new Probe
prb.p_type := p_type
prb.addr_block := addr_block
prb
}
def onVoluntaryWriteback(meta: ManagerMetadata, addr_block: UInt): Probe = {
apply(co.getProbeType(M_FLUSH, meta), addr_block)
}
}
class Release extends ClientToManagerChannel
with HasCacheBlockAddress
with HasClientTransactionId
with HasTileLinkData {
val r_type = UInt(width = co.releaseTypeWidth)
val voluntary = Bool()
// Helper funcs
def is(t: UInt) = r_type === t
def hasData(dummy: Int = 0) = co.releaseTypesWithData.contains(r_type)
def hasMultibeatData(dummy: Int = 0) = Bool(tlDataBeats > 1) && co.releaseTypesWithData.contains(r_type)
def isVoluntary(dummy: Int = 0) = voluntary
def requiresAck(dummy: Int = 0) = !Bool(networkPreservesPointToPointOrdering)
def makeGrant(
manager_xact_id: UInt,
meta: ManagerMetadata = co.managerMetadataOnFlush): Grant = {
Grant(
g_type = Grant.voluntaryAck,
uncached = Bool(true), // Grant.voluntaryAck is built-in type
client_xact_id = this.client_xact_id,
manager_xact_id = manager_xact_id
)
}
}
object Release {
val co = new Release().co
def apply(
voluntary: Bool,
r_type: UInt,
client_xact_id: UInt,
addr_block: UInt,
addr_beat: UInt = UInt(0),
data: UInt = UInt(0)): Release = {
val rel = new Release
rel.r_type := r_type
rel.client_xact_id := client_xact_id
rel.addr_block := addr_block
rel.addr_beat := addr_beat
rel.data := data
rel.voluntary := voluntary
rel
}
def makeVoluntaryWriteback(
meta: ClientMetadata,
client_xact_id: UInt,
addr_block: UInt,
addr_beat: UInt = UInt(0),
data: UInt = UInt(0)): Release = {
Release(
voluntary = Bool(true),
r_type = co.getReleaseType(M_FLUSH, meta),
client_xact_id = client_xact_id,
addr_block = addr_block,
addr_beat = addr_beat,
data = data)
}
}
class Grant extends ManagerToClientChannel
with HasTileLinkData
with HasClientTransactionId
with HasManagerTransactionId {
val uncached = Bool()
val g_type = UInt(width = max(log2Up(Grant.nBuiltinGrantTypes), co.grantTypeWidth))
// Helper funcs
def is(t: UInt) = g_type === t
def hasData(dummy: Int = 0): Bool = Mux(uncached,
Grant.typesWithData.contains(g_type),
co.grantTypesWithData.contains(g_type))
def hasMultibeatData(dummy: Int = 0): Bool =
Bool(tlDataBeats > 1) && Mux(uncached,
Grant.typesWithMultibeatData.contains(g_type),
co.grantTypesWithData.contains(g_type))
def isVoluntary(dummy: Int = 0): Bool = uncached && (g_type === Grant.voluntaryAck)
def requiresAck(dummy: Int = 0): Bool = !Bool(networkPreservesPointToPointOrdering) && !isVoluntary()
def makeFinish(dummy: Int = 0): Finish = {
val f = new Finish
f.manager_xact_id := this.manager_xact_id
f
}
}
object Grant {
val nBuiltinGrantTypes = 5
//TODO Use Enum
def voluntaryAck = UInt(0)
def uncachedRead = UInt(1)
def uncachedReadBlock = UInt(2)
def uncachedWrite = UInt(3)
def uncachedAtomic = UInt(4)
def typesWithData = Vec(uncachedRead, uncachedReadBlock, uncachedAtomic)
def typesWithMultibeatData= Vec(uncachedReadBlock)
def apply(
uncached: Bool,
g_type: UInt,
client_xact_id: UInt,
manager_xact_id: UInt,
addr_beat: UInt = UInt(0),
data: UInt = UInt(0)): Grant = {
val gnt = new Grant
gnt.uncached := uncached
gnt.g_type := g_type
gnt.client_xact_id := client_xact_id
gnt.manager_xact_id := manager_xact_id
gnt.addr_beat := addr_beat
gnt.data := data
gnt
}
def getGrantTypeForUncached(a: Acquire): UInt = {
MuxLookup(a.a_type, Grant.uncachedRead, Array(
Acquire.uncachedRead -> Grant.uncachedRead,
Acquire.uncachedReadBlock -> Grant.uncachedReadBlock,
Acquire.uncachedWrite -> Grant.uncachedWrite,
Acquire.uncachedWriteBlock -> Grant.uncachedWrite,
Acquire.uncachedAtomic -> Grant.uncachedAtomic
))
}
}
class Finish extends ClientToManagerChannel with HasManagerTransactionId
// Complete IO definitions for two types of TileLink clients
class UncachedTileLinkIO extends Bundle {
val acquire = new DecoupledIO(new LogicalNetworkIO(new Acquire))
val grant = new DecoupledIO(new LogicalNetworkIO(new Grant)).flip
val finish = new DecoupledIO(new LogicalNetworkIO(new Finish))
}
class TileLinkIO extends UncachedTileLinkIO {
val probe = new DecoupledIO(new LogicalNetworkIO(new Probe)).flip
val release = new DecoupledIO(new LogicalNetworkIO(new Release))
}
// Converts UncachedTileLinkIO to regular TileLinkIO by pinning
// probe.ready and release.valid low
class TileLinkIOWrapper extends TLModule {
val io = new Bundle {
val in = new UncachedTileLinkIO().flip
val out = new TileLinkIO
}
io.out.acquire <> io.in.acquire
io.out.grant <> io.in.grant
io.out.finish <> io.in.finish
io.out.probe.ready := Bool(false)
io.out.release.valid := Bool(false)
}
object TileLinkIOWrapper {
def apply[T <: Data](uncached: UncachedTileLinkIO) = {
val conv = Module(new TileLinkIOWrapper)
conv.io.in <> uncached
conv.io.out
}
}
abstract trait HasArbiterTypes {
val arbN: Int
type ManagerSourcedWithId = ManagerToClientChannel with HasClientTransactionId
type ClientSourcedWithId = ClientToManagerChannel with HasClientTransactionId
type ClientSourcedWithIdAndData = ClientToManagerChannel with
HasClientTransactionId with
HasTileLinkData
}
// Utility functions for constructing TileLinkIO arbiters
abstract class TileLinkArbiterLike(val arbN: Int) extends TLModule
with HasArbiterTypes {
// These are filled in depending on whether the arbiter mucks with the
// client ids and then needs to revert them on the way back
def clientSourcedClientXactId(in: ClientSourcedWithId, id: Int): Bits
def managerSourcedClientXactId(in: ManagerSourcedWithId): Bits
def arbIdx(in: ManagerSourcedWithId): UInt
def hookupClientSource[M <: ClientSourcedWithIdAndData]
(ins: Seq[DecoupledIO[LogicalNetworkIO[M]]],
out: DecoupledIO[LogicalNetworkIO[M]]) {
def hasData(m: LogicalNetworkIO[M]) = m.payload.hasMultibeatData()
val arb = Module(new LockingRRArbiter(out.bits.clone, arbN, params(TLDataBeats), Some(hasData _)))
out <> arb.io.out
ins.zipWithIndex.zip(arb.io.in).map{ case ((req,id), arb) => {
arb.valid := req.valid
arb.bits := req.bits
arb.bits.payload.client_xact_id := clientSourcedClientXactId(req.bits.payload, id)
req.ready := arb.ready
}}
}
def hookupManagerSource[M <: ManagerSourcedWithId]
(ins: Seq[DecoupledIO[LogicalNetworkIO[M]]],
out: DecoupledIO[LogicalNetworkIO[M]]) {
out.ready := Bool(false)
for (i <- 0 until arbN) {
ins(i).valid := Bool(false)
when (arbIdx(out.bits.payload) === UInt(i)) {
ins(i).valid := out.valid
out.ready := ins(i).ready
}
ins(i).bits := out.bits
ins(i).bits.payload.client_xact_id := managerSourcedClientXactId(out.bits.payload)
}
}
}
abstract class UncachedTileLinkIOArbiter(n: Int)
extends TileLinkArbiterLike(n) {
val io = new Bundle {
val in = Vec.fill(n){new UncachedTileLinkIO}.flip
val out = new UncachedTileLinkIO
}
hookupClientSource(io.in.map(_.acquire), io.out.acquire)
hookupManagerSource(io.in.map(_.grant), io.out.grant)
val finish_arb = Module(new RRArbiter(new LogicalNetworkIO(new Finish), n))
io.out.finish <> finish_arb.io.out
finish_arb.io.in zip io.in map { case (arb, req) => arb <> req.finish }
}
abstract class TileLinkIOArbiter(n: Int) extends TileLinkArbiterLike(n) {
val io = new Bundle {
val in = Vec.fill(n){new TileLinkIO}.flip
val out = new TileLinkIO
}
hookupClientSource(io.in.map(_.acquire), io.out.acquire)
hookupClientSource(io.in.map(_.release), io.out.release)
hookupManagerSource(io.in.map(_.grant), io.out.grant)
io.in.map{ _.probe.valid := io.out.probe.valid }
io.in.map{ _.probe.bits := io.out.probe.bits }
io.out.probe.ready := io.in.map(_.probe.ready).reduce(_||_)
val finish_arb = Module(new RRArbiter(new LogicalNetworkIO(new Finish), n))
io.out.finish <> finish_arb.io.out
finish_arb.io.in zip io.in map { case (arb, req) => arb <> req.finish }
}
// Appends the port index of the arbiter to the client_xact_id
abstract trait AppendsArbiterId extends HasArbiterTypes {
def clientSourcedClientXactId(in: ClientSourcedWithId, id: Int) =
Cat(in.client_xact_id, UInt(id, log2Up(arbN)))
def managerSourcedClientXactId(in: ManagerSourcedWithId) =
in.client_xact_id >> UInt(log2Up(arbN))
def arbIdx(in: ManagerSourcedWithId) = in.client_xact_id(log2Up(arbN)-1,0).toUInt
}
// Uses the client_xact_id as is (assumes it has been set to port index)
abstract trait PassesId extends HasArbiterTypes {
def clientSourcedClientXactId(in: ClientSourcedWithId, id: Int) = in.client_xact_id
def managerSourcedClientXactId(in: ManagerSourcedWithId) = in.client_xact_id
def arbIdx(in: ManagerSourcedWithId) = in.client_xact_id
}
// Overwrites some default client_xact_id with the port idx
abstract trait UsesNewId extends HasArbiterTypes {
def clientSourcedClientXactId(in: ClientSourcedWithId, id: Int) = UInt(id, log2Up(arbN))
def managerSourcedClientXactId(in: ManagerSourcedWithId) = UInt(0)
def arbIdx(in: ManagerSourcedWithId) = in.client_xact_id
}
// Mix-in id generation traits to make concrete arbiter classes
class UncachedTileLinkIOArbiterThatAppendsArbiterId(val n: Int) extends UncachedTileLinkIOArbiter(n) with AppendsArbiterId
class UncachedTileLinkIOArbiterThatPassesId(val n: Int) extends UncachedTileLinkIOArbiter(n) with PassesId
class UncachedTileLinkIOArbiterThatUsesNewId(val n: Int) extends UncachedTileLinkIOArbiter(n) with UsesNewId
class TileLinkIOArbiterThatAppendsArbiterId(val n: Int) extends TileLinkIOArbiter(n) with AppendsArbiterId
class TileLinkIOArbiterThatPassesId(val n: Int) extends TileLinkIOArbiter(n) with PassesId
class TileLinkIOArbiterThatUsesNewId(val n: Int) extends TileLinkIOArbiter(n) with UsesNewId