commit
28982ab569
2
chisel3
2
chisel3
@ -1 +1 @@
|
||||
Subproject commit 16426b3a68d85ce7dd9655b0ce773431eb69fc74
|
||||
Subproject commit bb240453abf96b4c2d75ebb2cdc7e3159068431d
|
@ -4,12 +4,14 @@ import Chisel._
|
||||
import junctions._
|
||||
import cde.{Field, Parameters}
|
||||
|
||||
abstract class UnitTest extends Module {
|
||||
trait HasUnitTestIO {
|
||||
val io = new Bundle {
|
||||
val finished = Bool(OUTPUT)
|
||||
val start = Bool(INPUT)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class UnitTest extends Module with HasUnitTestIO {
|
||||
when (io.start) {
|
||||
printf(s"Started UnitTest ${this.getClass.getSimpleName}\n")
|
||||
}
|
||||
@ -37,7 +39,7 @@ class UnitTestSuite(implicit p: Parameters) extends Module {
|
||||
state := Mux(test_idx === UInt(tests.size - 1), s_done, s_start)
|
||||
}
|
||||
|
||||
val timer = Module(new Timer(1000, tests.size))
|
||||
val timer = Module(new Timer(100000, tests.size))
|
||||
timer.io.start.valid := Bool(false)
|
||||
timer.io.stop.valid := Bool(false)
|
||||
|
||||
|
@ -15,24 +15,22 @@ class TLBuffer(entries: Int = 2, pipe: Boolean = false) extends LazyModule
|
||||
val out = node.bundleOut
|
||||
}
|
||||
|
||||
val in = io.in(0)
|
||||
val out = io.out(0)
|
||||
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||
out.a <> Queue(in .a, entries, pipe)
|
||||
in .d <> Queue(out.d, entries, pipe)
|
||||
|
||||
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)
|
||||
if (edgeOut.manager.anySupportAcquire && edgeOut.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,9 @@ class TLEdge(
|
||||
Cat(helper(lgBytes).map(_._1).reverse)
|
||||
}
|
||||
|
||||
def addr_lo(mask: UInt): UInt = {
|
||||
// !!! make sure to align addr_lo for PutPartials with 0 masks
|
||||
def addr_lo(mask: UInt, lgSize: UInt): UInt = {
|
||||
val sizeOH1 = UIntToOH1(lgSize, log2Up(manager.beatBytes))
|
||||
// Almost OHToUInt, but bits set => bits not set
|
||||
def helper(mask: UInt, width: Int): UInt = {
|
||||
if (width <= 1) {
|
||||
@ -61,7 +63,11 @@ class TLEdge(
|
||||
Cat(!lo.orR, helper(hi | lo, mid))
|
||||
}
|
||||
}
|
||||
helper(mask, bundle.dataBits/8)
|
||||
helper(mask, bundle.dataBits/8) & ~sizeOH1
|
||||
}
|
||||
|
||||
def full_mask(imask: UInt, lgSize: UInt): UInt = {
|
||||
mask(addr_lo(imask, lgSize), lgSize)
|
||||
}
|
||||
|
||||
def staticHasData(bundle: TLChannel): Option[Boolean] = {
|
||||
@ -167,13 +173,22 @@ class TLEdge(
|
||||
|
||||
def addr_lo(x: TLDataChannel): UInt = {
|
||||
x match {
|
||||
case a: TLBundleA => addr_lo(a.mask)
|
||||
case b: TLBundleB => addr_lo(b.mask)
|
||||
case a: TLBundleA => addr_lo(a.mask, a.size)
|
||||
case b: TLBundleB => addr_lo(b.mask, b.size)
|
||||
case c: TLBundleC => c.addr_lo
|
||||
case d: TLBundleD => d.addr_lo
|
||||
}
|
||||
}
|
||||
|
||||
def full_mask(x: TLDataChannel): UInt = {
|
||||
x match {
|
||||
case a: TLBundleA => full_mask(a.mask, a.size)
|
||||
case b: TLBundleB => full_mask(b.mask, b.size)
|
||||
case c: TLBundleC => mask(c.addr_lo, c.size)
|
||||
case d: TLBundleD => mask(d.addr_lo, d.size)
|
||||
}
|
||||
}
|
||||
|
||||
def address(x: TLAddrChannel): UInt = {
|
||||
val hi = x match {
|
||||
case a: TLBundleA => a.addr_hi
|
||||
@ -192,7 +207,17 @@ class TLEdge(
|
||||
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)
|
||||
Mux(hasData, decode | small.asUInt, UInt(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def numBeats1(x: TLChannel): UInt = {
|
||||
x match {
|
||||
case _: TLBundleE => UInt(0)
|
||||
case bundle: TLDataChannel => {
|
||||
val decode = UIntToOH1(size(bundle), maxLgSize) >> log2Ceil(manager.beatBytes)
|
||||
Mux(hasData(bundle), decode, UInt(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -353,7 +378,7 @@ class TLEdgeOut(
|
||||
|
||||
def Hint(fromSource: UInt, toAddress: UInt, lgSize: UInt, param: UInt) = {
|
||||
require (manager.anySupportHint)
|
||||
val legal = manager.supportsHint(toAddress)
|
||||
val legal = manager.supportsHint(toAddress, lgSize)
|
||||
val a = Wire(new TLBundleA(bundle))
|
||||
a.opcode := TLMessages.Hint
|
||||
a.param := param
|
||||
@ -546,7 +571,7 @@ class TLEdgeIn(
|
||||
|
||||
def Hint(fromAddress: UInt, toSource: UInt, lgSize: UInt, param: UInt) = {
|
||||
require (client.anySupportHint)
|
||||
val legal = client.supportsHint(toSource)
|
||||
val legal = client.supportsHint(toSource, lgSize)
|
||||
val b = Wire(new TLBundleB(bundle))
|
||||
b.opcode := TLMessages.Hint
|
||||
b.param := param
|
||||
@ -590,7 +615,7 @@ class TLEdgeIn(
|
||||
d
|
||||
}
|
||||
|
||||
def HintAck(a: TLBundleA, sink: UInt = UInt(0)): TLBundleD = HintAck(address(a), sink, a.source, a.size)
|
||||
def HintAck(a: TLBundleA, fromSink: UInt): TLBundleD = HintAck(address(a), fromSink, a.source, a.size)
|
||||
def HintAck(fromAddress: UInt, fromSink: UInt, toSource: UInt, lgSize: UInt) = {
|
||||
val d = Wire(new TLBundleD(bundle))
|
||||
d.opcode := TLMessages.HintAck
|
||||
|
@ -34,7 +34,8 @@ class TLFragmenter(minSize: Int, maxSize: Int, alwaysMin: Boolean = false) exten
|
||||
supportsLogical = expandTransfer(m.supportsLogical),
|
||||
supportsGet = expandTransfer(m.supportsGet),
|
||||
supportsPutFull = expandTransfer(m.supportsPutFull),
|
||||
supportsPutPartial = expandTransfer(m.supportsPutPartial))
|
||||
supportsPutPartial = expandTransfer(m.supportsPutPartial),
|
||||
supportsHint = expandTransfer(m.supportsHint))
|
||||
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:
|
||||
@ -44,7 +45,7 @@ class TLFragmenter(minSize: Int, maxSize: Int, alwaysMin: Boolean = false) exten
|
||||
supportsGet = TransferSizes.none,
|
||||
supportsPutFull = TransferSizes.none,
|
||||
supportsPutPartial = TransferSizes.none,
|
||||
supportsHint = false)
|
||||
supportsHint = TransferSizes.none)
|
||||
|
||||
val node = TLAdapterNode(
|
||||
clientFn = { case Seq(c) => c.copy(clients = c.clients.map(mapClient)) },
|
||||
@ -163,7 +164,7 @@ class TLFragmenter(minSize: Int, maxSize: Int, alwaysMin: Boolean = false) exten
|
||||
}
|
||||
|
||||
// Swallow up non-data ack fragments
|
||||
val drop = (out.d.bits.opcode === TLMessages.AccessAck) && (dFragnum =/= UInt(0))
|
||||
val drop = !dHasData && (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
|
||||
|
230
src/main/scala/uncore/tilelink2/Fuzzer.scala
Normal file
230
src/main/scala/uncore/tilelink2/Fuzzer.scala
Normal file
@ -0,0 +1,230 @@
|
||||
// See LICENSE for license details.
|
||||
package uncore.tilelink2
|
||||
|
||||
import Chisel._
|
||||
import chisel3.util.LFSR16
|
||||
import junctions.unittests._
|
||||
|
||||
class IDMapGenerator(numIds: Int) extends Module {
|
||||
val w = log2Up(numIds)
|
||||
val io = new Bundle {
|
||||
val free = Decoupled(UInt(width = w)).flip
|
||||
val alloc = Decoupled(UInt(width = w))
|
||||
}
|
||||
|
||||
// True indicates that the id is available
|
||||
val bitmap = RegInit(Vec.fill(numIds){Bool(true)})
|
||||
|
||||
io.free.ready := Bool(true)
|
||||
assert(!io.free.valid || !bitmap(io.free.bits)) // No double freeing
|
||||
|
||||
val mask = bitmap.scanLeft(Bool(false))(_||_).init
|
||||
val select = mask zip bitmap map { case(m,b) => !m && b }
|
||||
io.alloc.bits := OHToUInt(select)
|
||||
io.alloc.valid := bitmap.reduce(_||_)
|
||||
|
||||
when (io.alloc.fire()) {
|
||||
bitmap(io.alloc.bits) := Bool(false)
|
||||
}
|
||||
|
||||
when (io.free.fire()) {
|
||||
bitmap(io.free.bits) := Bool(true)
|
||||
}
|
||||
}
|
||||
|
||||
object LFSR64
|
||||
{
|
||||
private var counter = 0
|
||||
private def next: Int = {
|
||||
counter += 1
|
||||
counter
|
||||
}
|
||||
|
||||
def apply(increment: Bool = Bool(true), seed: Int = next): UInt =
|
||||
{
|
||||
val wide = 64
|
||||
val lfsr = RegInit(UInt((seed * 0xDEADBEEFCAFEBAB1L) >>> 1, width = wide))
|
||||
val xor = lfsr(0) ^ lfsr(1) ^ lfsr(3) ^ lfsr(4)
|
||||
when (increment) { lfsr := Cat(xor, lfsr(wide-1,1)) }
|
||||
lfsr
|
||||
}
|
||||
}
|
||||
|
||||
trait HasNoiseMakerIO
|
||||
{
|
||||
val io = new Bundle {
|
||||
val inc = Bool(INPUT)
|
||||
val random = UInt(OUTPUT)
|
||||
}
|
||||
}
|
||||
|
||||
class LFSRNoiseMaker(wide: Int) extends Module with HasNoiseMakerIO
|
||||
{
|
||||
val lfsrs = Seq.fill((wide+63)/64) { LFSR64(io.inc) }
|
||||
io.random := Cat(lfsrs)(wide-1,0)
|
||||
}
|
||||
|
||||
object LFSRNoiseMaker {
|
||||
def apply(wide: Int, increment: Bool = Bool(true)): UInt = {
|
||||
val nm = Module(new LFSRNoiseMaker(wide))
|
||||
nm.io.inc := increment
|
||||
nm.io.random
|
||||
}
|
||||
}
|
||||
|
||||
/** TLFuzzer drives test traffic over TL2 links. It generates a sequence of randomized
|
||||
* requests, and issues legal ones into the DUT. TODO: Currently the fuzzer only generates
|
||||
* memory operations, not permissions transfers.
|
||||
* @param nOperations is the total number of operations that the fuzzer must complete for the test to pass
|
||||
* @param inFlight is the number of operations that can be in-flight to the DUT concurrently
|
||||
* @param noiseMaker is a function that supplies a random UInt of a given width every time inc is true
|
||||
*/
|
||||
class TLFuzzer(
|
||||
nOperations: Int,
|
||||
inFlight: Int = 32,
|
||||
noiseMaker: (Int, Bool) => UInt = LFSRNoiseMaker.apply _) extends LazyModule
|
||||
{
|
||||
val node = TLClientNode(TLClientParameters(sourceId = IdRange(0,inFlight)))
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
val out = node.bundleOut
|
||||
val finished = Bool()
|
||||
}
|
||||
|
||||
val out = io.out(0)
|
||||
val edge = node.edgesOut(0)
|
||||
|
||||
// Extract useful parameters from the TL edge
|
||||
val endAddress = edge.manager.maxAddress + 1
|
||||
val maxTransfer = edge.manager.maxTransfer
|
||||
val beatBytes = edge.manager.beatBytes
|
||||
val maxLgBeats = log2Up(maxTransfer/beatBytes)
|
||||
val addressBits = log2Up(endAddress)
|
||||
val sizeBits = edge.bundle.sizeBits
|
||||
val dataBits = edge.bundle.dataBits
|
||||
|
||||
// Progress through operations
|
||||
val num_reqs = Reg(init = UInt(nOperations-1, log2Up(nOperations)))
|
||||
val num_resps = Reg(init = UInt(nOperations-1, log2Up(nOperations)))
|
||||
io.finished := num_resps === UInt(0)
|
||||
|
||||
// Progress within each operation
|
||||
val a = out.a.bits
|
||||
val a_beats1 = edge.numBeats1(a)
|
||||
val a_counter = RegInit(UInt(0, width = maxLgBeats))
|
||||
val a_counter1 = a_counter - UInt(1)
|
||||
val a_first = a_counter === UInt(0)
|
||||
val a_last = a_counter === UInt(1) || a_beats1 === UInt(0)
|
||||
val req_done = out.a.fire() && a_last
|
||||
|
||||
val d = out.d.bits
|
||||
val d_beats1 = edge.numBeats1(d)
|
||||
val d_counter = RegInit(UInt(0, width = maxLgBeats))
|
||||
val d_counter1 = d_counter - UInt(1)
|
||||
val d_first = d_counter === UInt(0)
|
||||
val d_last = d_counter === UInt(1) || d_beats1 === UInt(0)
|
||||
val resp_done = out.d.fire() && d_last
|
||||
|
||||
// Source ID generation
|
||||
val idMap = Module(new IDMapGenerator(inFlight))
|
||||
val alloc = Queue.irrevocable(idMap.io.alloc, 1, pipe = true)
|
||||
val src = alloc.bits
|
||||
alloc.ready := req_done
|
||||
idMap.io.free.valid := resp_done
|
||||
idMap.io.free.bits := out.d.bits.source
|
||||
|
||||
// Increment random number generation for the following subfields
|
||||
val inc = Wire(Bool())
|
||||
val inc_beat = Wire(Bool())
|
||||
val arth_op = noiseMaker(3, inc)
|
||||
val log_op = noiseMaker(2, inc)
|
||||
val amo_size = UInt(2) + noiseMaker(1, inc) // word or dword
|
||||
val size = noiseMaker(sizeBits, inc)
|
||||
val addr = noiseMaker(addressBits, inc) & ~UIntToOH1(size, addressBits)
|
||||
val mask = noiseMaker(beatBytes, inc_beat) & edge.mask(addr, size)
|
||||
val data = noiseMaker(dataBits, inc_beat)
|
||||
|
||||
// Actually generate specific TL messages when it is legal to do so
|
||||
val (glegal, gbits) = edge.Get(src, addr, size)
|
||||
val (pflegal, pfbits) = if(edge.manager.anySupportPutFull) {
|
||||
edge.Put(src, addr, size, data)
|
||||
} else { (glegal, gbits) }
|
||||
val (pplegal, ppbits) = if(edge.manager.anySupportPutPartial) {
|
||||
edge.Put(src, addr, size, data, mask)
|
||||
} else { (glegal, gbits) }
|
||||
val (alegal, abits) = if(edge.manager.anySupportArithmetic) {
|
||||
edge.Arithmetic(src, addr, size, data, arth_op)
|
||||
} else { (glegal, gbits) }
|
||||
val (llegal, lbits) = if(edge.manager.anySupportLogical) {
|
||||
edge.Logical(src, addr, size, data, log_op)
|
||||
} else { (glegal, gbits) }
|
||||
val (hlegal, hbits) = if(edge.manager.anySupportHint) {
|
||||
edge.Hint(src, addr, size, UInt(0))
|
||||
} else { (glegal, gbits) }
|
||||
|
||||
// Pick a specific message to try to send
|
||||
val a_type_sel = noiseMaker(3, inc)
|
||||
|
||||
val legal = MuxLookup(a_type_sel, glegal, Seq(
|
||||
UInt("b000") -> glegal,
|
||||
UInt("b001") -> pflegal,
|
||||
UInt("b010") -> pplegal,
|
||||
UInt("b011") -> alegal,
|
||||
UInt("b100") -> llegal,
|
||||
UInt("b101") -> hlegal))
|
||||
|
||||
val bits = MuxLookup(a_type_sel, gbits, Seq(
|
||||
UInt("b000") -> gbits,
|
||||
UInt("b001") -> pfbits,
|
||||
UInt("b010") -> ppbits,
|
||||
UInt("b011") -> abits,
|
||||
UInt("b100") -> lbits,
|
||||
UInt("b101") -> hbits))
|
||||
|
||||
// Wire both the used and un-used channel signals
|
||||
out.a.valid := legal && alloc.valid && num_reqs =/= UInt(0)
|
||||
out.a.bits := bits
|
||||
out.b.ready := Bool(true)
|
||||
out.c.valid := Bool(false)
|
||||
out.d.ready := Bool(true)
|
||||
out.e.valid := Bool(false)
|
||||
|
||||
// Increment the various progress-tracking states
|
||||
inc := !legal || req_done
|
||||
inc_beat := !legal || out.a.fire()
|
||||
|
||||
when (out.a.fire()) {
|
||||
a_counter := Mux(a_first, a_beats1, a_counter1)
|
||||
when(a_last) { num_reqs := num_reqs - UInt(1) }
|
||||
}
|
||||
|
||||
when (out.d.fire()) {
|
||||
d_counter := Mux(d_first, d_beats1, d_counter1)
|
||||
when(d_last) { num_resps := num_resps - UInt(1) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TLFuzzRAM extends LazyModule
|
||||
{
|
||||
val model = LazyModule(new TLRAMModel)
|
||||
val ram = LazyModule(new TLRAM(AddressSet(0, 0x3ff)))
|
||||
val gpio = LazyModule(new RRTest1(0x400))
|
||||
val xbar = LazyModule(new TLXbar)
|
||||
val fuzz = LazyModule(new TLFuzzer(5000))
|
||||
|
||||
model.node := fuzz.node
|
||||
xbar.node := TLWidthWidget(TLHintHandler(model.node), 16)
|
||||
ram.node := TLFragmenter(TLBuffer(xbar.node), 4, 256)
|
||||
gpio.node := TLFragmenter(TLBuffer(xbar.node), 4, 32)
|
||||
|
||||
lazy val module = new LazyModuleImp(this) with HasUnitTestIO {
|
||||
io.finished := fuzz.module.io.finished
|
||||
}
|
||||
}
|
||||
|
||||
class TLFuzzRAMTest extends UnitTest {
|
||||
val dut = Module(LazyModule(new TLFuzzRAM).module)
|
||||
io.finished := dut.io.finished
|
||||
}
|
@ -9,8 +9,8 @@ import chisel3.internal.sourceinfo.SourceInfo
|
||||
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 })
|
||||
clientFn = { case Seq(c) => if (!supportClients) c else c.copy(clients = c.clients .map(_.copy(supportsHint = TransferSizes(1, c.maxTransfer)))) },
|
||||
managerFn = { case Seq(m) => if (!supportManagers) m else m.copy(managers = m.managers.map(_.copy(supportsHint = TransferSizes(1, m.maxTransfer)))) })
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
@ -27,16 +27,26 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f
|
||||
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)
|
||||
// Does it even make sense to add the HintHandler?
|
||||
val smartClients = edgeIn.client.clients.map(_.supportsHint.max == edgeIn.client.maxTransfer).reduce(_&&_)
|
||||
val smartManagers = edgeOut.manager.managers.map(_.supportsHint.max == edgeOut.manager.maxTransfer).reduce(_&&_)
|
||||
|
||||
if (supportManagers && !smartManagers) {
|
||||
val address = edgeIn.address(in.a.bits)
|
||||
val handleA = if (passthrough) !edgeOut.manager.supportsHint(address, edgeIn.size(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))
|
||||
// Prioritize existing D traffic over HintAck (and finish multibeat xfers)
|
||||
val beats1 = edgeOut.numBeats1(out.d.bits)
|
||||
val counter = RegInit(UInt(0, width = log2Up(edgeOut.manager.maxTransfer/edgeOut.manager.beatBytes)))
|
||||
val first = counter === UInt(0)
|
||||
when (out.d.fire()) { counter := Mux(first, beats1, counter - UInt(1)) }
|
||||
|
||||
in.a.ready := Mux(bypassD, in.d.ready && !out.d.valid, out.a.ready)
|
||||
in.d.valid := out.d.valid || (bypassD && in.a.valid && first)
|
||||
out.d.ready := in.d.ready
|
||||
in.d.bits := Mux(out.d.valid, out.d.bits, edgeIn.HintAck(in.a.bits, edgeOut.manager.findId(address)))
|
||||
|
||||
in.a.ready := Mux(bypassD, in.d.ready && first && !out.d.valid, out.a.ready)
|
||||
out.a.valid := in.a.valid && !bypassD
|
||||
out.a.bits := in.a.bits
|
||||
} else {
|
||||
@ -49,16 +59,21 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f
|
||||
in.d.bits := out.d.bits
|
||||
}
|
||||
|
||||
if (supportClients) {
|
||||
val handleB = if (passthrough) !edgeIn.client.supportsHint(out.b.bits.source) else Bool(true)
|
||||
if (supportClients && !smartClients) {
|
||||
val handleB = if (passthrough) !edgeIn.client.supportsHint(out.b.bits.source, edgeOut.size(out.b.bits)) 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)
|
||||
// Prioritize existing C traffic over HintAck (and finish multibeat xfers)
|
||||
val beats1 = edgeIn.numBeats1(in.c.bits)
|
||||
val counter = RegInit(UInt(0, width = log2Up(edgeIn.client.maxTransfer/edgeIn.manager.beatBytes)))
|
||||
val first = counter === UInt(0)
|
||||
when (in.c.fire()) { counter := Mux(first, beats1, counter - UInt(1)) }
|
||||
|
||||
out.c.valid := in.c.valid || (bypassC && in.b.valid && first)
|
||||
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)
|
||||
out.b.ready := Mux(bypassC, out.c.ready && first && !in.c.valid, in.b.ready)
|
||||
in.b.valid := out.b.valid && !bypassC
|
||||
in.b.bits := out.b.bits
|
||||
} else if (bce) {
|
||||
|
@ -20,7 +20,7 @@ object TLMonitor
|
||||
// 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)
|
||||
val mask = edge.full_mask(bundle)
|
||||
|
||||
when (bundle.opcode === TLMessages.Acquire) {
|
||||
assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'A' channel carries Acquire type unsupported by manager" + extra)
|
||||
@ -53,7 +53,6 @@ object TLMonitor
|
||||
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) {
|
||||
@ -73,7 +72,7 @@ object TLMonitor
|
||||
}
|
||||
|
||||
when (bundle.opcode === TLMessages.Hint) {
|
||||
assert (edge.manager.supportsHint(edge.address(bundle)), "'A' channel carries Hint type unsupported by manager" + extra)
|
||||
assert (edge.manager.supportsHint(edge.address(bundle), bundle.size), "'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)
|
||||
@ -86,7 +85,7 @@ object TLMonitor
|
||||
// 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)
|
||||
val mask = edge.full_mask(bundle)
|
||||
|
||||
when (bundle.opcode === TLMessages.Probe) {
|
||||
assert (edge.client.supportsProbe(bundle.source, bundle.size), "'B' channel carries Probe type unsupported by client" + extra)
|
||||
@ -119,7 +118,6 @@ object TLMonitor
|
||||
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) {
|
||||
@ -139,7 +137,7 @@ object TLMonitor
|
||||
}
|
||||
|
||||
when (bundle.opcode === TLMessages.Hint) {
|
||||
assert (edge.client.supportsHint(bundle.source), "'B' channel carries Hint type unsupported by client" + extra)
|
||||
assert (edge.client.supportsHint(bundle.source, bundle.size), "'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)
|
||||
|
@ -110,16 +110,14 @@ case class TLManagerParameters(
|
||||
supportsGet: TransferSizes = TransferSizes.none,
|
||||
supportsPutFull: TransferSizes = TransferSizes.none,
|
||||
supportsPutPartial: TransferSizes = TransferSizes.none,
|
||||
supportsHint: Boolean = false,
|
||||
supportsHint: TransferSizes = TransferSizes.none,
|
||||
// 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)
|
||||
})
|
||||
require (supportsPutFull.contains(supportsPutPartial))
|
||||
|
||||
// Largest support transfer of all types
|
||||
val maxTransfer = List(
|
||||
@ -161,7 +159,7 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes
|
||||
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(_ && _)
|
||||
val allSupportHint = managers.map(_.supportsHint) .reduce(_ intersect _)
|
||||
|
||||
// Operation supported by at least one outward Managers
|
||||
val anySupportAcquire = managers.map(!_.supportsAcquire.none) .reduce(_ || _)
|
||||
@ -170,7 +168,7 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes
|
||||
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(_ || _)
|
||||
val anySupportHint = managers.map(!_.supportsHint.none) .reduce(_ || _)
|
||||
|
||||
// These return Option[TLManagerParameters] for your convenience
|
||||
def find(address: BigInt) = managers.find(_.address.exists(_.contains(address)))
|
||||
@ -179,6 +177,7 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes
|
||||
// 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)))
|
||||
def findId(address: UInt) = Mux1H(find(address), managers.map(m => UInt(m.sinkId.start)))
|
||||
|
||||
// !!! need a cheaper version of find, where we assume a valid address match exists
|
||||
|
||||
@ -200,11 +199,7 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes
|
||||
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)))
|
||||
}
|
||||
}
|
||||
val supportsHint = safety_helper(_.supportsHint) _
|
||||
}
|
||||
|
||||
case class TLClientParameters(
|
||||
@ -216,8 +211,10 @@ case class TLClientParameters(
|
||||
supportsGet: TransferSizes = TransferSizes.none,
|
||||
supportsPutFull: TransferSizes = TransferSizes.none,
|
||||
supportsPutPartial: TransferSizes = TransferSizes.none,
|
||||
supportsHint: Boolean = false)
|
||||
supportsHint: TransferSizes = TransferSizes.none)
|
||||
{
|
||||
require (supportsPutFull.contains(supportsPutPartial))
|
||||
|
||||
val maxTransfer = List(
|
||||
supportsProbe.max,
|
||||
supportsArithmetic.max,
|
||||
@ -246,7 +243,7 @@ case class TLClientPortParameters(clients: Seq[TLClientParameters]) {
|
||||
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(_ && _)
|
||||
val allSupportHint = clients.map(_.supportsHint) .reduce(_ intersect _)
|
||||
|
||||
// Operation is supported by at least one client
|
||||
val anySupportProbe = clients.map(!_.supportsProbe.none) .reduce(_ || _)
|
||||
@ -255,7 +252,7 @@ case class TLClientPortParameters(clients: Seq[TLClientParameters]) {
|
||||
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(_ || _)
|
||||
val anySupportHint = clients.map(!_.supportsHint.none) .reduce(_ || _)
|
||||
|
||||
// These return Option[TLClientParameters] for your convenience
|
||||
def find(id: Int) = clients.find(_.sourceId.contains(id))
|
||||
@ -278,11 +275,7 @@ case class TLClientPortParameters(clients: Seq[TLClientParameters]) {
|
||||
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)))
|
||||
}
|
||||
}
|
||||
val supportsHint = safety_helper(_.supportsHint) _
|
||||
}
|
||||
|
||||
case class TLBundleParameters(
|
||||
@ -322,7 +315,7 @@ case class TLEdgeParameters(
|
||||
require (maxTransfer >= manager.beatBytes)
|
||||
|
||||
val bundle = TLBundleParameters(
|
||||
addrHiBits = log2Up(manager.maxAddress + 1) - log2Up(manager.beatBytes),
|
||||
addrHiBits = log2Up(manager.maxAddress + 1) - log2Ceil(manager.beatBytes),
|
||||
dataBits = manager.beatBytes * 8,
|
||||
sourceBits = log2Up(client.endSourceId),
|
||||
sinkBits = log2Up(manager.endSinkId),
|
||||
|
249
src/main/scala/uncore/tilelink2/RAMModel.scala
Normal file
249
src/main/scala/uncore/tilelink2/RAMModel.scala
Normal file
@ -0,0 +1,249 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package uncore.tilelink2
|
||||
|
||||
import Chisel._
|
||||
import chisel3.util.LFSR16
|
||||
|
||||
class TLRAMModel extends LazyModule
|
||||
{
|
||||
val node = TLIdentityNode()
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
val in = node.bundleIn
|
||||
val out = node.bundleOut
|
||||
}
|
||||
|
||||
// !!! support multiple clients via clock division
|
||||
require (io.out.size == 1)
|
||||
|
||||
val in = io.in(0)
|
||||
val out = io.out(0)
|
||||
|
||||
val edge = node.edgesIn(0)
|
||||
val endAddress = edge.manager.maxAddress + 1
|
||||
val endSourceId = edge.client.endSourceId
|
||||
val maxTransfer = edge.manager.maxTransfer
|
||||
val beatBytes = edge.manager.beatBytes
|
||||
val endAddressHi = (endAddress / beatBytes).intValue
|
||||
val maxLgBeats = log2Up(maxTransfer/beatBytes)
|
||||
val shift = log2Ceil(beatBytes)
|
||||
val decTrees = log2Ceil(maxTransfer/beatBytes)
|
||||
val addressBits = log2Up(endAddress)
|
||||
val countBits = log2Up(endSourceId)
|
||||
val sizeBits = edge.bundle.sizeBits
|
||||
|
||||
// Reset control logic
|
||||
val wipeIndex = RegInit(UInt(0, width = log2Ceil(endAddressHi) + 1))
|
||||
val wipe = !wipeIndex(log2Ceil(endAddressHi))
|
||||
wipeIndex := wipeIndex + wipe.asUInt
|
||||
|
||||
// Block traffic while wiping Mems
|
||||
in.a.ready := out.a.ready && !wipe
|
||||
out.a.valid := in.a.valid && !wipe
|
||||
out.a.bits := in.a.bits
|
||||
out.d.ready := in.d.ready && !wipe
|
||||
in.d.valid := out.d.valid && !wipe
|
||||
in.d.bits := out.d.bits
|
||||
|
||||
// BCE unsupported
|
||||
in.b.valid := Bool(false)
|
||||
out.c.valid := Bool(false)
|
||||
out.e.valid := Bool(false)
|
||||
out.b.ready := Bool(true)
|
||||
in.c.ready := Bool(true)
|
||||
in.e.ready := Bool(true)
|
||||
|
||||
class ByteMonitor extends Bundle {
|
||||
val valid = Bool()
|
||||
val value = UInt(width = 8)
|
||||
}
|
||||
class FlightMonitor extends Bundle {
|
||||
val base = UInt(width = addressBits)
|
||||
val size = UInt(width = sizeBits)
|
||||
val opcode = UInt(width = 3)
|
||||
}
|
||||
|
||||
// Infer as simple dual port BRAM/M10k with write-first/new-data semantics (bypass needed)
|
||||
val shadow = Seq.fill(beatBytes) { Mem(endAddressHi, new ByteMonitor) }
|
||||
val inc_bytes = Seq.fill(beatBytes) { Mem(endAddressHi, UInt(width = countBits)) }
|
||||
val dec_bytes = Seq.fill(beatBytes) { Mem(endAddressHi, UInt(width = countBits)) }
|
||||
val inc_trees = Seq.tabulate(decTrees) { i => Mem(endAddressHi >> (i+1), UInt(width = countBits)) }
|
||||
val dec_trees = Seq.tabulate(decTrees) { i => Mem(endAddressHi >> (i+1), UInt(width = countBits)) }
|
||||
|
||||
val shadow_wen = Wire(init = Fill(beatBytes, wipe))
|
||||
val inc_bytes_wen = Wire(init = Fill(beatBytes, wipe))
|
||||
val dec_bytes_wen = Wire(init = Fill(beatBytes, wipe))
|
||||
val inc_trees_wen = Wire(init = Fill(decTrees, wipe))
|
||||
val dec_trees_wen = Wire(init = Fill(decTrees, wipe))
|
||||
|
||||
// This requires either distributed memory or registers (no register on either input or output)
|
||||
val flight = Mem(endSourceId, new FlightMonitor)
|
||||
|
||||
// We want to cross flight data from A to D in the same cycle (for combinational TL2 devices)
|
||||
val a_flight = Wire(new FlightMonitor)
|
||||
a_flight.base := edge.address(in.a.bits)
|
||||
a_flight.size := edge.size(in.a.bits)
|
||||
a_flight.opcode := in.a.bits.opcode
|
||||
|
||||
flight.write(in.a.bits.source, a_flight)
|
||||
val bypass = in.a.valid && in.a.bits.source === out.d.bits.source
|
||||
val d_flight = RegNext(Mux(bypass, a_flight, flight.read(out.d.bits.source)))
|
||||
|
||||
// Process A access requests
|
||||
val a = Reg(next = in.a.bits)
|
||||
val a_fire = Reg(next = in.a.fire(), init = Bool(false))
|
||||
val a_beats1 = edge.numBeats1(a)
|
||||
val a_size = edge.size(a)
|
||||
val a_sizeOH = UIntToOH(a_size)
|
||||
val a_counter = RegInit(UInt(0, width = maxLgBeats))
|
||||
val a_counter1 = a_counter - UInt(1)
|
||||
val a_first = a_counter === UInt(0)
|
||||
val a_addr_hi = a.addr_hi | (a_beats1 & ~a_counter1)
|
||||
val a_base = edge.address(a)
|
||||
val a_mask = edge.mask(a_base, a_size)
|
||||
|
||||
// Grab the concurrency state we need
|
||||
val a_inc_bytes = inc_bytes.map(_.read(a_addr_hi))
|
||||
val a_dec_bytes = dec_bytes.map(_.read(a_addr_hi))
|
||||
val a_inc_trees = inc_trees.zipWithIndex.map{ case (m, i) => m.read(a_addr_hi >> (i+1)) }
|
||||
val a_dec_trees = dec_trees.zipWithIndex.map{ case (m, i) => m.read(a_addr_hi >> (i+1)) }
|
||||
val a_inc_tree = a_inc_trees.fold(UInt(0))(_ + _)
|
||||
val a_dec_tree = a_dec_trees.fold(UInt(0))(_ + _)
|
||||
val a_inc = a_inc_bytes.map(_ + a_inc_tree)
|
||||
val a_dec = a_dec_bytes.map(_ + a_dec_tree)
|
||||
|
||||
when (a_fire) {
|
||||
// Record the request so we can handle it's response
|
||||
a_counter := Mux(a_first, a_beats1, a_counter1)
|
||||
|
||||
// !!! atomics
|
||||
assert (a.opcode =/= TLMessages.Acquire)
|
||||
|
||||
// Increase the per-byte flight counter for the whole transaction
|
||||
when (a_first && a.opcode =/= TLMessages.Hint) {
|
||||
when (a_size <= UInt(shift)) {
|
||||
inc_bytes_wen := a_mask
|
||||
}
|
||||
inc_trees_wen := a_sizeOH >> (shift+1)
|
||||
}
|
||||
|
||||
when (a.opcode === TLMessages.PutFullData || a.opcode === TLMessages.PutPartialData) {
|
||||
shadow_wen := a.mask
|
||||
for (i <- 0 until beatBytes) {
|
||||
val busy = a_inc(i) - a_dec(i) - (!a_first).asUInt
|
||||
val byte = a.data(8*(i+1)-1, 8*i)
|
||||
when (a.mask(i)) {
|
||||
printf("P 0x%x := 0x%x #%d\n", a_addr_hi << shift | UInt(i), byte, busy)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val a_waddr = Mux(wipe, wipeIndex, a_addr_hi)
|
||||
for (i <- 0 until beatBytes) {
|
||||
val data = Wire(new ByteMonitor)
|
||||
val busy = a_inc(i) - a_dec(i) - (!a_first).asUInt
|
||||
data.valid := Mux(wipe, Bool(false), busy === UInt(0))
|
||||
data.value := a.data(8*(i+1)-1, 8*i)
|
||||
when (shadow_wen(i)) {
|
||||
shadow(i).write(a_waddr, data)
|
||||
}
|
||||
}
|
||||
|
||||
for (i <- 0 until beatBytes) {
|
||||
val data = Mux(wipe, UInt(0), a_inc_bytes(i) + UInt(1))
|
||||
when (inc_bytes_wen(i)) {
|
||||
inc_bytes(i).write(a_waddr, data)
|
||||
}
|
||||
}
|
||||
|
||||
for (i <- 0 until inc_trees.size) {
|
||||
val data = Mux(wipe, UInt(0), a_inc_trees(i) + UInt(1))
|
||||
when (inc_trees_wen(i)) {
|
||||
inc_trees(i).write(a_waddr >> (i+1), data)
|
||||
}
|
||||
}
|
||||
|
||||
// Process D access responses
|
||||
val d = RegNext(out.d.bits)
|
||||
val d_fire = Reg(next = out.d.fire(), init = Bool(false))
|
||||
val d_bypass = a_fire && d.source === a.source
|
||||
val d_beats1 = edge.numBeats1(d)
|
||||
val d_size = edge.size(d)
|
||||
val d_sizeOH = UIntToOH(d_size)
|
||||
val d_counter = RegInit(UInt(0, width = maxLgBeats))
|
||||
val d_counter1 = d_counter - UInt(1)
|
||||
val d_first = d_counter === UInt(0)
|
||||
val d_last = d_counter === UInt(1) || d_beats1 === UInt(0)
|
||||
val d_base = d_flight.base
|
||||
val d_addr_hi = d_base >> shift | (d_beats1 & ~d_counter1)
|
||||
val d_mask = edge.mask(d_base, d_size)
|
||||
|
||||
// Grab the concurrency state we need
|
||||
val d_inc_bytes = inc_bytes.map(_.read(d_addr_hi))
|
||||
val d_dec_bytes = dec_bytes.map(_.read(d_addr_hi))
|
||||
val d_inc_trees = inc_trees.zipWithIndex.map{ case (m, i) => m.read(d_addr_hi >> (i+1)) }
|
||||
val d_dec_trees = dec_trees.zipWithIndex.map{ case (m, i) => m.read(d_addr_hi >> (i+1)) }
|
||||
val d_inc_tree = d_inc_trees.fold(UInt(0))(_ + _)
|
||||
val d_dec_tree = d_dec_trees.fold(UInt(0))(_ + _)
|
||||
val d_inc = d_inc_bytes.map(_ + d_inc_tree)
|
||||
val d_dec = d_dec_bytes.map(_ + d_dec_tree)
|
||||
val d_shadow = shadow.map(_.read(d_addr_hi))
|
||||
|
||||
when (d_fire) {
|
||||
assert (d_size === d_flight.size)
|
||||
d_counter := Mux(d_first, d_beats1, d_counter1)
|
||||
|
||||
when (d_flight.opcode === TLMessages.Hint) {
|
||||
assert (d.opcode === TLMessages.HintAck)
|
||||
}
|
||||
|
||||
// Decreaes the per-byte flight counter for the whole transaction
|
||||
when (d_last && d_flight.opcode =/= TLMessages.Hint) {
|
||||
when (d_size <= UInt(shift)) {
|
||||
dec_bytes_wen := d_mask
|
||||
}
|
||||
dec_trees_wen := d_sizeOH >> (shift+1)
|
||||
}
|
||||
|
||||
when (d_flight.opcode === TLMessages.PutFullData || d_flight.opcode === TLMessages.PutPartialData) {
|
||||
assert (d.opcode === TLMessages.AccessAck)
|
||||
}
|
||||
|
||||
// !!! atomics
|
||||
|
||||
when (d_flight.opcode === TLMessages.Get) {
|
||||
assert (d.opcode === TLMessages.AccessAckData)
|
||||
for (i <- 0 until beatBytes) {
|
||||
val got = d.data(8*(i+1)-1, 8*i)
|
||||
val shadow = Wire(init = d_shadow(i))
|
||||
when (d_mask(i)) {
|
||||
when (!shadow.valid) {
|
||||
printf("G 0x%x := undefined\n", d_addr_hi << shift | UInt(i))
|
||||
} .otherwise {
|
||||
printf("G 0x%x := 0x%x\n", d_addr_hi << shift | UInt(i), got)
|
||||
assert (shadow.value === got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val d_waddr = Mux(wipe, wipeIndex, d_addr_hi)
|
||||
for (i <- 0 until beatBytes) {
|
||||
val data = Mux(wipe, UInt(0), d_dec_bytes(i) + UInt(1))
|
||||
when (dec_bytes_wen(i)) {
|
||||
dec_bytes(i).write(d_waddr, data)
|
||||
}
|
||||
}
|
||||
|
||||
for (i <- 0 until dec_trees.size) {
|
||||
val data = Mux(wipe, UInt(0), d_dec_trees(i) + UInt(1))
|
||||
when (dec_trees_wen(i)) {
|
||||
dec_trees(i).write(d_waddr >> (i+1), data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -105,9 +105,8 @@ class TLRegisterRouter[B <: TLRegBundleBase, M <: LazyModuleImp]
|
||||
(moduleBuilder: (=> B, TLRegisterRouterBase) => M)
|
||||
extends TLRegisterRouterBase(AddressSet(base, size-1), interrupts, concurrency, beatBytes)
|
||||
{
|
||||
require (size % 4096 == 0) // devices should be 4K aligned
|
||||
require (isPow2(size))
|
||||
require (size >= 4096)
|
||||
// require (size >= 4096) ... not absolutely required, but highly recommended
|
||||
|
||||
lazy val module = moduleBuilder(bundleBuilder(TLRegBundleArg(intnode.bundleOut, node.bundleIn)), this)
|
||||
}
|
||||
|
222
src/main/scala/uncore/tilelink2/RegisterRouterTest.scala
Normal file
222
src/main/scala/uncore/tilelink2/RegisterRouterTest.scala
Normal file
@ -0,0 +1,222 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package uncore.tilelink2
|
||||
|
||||
import Chisel._
|
||||
|
||||
object LFSR16Seed
|
||||
{
|
||||
def apply(seed: Int): UInt =
|
||||
{
|
||||
val width = 16
|
||||
val lfsr = Reg(init=UInt((seed*0x7231) % 65536, width))
|
||||
lfsr := Cat(lfsr(0)^lfsr(2)^lfsr(3)^lfsr(5), lfsr(width-1,1))
|
||||
lfsr
|
||||
}
|
||||
}
|
||||
|
||||
class RRTestCombinational(val bits: Int, rvalid: Bool => Bool, wready: Bool => Bool) extends Module
|
||||
{
|
||||
val io = new Bundle {
|
||||
val rvalid = Bool(OUTPUT)
|
||||
val rready = Bool(INPUT)
|
||||
val rdata = UInt(OUTPUT, width = bits)
|
||||
val wvalid = Bool(INPUT)
|
||||
val wready = Bool(OUTPUT)
|
||||
val wdata = UInt(INPUT, width = bits)
|
||||
}
|
||||
|
||||
val rfire = io.rvalid && io.rready
|
||||
val wfire = io.wvalid && io.wready
|
||||
val reg = Reg(UInt(width = bits))
|
||||
|
||||
io.rvalid := rvalid(rfire)
|
||||
io.wready := wready(wfire)
|
||||
|
||||
io.rdata := Mux(rfire, reg, UInt(0))
|
||||
when (wfire) { reg := io.wdata }
|
||||
}
|
||||
|
||||
object RRTestCombinational
|
||||
{
|
||||
private var seed = 42
|
||||
def always: Bool => Bool = _ => Bool(true)
|
||||
def random: Bool => Bool = {
|
||||
seed = seed + 1
|
||||
_ => LFSR16Seed(seed)(0)
|
||||
}
|
||||
def delay(x: Int): Bool => Bool = { fire =>
|
||||
val reg = RegInit(UInt(0, width = log2Ceil(x+1)))
|
||||
val ready = reg === UInt(0)
|
||||
reg := Mux(fire, UInt(x), Mux(ready, UInt(0), reg - UInt(1)))
|
||||
ready
|
||||
}
|
||||
|
||||
def combo(bits: Int, rvalid: Bool => Bool, wready: Bool => Bool): RegField = {
|
||||
val combo = Module(new RRTestCombinational(bits, rvalid, wready))
|
||||
RegField(bits,
|
||||
RegReadFn { ready => combo.io.rready := ready; (combo.io.rvalid, combo.io.rdata) },
|
||||
RegWriteFn { (valid, data) => combo.io.wvalid := valid; combo.io.wdata := data; combo.io.wready })
|
||||
}
|
||||
}
|
||||
|
||||
class RRTestRequest(val bits: Int,
|
||||
rflow: (Bool, Bool, UInt) => (Bool, Bool, UInt),
|
||||
wflow: (Bool, Bool, UInt) => (Bool, Bool, UInt)) extends Module
|
||||
{
|
||||
val io = new Bundle {
|
||||
val rivalid = Bool(INPUT)
|
||||
val riready = Bool(OUTPUT)
|
||||
val rovalid = Bool(OUTPUT)
|
||||
val roready = Bool(INPUT)
|
||||
val rdata = UInt(OUTPUT, width = bits)
|
||||
val wivalid = Bool(INPUT)
|
||||
val wiready = Bool(OUTPUT)
|
||||
val wovalid = Bool(OUTPUT)
|
||||
val woready = Bool(INPUT)
|
||||
val wdata = UInt(INPUT, width = bits)
|
||||
}
|
||||
|
||||
val (riready, rovalid, _) = rflow(io.rivalid, io.roready, UInt(0, width = 1))
|
||||
val (wiready, wovalid, wdata) = wflow(io.wivalid, io.woready, io.wdata)
|
||||
val reg = Reg(UInt(width = bits))
|
||||
|
||||
io.riready := riready
|
||||
io.rovalid := rovalid
|
||||
io.wiready := wiready
|
||||
io.wovalid := wovalid
|
||||
|
||||
val rofire = io.roready && rovalid
|
||||
val wofire = io.woready && wovalid
|
||||
|
||||
io.rdata := Mux(rofire, reg, UInt(0))
|
||||
when (wofire) { reg := wdata }
|
||||
}
|
||||
|
||||
object RRTestRequest
|
||||
{
|
||||
private var seed = 1231
|
||||
def pipe(x: Int): (Bool, Bool, UInt) => (Bool, Bool, UInt) = { (ivalid, oready, idata) =>
|
||||
val full = RegInit(Vec.fill(x)(Bool(false)))
|
||||
val ready = Wire(Vec(x, Bool()))
|
||||
val data = Reg(Vec(x, UInt(width = idata.getWidth)))
|
||||
// Construct a classic bubble-filling pipeline
|
||||
ready(x-1) := oready || !full(x-1)
|
||||
when (ready(0)) { data(0) := idata }
|
||||
when (ready(0)) { full(0) := ivalid }
|
||||
((ready.init zip ready.tail) zip full.init) foreach { case ((self, next), full) =>
|
||||
self := next || !full
|
||||
}
|
||||
((data.init zip data.tail) zip ready.tail) foreach { case ((prev, self), ready) =>
|
||||
when (ready) { self := prev }
|
||||
}
|
||||
((full.init zip full.tail) zip ready.tail) foreach { case ((prev, self), ready) =>
|
||||
when (ready) { self := prev }
|
||||
}
|
||||
(ready(0), full(x-1), data(x-1))
|
||||
}
|
||||
def busy: (Bool, Bool, UInt) => (Bool, Bool, UInt) = {
|
||||
seed = seed + 1
|
||||
(ivalid, oready, idata) => {
|
||||
val lfsr = LFSR16Seed(seed)
|
||||
val busy = RegInit(Bool(false))
|
||||
val data = Reg(UInt(width = idata.getWidth))
|
||||
val progress = lfsr(0)
|
||||
when (progress) {
|
||||
busy := Mux(busy, !oready, ivalid)
|
||||
}
|
||||
val iready = progress && !busy
|
||||
val ovalid = progress && busy
|
||||
when (ivalid && iready) { data := idata }
|
||||
(iready, ovalid, data)
|
||||
}
|
||||
}
|
||||
def request(bits: Int,
|
||||
rflow: (Bool, Bool, UInt) => (Bool, Bool, UInt),
|
||||
wflow: (Bool, Bool, UInt) => (Bool, Bool, UInt)): RegField = {
|
||||
val request = Module(new RRTestRequest(bits, rflow, wflow))
|
||||
RegField(bits,
|
||||
RegReadFn { (rivalid, roready) =>
|
||||
request.io.rivalid := rivalid
|
||||
request.io.roready := roready
|
||||
(request.io.riready, request.io.rovalid, request.io.rdata) },
|
||||
RegWriteFn { (wivalid, woready, wdata) =>
|
||||
request.io.wivalid := wivalid
|
||||
request.io.woready := woready
|
||||
request.io.wdata := wdata
|
||||
(request.io.wiready, request.io.wovalid) })
|
||||
}
|
||||
}
|
||||
|
||||
object RRTest0Map
|
||||
{
|
||||
import RRTestCombinational._
|
||||
|
||||
def aa(bits: Int) = combo(bits, always, always)
|
||||
def ar(bits: Int) = combo(bits, always, random)
|
||||
def ad(bits: Int) = combo(bits, always, delay(11))
|
||||
def ae(bits: Int) = combo(bits, always, delay(5))
|
||||
def ra(bits: Int) = combo(bits, random, always)
|
||||
def rr(bits: Int) = combo(bits, random, random)
|
||||
def rd(bits: Int) = combo(bits, random, delay(11))
|
||||
def re(bits: Int) = combo(bits, random, delay(5))
|
||||
def da(bits: Int) = combo(bits, delay(5), always)
|
||||
def dr(bits: Int) = combo(bits, delay(5), random)
|
||||
def dd(bits: Int) = combo(bits, delay(5), delay(5))
|
||||
def de(bits: Int) = combo(bits, delay(5), delay(11))
|
||||
def ea(bits: Int) = combo(bits, delay(11), always)
|
||||
def er(bits: Int) = combo(bits, delay(11), random)
|
||||
def ed(bits: Int) = combo(bits, delay(11), delay(5))
|
||||
def ee(bits: Int) = combo(bits, delay(11), delay(11))
|
||||
|
||||
// All fields must respect byte alignment, or else it won't behave like an SRAM
|
||||
val map = Seq(
|
||||
0 -> Seq(aa(8), ar(8), ad(8), ae(8)),
|
||||
1 -> Seq(ra(8), rr(8), rd(8), re(8)),
|
||||
2 -> Seq(da(8), dr(8), dd(8), de(8)),
|
||||
3 -> Seq(ea(8), er(8), ed(8), ee(8)),
|
||||
4 -> Seq(aa(3), ar(5), ad(1), ae(7), ra(2), rr(6), rd(4), re(4)),
|
||||
5 -> Seq(da(3), dr(5), dd(1), de(7), ea(2), er(6), ed(4), ee(4)),
|
||||
6 -> Seq(aa(8), rr(8), dd(8), ee(8)),
|
||||
7 -> Seq(ar(8), rd(8), de(8), ea(8)))
|
||||
}
|
||||
|
||||
object RRTest1Map
|
||||
{
|
||||
import RRTestRequest._
|
||||
|
||||
def pp(bits: Int) = request(bits, pipe(3), pipe(3))
|
||||
def pb(bits: Int) = request(bits, pipe(3), busy)
|
||||
def bp(bits: Int) = request(bits, busy, pipe(3))
|
||||
def bb(bits: Int) = request(bits, busy, busy)
|
||||
|
||||
val map = RRTest0Map.map.take(6) ++ Seq(
|
||||
6 -> Seq(pp(8), pb(8), bp(8), bb(8)),
|
||||
7 -> Seq(pp(3), pb(5), bp(1), bb(7), pb(5), bp(3), pp(4), bb(4)))
|
||||
}
|
||||
|
||||
trait RRTest0Bundle
|
||||
{
|
||||
}
|
||||
|
||||
trait RRTest0Module extends HasRegMap
|
||||
{
|
||||
regmap(RRTest0Map.map:_*)
|
||||
}
|
||||
|
||||
class RRTest0(address: BigInt) extends TLRegisterRouter(address, 0, 32, Some(0), 4)(
|
||||
new TLRegBundle((), _) with RRTest0Bundle)(
|
||||
new TLRegModule((), _, _) with RRTest0Module)
|
||||
|
||||
trait RRTest1Bundle
|
||||
{
|
||||
}
|
||||
|
||||
trait RRTest1Module extends HasRegMap
|
||||
{
|
||||
regmap(RRTest1Map.map:_*)
|
||||
}
|
||||
|
||||
class RRTest1(address: BigInt) extends TLRegisterRouter(address, 0, 32, Some(6), 4)(
|
||||
new TLRegBundle((), _) with RRTest1Bundle)(
|
||||
new TLRegModule((), _, _) with RRTest1Module)
|
@ -81,5 +81,6 @@ object UncoreUnitTests {
|
||||
Seq(
|
||||
Module(new SmiConverterTest),
|
||||
Module(new ROMSlaveTest),
|
||||
Module(new TileLinkRAMTest))
|
||||
Module(new TileLinkRAMTest),
|
||||
Module(new uncore.tilelink2.TLFuzzRAMTest))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user