1
0
Fork 0
rocket-chip/src/main/scala/uncore/tilelink2/Fuzzer.scala

255 lines
8.3 KiB
Scala
Raw Normal View History

// See LICENSE for license details.
package uncore.tilelink2
import Chisel._
import chisel3.util.LFSR16
import unittest._
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)))
2016-09-13 03:39:50 +02:00
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 ClockDivider extends BlackBox {
val io = new Bundle {
val clock_in = Clock(INPUT)
val reset_in = Bool(INPUT)
val clock_out = Clock(OUTPUT)
val reset_out = Bool(OUTPUT)
}
}
class TLFuzzRAM extends LazyModule
{
val model = LazyModule(new TLRAMModel)
2016-09-13 06:41:36 +02:00
val ram = LazyModule(new TLRAM(AddressSet(0, 0x3ff)))
val gpio = LazyModule(new RRTest1(0x400))
val xbar = LazyModule(new TLXbar)
2016-09-13 06:41:36 +02:00
val fuzz = LazyModule(new TLFuzzer(5000))
val cross = LazyModule(new TLAsyncCrossing)
model.node := fuzz.node
xbar.node := TLWidthWidget(TLHintHandler(model.node), 16)
cross.node := TLFragmenter(TLBuffer(xbar.node), 4, 256)
ram.node := cross.node
2016-09-13 06:41:36 +02:00
gpio.node := TLFragmenter(TLBuffer(xbar.node), 4, 32)
lazy val module = new LazyModuleImp(this) with HasUnitTestIO {
io.finished := fuzz.module.io.finished
// Shove the RAM into another clock domain
val clocks = Module(new ClockDivider)
ram.module.clock := clocks.io.clock_out
ram.module.reset := clocks.io.reset_out
clocks.io.clock_in := clock
clocks.io.reset_in := reset
// ... and safely cross TL2 into it
cross.module.io.in_clock := clock
cross.module.io.in_reset := reset
cross.module.io.out_clock := clocks.io.clock_out
cross.module.io.out_reset := clocks.io.reset_out
}
}
class TLFuzzRAMTest extends UnitTest {
val dut = Module(LazyModule(new TLFuzzRAM).module)
io.finished := dut.io.finished
}