Fundamental new features: * Added tile package: This package is intended to hold components re-usable across different types of tile. Will be the future location of TL2-RoCC accelerators and new diplomatic versions of intra-tile interfaces. * Adopted [ModuleName]Params convention: Code base was very inconsistent about what to name case classes that provide parameters to modules. Settled on calling them [ModuleName]Params to distinguish them from config.Parameters and config.Config. So far applied mostly only to case classes defined within rocket and tile. * Defined RocketTileParams: A nested case class containing case classes for all the components of a tile (L1 caches and core). Allows all such parameters to vary per-tile. * Defined RocketCoreParams: All the parameters that can be varied per-core. * Defined L1CacheParams: A trait defining the parameters common to L1 caches, made concrete in different derived case classes. * Defined RocketTilesKey: A sequence of RocketTileParams, one for every tile to be created. * Provided HeterogeneousDualCoreConfig: An example of making a heterogeneous chip with two cores, one big and one little. * Changes to legacy code: ReplacementPolicy moved to package util. L1Metadata moved to package tile. Legacy L2 cache agent removed because it can no longer share the metadata array implementation with the L1. Legacy GroundTests on life support. Additional changes that got rolled in along the way: * rocket: Fix critical path through BTB for I$ index bits > pgIdxBits * coreplex: tiles connected via :=* * groundtest: updated to use TileParams * tilelink: cache cork requirements are relaxed to allow more cacheless masters
790 lines
28 KiB
Scala
790 lines
28 KiB
Scala
// See LICENSE.Berkeley for license details.
|
|
// See LICENSE.SiFive for license details.
|
|
|
|
package groundtest
|
|
|
|
import Chisel._
|
|
import uncore.tilelink._
|
|
import uncore.constants._
|
|
import uncore.util._
|
|
import util._
|
|
import rocket._
|
|
import rocketchip._
|
|
import config._
|
|
|
|
class RegressionIO(implicit val p: Parameters) extends ParameterizedBundle()(p) {
|
|
val start = Bool(INPUT)
|
|
val cache = new HellaCacheIO
|
|
val mem = new ClientUncachedTileLinkIO
|
|
val finished = Bool(OUTPUT)
|
|
val errored = Bool(OUTPUT)
|
|
}
|
|
|
|
abstract class Regression(implicit val p: Parameters)
|
|
extends Module with HasTileLinkParameters {
|
|
val memStart = p(ExtMem).base
|
|
val memStartBlock = memStart >> p(CacheBlockOffsetBits)
|
|
val io = new RegressionIO
|
|
|
|
def disableCache() {
|
|
io.cache.req.valid := Bool(false)
|
|
io.cache.req.bits.addr := UInt(memStart)
|
|
io.cache.req.bits.typ := UInt(0)
|
|
io.cache.req.bits.cmd := M_XRD
|
|
io.cache.req.bits.tag := UInt(0)
|
|
io.cache.req.bits.data := Bits(0)
|
|
io.cache.req.bits.phys := Bool(true)
|
|
io.cache.invalidate_lr := Bool(false)
|
|
}
|
|
|
|
def disableMem() {
|
|
io.mem.acquire.valid := Bool(false)
|
|
io.mem.grant.ready := Bool(false)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This was a bug in which the TileLinkIONarrower logic screwed up
|
|
* when a PutBlock request and a narrow Get request are sent to it at the
|
|
* same time. Repeating this sequence enough times will cause a queue to
|
|
* get filled up and deadlock the system.
|
|
*/
|
|
class IOGetAfterPutBlockRegression(implicit p: Parameters) extends Regression()(p) {
|
|
val nRuns = 7
|
|
val run = Reg(init = UInt(0, log2Up(nRuns + 1)))
|
|
|
|
val (put_beat, put_done) = Counter(
|
|
io.mem.acquire.fire() && io.mem.acquire.bits.hasData(), tlDataBeats)
|
|
|
|
val started = Reg(init = Bool(false))
|
|
val put_sent = Reg(init = Bool(false))
|
|
val get_sent = Reg(init = Bool(false))
|
|
val put_acked = Reg(init = Bool(false))
|
|
val get_acked = Reg(init = Bool(false))
|
|
val both_acked = put_acked && get_acked
|
|
|
|
when (!started && io.start) { started := Bool(true) }
|
|
|
|
io.mem.acquire.valid := !put_sent && started
|
|
io.mem.acquire.bits := PutBlock(
|
|
client_xact_id = UInt(0),
|
|
addr_block = UInt(memStartBlock),
|
|
addr_beat = put_beat,
|
|
data = UInt(0))
|
|
io.mem.grant.ready := Bool(true)
|
|
|
|
io.cache.req.valid := !get_sent && started
|
|
io.cache.req.bits.addr := UInt(testRamAddr)
|
|
io.cache.req.bits.typ := MT_WU
|
|
io.cache.req.bits.cmd := M_XRD
|
|
io.cache.req.bits.tag := UInt(0)
|
|
io.cache.invalidate_lr := Bool(false)
|
|
|
|
when (put_done) { put_sent := Bool(true) }
|
|
when (io.cache.req.fire()) { get_sent := Bool(true) }
|
|
when (io.mem.grant.fire()) { put_acked := Bool(true) }
|
|
when (io.cache.resp.valid) { get_acked := Bool(true) }
|
|
|
|
when (both_acked) {
|
|
when (run < UInt(nRuns - 1)) {
|
|
put_sent := Bool(false)
|
|
get_sent := Bool(false)
|
|
}
|
|
put_acked := Bool(false)
|
|
get_acked := Bool(false)
|
|
run := run + UInt(1)
|
|
}
|
|
|
|
io.finished := (run === UInt(nRuns))
|
|
}
|
|
|
|
/* This was a bug with merging two PutBlocks to the same address in the L2.
|
|
* The transactor would start accepting beats of the second transaction but
|
|
* acknowledge both of them when the first one finished.
|
|
* This caused the state to go funky since the next time around it would
|
|
* start the put in the middle */
|
|
class PutBlockMergeRegression(nSets: Int)(implicit p: Parameters)
|
|
extends Regression()(p) with HasTileLinkParameters {
|
|
val s_idle :: s_put :: s_wait :: s_done :: Nil = Enum(Bits(), 4)
|
|
val state = Reg(init = s_idle)
|
|
|
|
disableCache()
|
|
|
|
val addr_blocks = Vec(Seq(0, 0, nSets).map(num => UInt(num + memStartBlock)))
|
|
val nSteps = addr_blocks.size
|
|
val (acq_beat, acq_done) = Counter(io.mem.acquire.fire(), tlDataBeats)
|
|
val (send_cnt, send_done) = Counter(acq_done, nSteps)
|
|
val (ack_cnt, ack_done) = Counter(io.mem.grant.fire(), nSteps)
|
|
|
|
io.mem.acquire.valid := (state === s_put)
|
|
io.mem.acquire.bits := PutBlock(
|
|
client_xact_id = send_cnt,
|
|
addr_block = addr_blocks(send_cnt),
|
|
addr_beat = acq_beat,
|
|
data = Cat(send_cnt, acq_beat))
|
|
io.mem.grant.ready := Bool(true)
|
|
|
|
when (state === s_idle && io.start) { state := s_put }
|
|
when (send_done) { state := s_wait }
|
|
when (ack_done) { state := s_done }
|
|
|
|
io.finished := (state === s_done)
|
|
}
|
|
|
|
/* Make sure the L2 does "the right thing" when a put is sent no-alloc but
|
|
* the block is already in cache. It should just treat the request as a
|
|
* regular allocating put */
|
|
class NoAllocPutHitRegression(implicit p: Parameters) extends Regression()(p) {
|
|
val (s_idle :: s_prefetch :: s_put :: s_get ::
|
|
s_wait :: s_done :: Nil) = Enum(Bits(), 6)
|
|
val state = Reg(init = s_idle)
|
|
|
|
val acq = io.mem.acquire.bits
|
|
val gnt = io.mem.grant.bits
|
|
|
|
val (put_beat, put_done) = Counter(io.mem.acquire.fire() && acq.hasData(), tlDataBeats)
|
|
val acked = Reg(init = UInt(0, tlDataBeats + 2))
|
|
|
|
val addr_block = UInt(memStartBlock + 2)
|
|
val test_data = UInt(0x3446)
|
|
|
|
val prefetch_acq = GetPrefetch(
|
|
client_xact_id = UInt(0),
|
|
addr_block = addr_block)
|
|
val put_acq = PutBlock(
|
|
client_xact_id = UInt(1),
|
|
addr_block = addr_block,
|
|
addr_beat = put_beat,
|
|
data = test_data,
|
|
alloc = Bool(false))
|
|
val get_acq = GetBlock(
|
|
client_xact_id = UInt(2),
|
|
addr_block = addr_block)
|
|
|
|
io.mem.acquire.valid := state.isOneOf(s_prefetch, s_get, s_put)
|
|
io.mem.acquire.bits := MuxCase(get_acq, Seq(
|
|
(state === s_prefetch) -> prefetch_acq,
|
|
(state === s_put) -> put_acq))
|
|
io.mem.grant.ready := Bool(true)
|
|
|
|
when (state === s_idle && io.start) { state := s_prefetch }
|
|
when (state === s_prefetch && io.mem.acquire.ready) { state := s_put }
|
|
when (put_done) { state := s_get }
|
|
when (state === s_get && io.mem.acquire.ready) { state := s_wait }
|
|
when (state === s_wait && acked.andR) { state := s_done }
|
|
|
|
when (io.mem.grant.fire()) {
|
|
switch (gnt.client_xact_id) {
|
|
is (UInt(0)) { acked := acked | UInt(1 << tlDataBeats) }
|
|
is (UInt(1)) { acked := acked | UInt(1 << (tlDataBeats + 1)) }
|
|
is (UInt(2)) { acked := acked | UIntToOH(gnt.addr_beat) }
|
|
}
|
|
}
|
|
|
|
val data_mismatch = io.mem.grant.fire() && gnt.hasData() && gnt.data =/= test_data
|
|
assert(!data_mismatch, "NoAllocPutHitRegression: data does not match")
|
|
|
|
io.finished := (state === s_done)
|
|
io.errored := data_mismatch
|
|
|
|
disableCache()
|
|
}
|
|
|
|
/** Make sure L2 does the right thing when multiple puts are sent for the
|
|
* same block, but only the first one has the alloc bit set. */
|
|
class MixedAllocPutRegression(implicit p: Parameters) extends Regression()(p) {
|
|
val (s_idle :: s_pf_send :: s_pf_wait :: s_put_send :: s_put_wait ::
|
|
s_get_send :: s_get_wait :: s_done :: Nil) = Enum(Bits(), 8)
|
|
val state = Reg(init = s_idle)
|
|
|
|
/** We have to test two cases: one when the block is already cached
|
|
* and one when the block is not yet cached.
|
|
* We use prefetching to assure the first case. */
|
|
val test_data = Vec(
|
|
UInt("h2222222211111111"),
|
|
UInt("h3333333333333333"),
|
|
UInt("h4444444444444444"),
|
|
UInt("h5555555555555555"))
|
|
val test_alloc = Vec(Bool(false), Bool(false), Bool(true), Bool(false))
|
|
val test_block = Vec(
|
|
Seq.fill(2) { UInt(memStartBlock + 15) } ++
|
|
Seq.fill(2) { UInt(memStartBlock + 16) })
|
|
val test_beat = Vec(UInt(0), UInt(2), UInt(1), UInt(2))
|
|
|
|
val (put_acq_id, put_acq_done) = Counter(
|
|
state === s_put_send && io.mem.acquire.ready, test_data.size)
|
|
val (put_gnt_cnt, put_gnt_done) = Counter(
|
|
state === s_put_wait && io.mem.grant.valid, test_data.size)
|
|
|
|
val (get_acq_id, get_acq_done) = Counter(
|
|
state === s_get_send && io.mem.acquire.ready, test_data.size)
|
|
val (get_gnt_cnt, get_gnt_done) = Counter(
|
|
state === s_get_wait && io.mem.grant.valid, test_data.size)
|
|
|
|
val pf_acquire = PutPrefetch(
|
|
client_xact_id = UInt(0),
|
|
addr_block = UInt(memStartBlock + 15))
|
|
|
|
val put_acquire = Put(
|
|
client_xact_id = put_acq_id,
|
|
addr_block = test_block(put_acq_id),
|
|
addr_beat = test_beat(put_acq_id),
|
|
data = test_data(put_acq_id),
|
|
alloc = test_alloc(put_acq_id))
|
|
|
|
val get_acquire = Get(
|
|
client_xact_id = get_acq_id,
|
|
addr_block = test_block(get_acq_id),
|
|
addr_beat = test_beat(get_acq_id))
|
|
|
|
io.mem.acquire.valid := state.isOneOf(s_pf_send, s_put_send, s_get_send)
|
|
io.mem.acquire.bits := MuxLookup(state, pf_acquire, Seq(
|
|
s_put_send -> put_acquire,
|
|
s_get_send -> get_acquire))
|
|
io.mem.grant.ready := state.isOneOf(s_pf_wait, s_put_wait, s_get_wait)
|
|
|
|
when (state === s_idle && io.start) { state := s_pf_send }
|
|
when (state === s_pf_send && io.mem.acquire.ready) { state := s_pf_wait }
|
|
when (state === s_pf_wait && io.mem.grant.valid) { state := s_put_send }
|
|
when (put_acq_done) { state := s_put_wait }
|
|
when (put_gnt_done) { state := s_get_send }
|
|
when (get_acq_done) { state := s_get_wait }
|
|
when (get_gnt_done) { state := s_done }
|
|
|
|
io.finished := (state === s_done)
|
|
|
|
val data_mismatch = state === s_get_wait && io.mem.grant.fire() &&
|
|
io.mem.grant.bits.data =/= test_data(io.mem.grant.bits.client_xact_id)
|
|
assert(!data_mismatch, "MixedAllocPutRegression: data mismatch")
|
|
io.errored := data_mismatch
|
|
|
|
disableCache()
|
|
}
|
|
|
|
/* Make sure each no-alloc put triggers a request to outer memory.
|
|
* Unfortunately, there's no way to verify that this works except by looking
|
|
* at the waveform */
|
|
class RepeatedNoAllocPutRegression(implicit p: Parameters) extends Regression()(p) {
|
|
disableCache()
|
|
|
|
val nPuts = 2
|
|
val (put_beat, put_done) = Counter(io.mem.acquire.fire(), tlDataBeats)
|
|
val (req_cnt, req_done) = Counter(put_done, nPuts)
|
|
|
|
val sending = Reg(init = Bool(false))
|
|
val acked = Reg(init = UInt(0, nPuts))
|
|
|
|
when (!sending && io.start) { sending := Bool(true) }
|
|
when (sending && req_done) { sending := Bool(false) }
|
|
|
|
io.mem.acquire.valid := sending
|
|
io.mem.acquire.bits := PutBlock(
|
|
client_xact_id = req_cnt,
|
|
addr_block = UInt(memStartBlock + 5),
|
|
addr_beat = put_beat,
|
|
data = Cat(req_cnt, UInt(0, 8)),
|
|
alloc = Bool(false))
|
|
io.mem.grant.ready := Bool(true)
|
|
|
|
when (io.mem.grant.fire()) {
|
|
acked := acked | UIntToOH(io.mem.grant.bits.client_xact_id)
|
|
}
|
|
|
|
io.finished := acked.andR
|
|
}
|
|
|
|
/* Make sure write masking works properly by writing a block of data
|
|
* piece by piece */
|
|
class WriteMaskedPutBlockRegression(implicit p: Parameters) extends Regression()(p) {
|
|
disableCache()
|
|
|
|
val (s_idle :: s_put_send :: s_put_ack :: s_stall ::
|
|
s_get_send :: s_get_ack :: s_done :: Nil) = Enum(Bits(), 7)
|
|
val state = Reg(init = s_idle)
|
|
val post_stall_state = Reg(init = s_idle)
|
|
|
|
val gnt = io.mem.grant.bits
|
|
val acq = io.mem.acquire.bits
|
|
|
|
val stage = Reg(init = UInt(0, 1))
|
|
|
|
val (put_beat, put_block_done) = Counter(
|
|
io.mem.acquire.fire() && acq.hasData(), tlDataBeats)
|
|
val put_data = UInt(0x30010040, tlDataBits) + (put_beat << UInt(2))
|
|
|
|
val put_acq = PutBlock(
|
|
client_xact_id = UInt(0),
|
|
addr_block = UInt(memStartBlock + 7),
|
|
addr_beat = put_beat,
|
|
data = Mux(put_beat(0) === stage, put_data, UInt(0)),
|
|
wmask = Some(Mux(put_beat(0) === stage, Acquire.fullWriteMask, Bits(0))))
|
|
|
|
val get_acq = GetBlock(
|
|
client_xact_id = UInt(0),
|
|
addr_block = UInt(memStartBlock + 6) + stage)
|
|
|
|
io.mem.acquire.valid := state.isOneOf(s_put_send, s_get_send)
|
|
io.mem.acquire.bits := Mux(state === s_get_send, get_acq, put_acq)
|
|
io.mem.grant.ready := state.isOneOf(s_put_ack, s_get_ack)
|
|
|
|
val (get_cnt, get_done) = Counter(
|
|
io.mem.grant.fire() && gnt.hasData(), tlDataBeats)
|
|
val get_data = UInt(0x30010040, tlDataBits) + (get_cnt << UInt(2))
|
|
|
|
val (stall_cnt, stall_done) = Counter(state === s_stall, 16)
|
|
|
|
when (state === s_idle && io.start) { state := s_put_send }
|
|
when (put_block_done) { state := s_put_ack }
|
|
when (state === s_put_ack && io.mem.grant.valid) {
|
|
post_stall_state := s_get_send
|
|
state := s_stall
|
|
}
|
|
when (stall_done) { state := post_stall_state }
|
|
when (state === s_get_send && io.mem.acquire.ready) { state := s_get_ack }
|
|
when (get_done) {
|
|
// do a read in-between the two put-blocks to overwrite the data buffer
|
|
when (stage === UInt(0)) {
|
|
stage := stage + UInt(1)
|
|
post_stall_state := s_put_send
|
|
state := s_stall
|
|
} .otherwise { state := s_done }
|
|
}
|
|
|
|
io.finished := (state === s_done)
|
|
|
|
val data_mismatch = io.mem.grant.fire() && io.mem.grant.bits.hasData() &&
|
|
stage =/= UInt(0) && io.mem.grant.bits.data =/= get_data
|
|
assert(!data_mismatch, "WriteMaskedPutBlockRegression: data does not match")
|
|
io.errored := data_mismatch
|
|
}
|
|
|
|
/* Make sure a prefetch that hits returns immediately. */
|
|
class PrefetchHitRegression(implicit p: Parameters) extends Regression()(p) {
|
|
disableCache()
|
|
|
|
val sending = Reg(init = Bool(false))
|
|
val nPrefetches = 2
|
|
val (pf_cnt, pf_done) = Counter(io.mem.acquire.fire(), nPrefetches)
|
|
val acked = Reg(init = UInt(0, nPrefetches))
|
|
|
|
val acq_bits = Vec(
|
|
PutPrefetch(client_xact_id = UInt(0), addr_block = UInt(memStartBlock + 12)),
|
|
GetPrefetch(client_xact_id = UInt(1), addr_block = UInt(memStartBlock + 12)))
|
|
|
|
io.mem.acquire.valid := sending
|
|
io.mem.acquire.bits := acq_bits(pf_cnt)
|
|
io.mem.grant.ready := Bool(true)
|
|
|
|
when (io.mem.grant.fire()) {
|
|
acked := acked | UIntToOH(io.mem.grant.bits.client_xact_id)
|
|
}
|
|
|
|
when (!sending && io.start) { sending := Bool(true) }
|
|
when (sending && pf_done) { sending := Bool(false) }
|
|
|
|
io.finished := acked.andR
|
|
io.errored := Bool(false)
|
|
}
|
|
|
|
/* Test that a writeback will occur by writing nWays + 1 blocks to the same
|
|
* set. This assumes that there is only a single cache bank. If we want to
|
|
* test multibank configurations, we'll have to think of some other way to
|
|
* determine which banks are conflicting */
|
|
class WritebackRegression(nSets: Int, nWays: Int)(implicit p: Parameters) extends Regression()(p) {
|
|
disableCache()
|
|
|
|
val addr_blocks = Vec.tabulate(nWays + 1) { i => UInt(memStartBlock + i * nSets) }
|
|
val data = Vec.tabulate(nWays + 1) { i => UInt((i + 1) * 1423) }
|
|
|
|
val (put_beat, put_done) = Counter(
|
|
io.mem.acquire.fire() && io.mem.acquire.bits.hasData(), tlDataBeats)
|
|
val (get_beat, get_done) = Counter(
|
|
io.mem.grant.fire() && io.mem.grant.bits.hasData(), tlDataBeats)
|
|
val (put_cnt, _) = Counter(put_done, nWays + 1)
|
|
val (get_cnt, _) = Counter(
|
|
io.mem.acquire.fire() && !io.mem.acquire.bits.hasData(), nWays + 1)
|
|
val (ack_cnt, ack_done) = Counter(
|
|
io.mem.grant.fire() && !io.mem.grant.bits.hasData() || get_done, nWays + 1)
|
|
|
|
val s_idle :: s_put :: s_get :: s_done :: Nil = Enum(Bits(), 4)
|
|
val state = Reg(init = s_idle)
|
|
val sending = Reg(init = Bool(false))
|
|
|
|
io.mem.acquire.valid := sending
|
|
io.mem.acquire.bits := Mux(state === s_put,
|
|
PutBlock(
|
|
client_xact_id = UInt(0),
|
|
addr_block = addr_blocks(put_cnt),
|
|
addr_beat = put_beat,
|
|
data = data(put_cnt)),
|
|
GetBlock(
|
|
client_xact_id = UInt(0),
|
|
addr_block = addr_blocks(get_cnt)))
|
|
io.mem.grant.ready := !sending
|
|
|
|
when (state === s_idle && io.start) { state := s_put; sending := Bool(true) }
|
|
when (put_done || state === s_get && io.mem.acquire.fire()) {
|
|
sending := Bool(false)
|
|
}
|
|
when (get_done && !ack_done || state === s_put && io.mem.grant.fire()) {
|
|
sending := Bool(true)
|
|
}
|
|
when (ack_done) { state := Mux(state === s_put, s_get, s_done) }
|
|
|
|
io.finished := (state === s_done)
|
|
|
|
val data_mismatch = io.mem.grant.fire() && io.mem.grant.bits.hasData() &&
|
|
io.mem.grant.bits.data =/= data(ack_cnt)
|
|
assert(!data_mismatch, "WritebackRegression: incorrect data")
|
|
io.errored := data_mismatch
|
|
}
|
|
|
|
class ReleaseRegression(nSets: Int, nWays: Int)(implicit p: Parameters) extends Regression()(p) {
|
|
disableMem()
|
|
|
|
val blockOffset = p(CacheBlockOffsetBits)
|
|
|
|
val startBlock = memStartBlock + 10
|
|
val addr_blocks = Vec.tabulate(nWays + 1) { i => UInt(startBlock + i * nSets) }
|
|
val data = Vec.tabulate(nWays + 1) { i => UInt((i + 1) * 1522) }
|
|
val (req_idx, req_done) = Counter(io.cache.req.fire(), nWays + 1)
|
|
val (resp_idx, resp_done) = Counter(io.cache.resp.valid, nWays + 1)
|
|
|
|
val sending = Reg(init = Bool(false))
|
|
val s_idle :: s_write :: s_read :: s_done :: Nil = Enum(Bits(), 4)
|
|
val state = Reg(init = s_idle)
|
|
|
|
io.cache.req.valid := sending && state.isOneOf(s_write, s_read)
|
|
io.cache.req.bits.addr := Cat(addr_blocks(req_idx), UInt(0, blockOffset))
|
|
io.cache.req.bits.typ := MT_D
|
|
io.cache.req.bits.cmd := Mux(state === s_write, M_XWR, M_XRD)
|
|
io.cache.req.bits.tag := UInt(0)
|
|
io.cache.req.bits.data := data(req_idx)
|
|
io.cache.req.bits.phys := Bool(true)
|
|
io.cache.invalidate_lr := Bool(false)
|
|
|
|
when (state === s_idle && io.start) {
|
|
sending := Bool(true)
|
|
state := s_write
|
|
}
|
|
|
|
when (resp_done) { state := Mux(state === s_write, s_read, s_done) }
|
|
when (io.cache.req.fire()) { sending := Bool(false) }
|
|
when (io.cache.resp.valid) { sending := Bool(true) }
|
|
|
|
io.finished := (state === s_done)
|
|
|
|
val data_mismatch = io.cache.resp.valid && io.cache.resp.bits.has_data &&
|
|
io.cache.resp.bits.data =/= data(resp_idx)
|
|
assert(!data_mismatch, "ReleaseRegression: data mismatch")
|
|
io.errored := data_mismatch
|
|
}
|
|
|
|
class PutBeforePutBlockRegression(implicit p: Parameters) extends Regression()(p) {
|
|
val (s_idle :: s_put :: s_putblock :: s_wait ::
|
|
s_finished :: Nil) = Enum(Bits(), 5)
|
|
val state = Reg(init = s_idle)
|
|
|
|
disableCache()
|
|
|
|
val (put_block_beat, put_block_done) = Counter(
|
|
state === s_putblock && io.mem.acquire.ready, tlDataBeats)
|
|
|
|
val put_acquire = Put(
|
|
client_xact_id = UInt(0),
|
|
addr_block = UInt(memStartBlock),
|
|
addr_beat = UInt(0),
|
|
data = UInt(0),
|
|
wmask = Some(UInt((1 << 8) - 1)))
|
|
|
|
val put_block_acquire = PutBlock(
|
|
client_xact_id = UInt(1),
|
|
addr_block = UInt(memStartBlock + 1),
|
|
addr_beat = put_block_beat,
|
|
data = UInt(0))
|
|
|
|
val put_acked = Reg(init = UInt(0, 2))
|
|
|
|
val (ack_cnt, all_acked) = Counter(io.mem.grant.fire(), 2)
|
|
|
|
io.mem.acquire.valid := state.isOneOf(s_put, s_putblock)
|
|
io.mem.acquire.bits := Mux(state === s_put, put_acquire, put_block_acquire)
|
|
io.mem.grant.ready := (state === s_wait)
|
|
|
|
when (state === s_idle && io.start) { state := s_put }
|
|
when (state === s_put && io.mem.acquire.ready) { state := s_putblock }
|
|
when (put_block_done) { state := s_wait }
|
|
when (all_acked) { state := s_finished }
|
|
|
|
io.finished := (state === s_finished)
|
|
io.errored := Bool(false)
|
|
}
|
|
|
|
/**
|
|
* Make sure that multiple gets to the same line and beat are merged
|
|
* correctly, even if it is a cache miss.
|
|
*/
|
|
class MergedGetRegression(nSets: Int, nWays: Int)(implicit p: Parameters) extends Regression()(p) {
|
|
disableCache()
|
|
|
|
val (s_idle :: s_put :: s_get :: s_done :: Nil) = Enum(Bits(), 4)
|
|
val state = Reg(init = s_idle)
|
|
|
|
// Write NWays + 1 different conflicting lines to force an eviction of the first line
|
|
val (put_acq_cnt, put_acq_done) = Counter(state === s_put && io.mem.acquire.fire(), nWays + 1)
|
|
val (put_gnt_cnt, put_gnt_done) = Counter(state === s_put && io.mem.grant.fire(), nWays + 1)
|
|
val put_addr = UInt(memStartBlock) + Cat(put_acq_cnt, UInt(0, log2Up(nSets)))
|
|
|
|
val (get_acq_cnt, get_acq_done) = Counter(state === s_get && io.mem.acquire.fire(), 2)
|
|
val (get_gnt_cnt, get_gnt_done) = Counter(state === s_get && io.mem.grant.fire(), 2)
|
|
val sending = Reg(init = Bool(false))
|
|
|
|
when (state === s_idle && io.start) { state := s_put; sending := Bool(true) }
|
|
when (state === s_put) {
|
|
when (io.mem.acquire.fire()) { sending := Bool(false) }
|
|
when (io.mem.grant.fire()) { sending := Bool(true) }
|
|
when (put_gnt_done) { state := s_get }
|
|
}
|
|
when (state === s_get) {
|
|
when (get_acq_done) { sending := Bool(false) }
|
|
when (get_gnt_done) { state := s_done }
|
|
}
|
|
|
|
io.mem.acquire.valid := sending
|
|
io.mem.acquire.bits := Mux(state === s_put,
|
|
Put(
|
|
client_xact_id = UInt(0),
|
|
addr_block = put_addr,
|
|
addr_beat = UInt(3),
|
|
data = UInt("hdabb9321")),
|
|
Get(
|
|
client_xact_id = get_acq_cnt,
|
|
addr_block = UInt(memStartBlock),
|
|
addr_beat = UInt(3)))
|
|
io.mem.grant.ready := !sending
|
|
|
|
val data_mismatch = io.mem.grant.valid && io.mem.grant.bits.hasData() &&
|
|
io.mem.grant.bits.data =/= UInt("hdabb9321")
|
|
assert(!data_mismatch, "RepeatedGetRegression: wrong data back")
|
|
|
|
io.finished := state === s_done
|
|
io.errored := data_mismatch
|
|
}
|
|
|
|
/**
|
|
* Make sure that multiple puts to the same line and beat are merged
|
|
* correctly, even if there is a release from the L1
|
|
*/
|
|
class MergedPutRegression(implicit p: Parameters) extends Regression()(p)
|
|
with HasTileLinkParameters {
|
|
val (s_idle :: s_cache_req :: s_cache_wait ::
|
|
s_put :: s_get :: s_done :: Nil) = Enum(Bits(), 6)
|
|
val state = Reg(init = s_idle)
|
|
|
|
io.cache.req.valid := (state === s_cache_req)
|
|
io.cache.req.bits.cmd := M_XWR
|
|
io.cache.req.bits.typ := MT_D
|
|
io.cache.req.bits.addr := UInt(memStart)
|
|
io.cache.req.bits.data := UInt(1)
|
|
io.cache.req.bits.tag := UInt(0)
|
|
|
|
val sending = Reg(init = Bool(false))
|
|
val delaying = Reg(init = Bool(false))
|
|
val (put_cnt, put_done) = Counter(io.mem.acquire.fire(), tlMaxClientXacts)
|
|
val (delay_cnt, delay_done) = Counter(delaying, 8)
|
|
val put_acked = Reg(UInt(width = tlMaxClientXacts), init = UInt(0))
|
|
|
|
io.mem.acquire.valid := sending && !delaying
|
|
io.mem.acquire.bits := Mux(state === s_put,
|
|
Put(
|
|
client_xact_id = put_cnt,
|
|
addr_block = UInt(memStartBlock),
|
|
addr_beat = UInt(0),
|
|
data = put_cnt + UInt(2)),
|
|
Get(
|
|
client_xact_id = UInt(0),
|
|
addr_block = UInt(memStartBlock),
|
|
addr_beat = UInt(0)))
|
|
io.mem.grant.ready := Bool(true)
|
|
|
|
when (state === s_idle && io.start) { state := s_cache_req }
|
|
when (io.cache.req.fire()) { state := s_cache_wait }
|
|
when (io.cache.resp.valid) { state := s_put; sending := Bool(true) }
|
|
|
|
when (io.mem.acquire.fire()) {
|
|
delaying := Bool(true)
|
|
when (put_done || state === s_get) { sending := Bool(false) }
|
|
}
|
|
when (delay_done) { delaying := Bool(false) }
|
|
|
|
when (io.mem.grant.fire()) {
|
|
when (state === s_put) {
|
|
put_acked := put_acked | UIntToOH(io.mem.grant.bits.client_xact_id)
|
|
}
|
|
when (state === s_get) { state := s_done }
|
|
}
|
|
|
|
when (state === s_put && put_acked.andR) {
|
|
state := s_get
|
|
sending := Bool(true)
|
|
}
|
|
|
|
val expected_data = UInt(2 + tlMaxClientXacts - 1)
|
|
val data_mismatch = io.mem.grant.valid && io.mem.grant.bits.hasData() &&
|
|
io.mem.grant.bits.data =/= expected_data
|
|
|
|
assert(!data_mismatch, "MergedPutRegression: data mismatch")
|
|
|
|
io.finished := (state === s_done)
|
|
io.errored := data_mismatch
|
|
}
|
|
|
|
class PutAfterReleaseRegression(implicit p: Parameters) extends Regression()(p) {
|
|
val (s_idle :: s_cache_req :: s_cache_resp ::
|
|
s_write_first_req :: s_delay :: s_write_remaining_req :: s_write_resp ::
|
|
s_read_req :: s_read_resp :: s_finished :: Nil) = Enum(Bits(), 10)
|
|
val state = Reg(init = s_idle)
|
|
|
|
val (delay_cnt, delay_done) = Counter(state === s_delay, 100)
|
|
val (write_cnt, write_done) = Counter(
|
|
io.mem.acquire.fire() && io.mem.acquire.bits.hasData(), tlDataBeats)
|
|
val (read_cnt, read_done) = Counter(
|
|
io.mem.grant.fire() && io.mem.grant.bits.hasData(), tlDataBeats)
|
|
|
|
when (state === s_idle && io.start) { state := s_cache_req }
|
|
when (io.cache.req.fire()) { state := s_cache_resp }
|
|
when (state === s_cache_resp && io.cache.resp.valid) { state := s_write_first_req }
|
|
when (state === s_write_first_req && io.mem.acquire.ready) { state := s_delay }
|
|
when (delay_done) { state := s_write_remaining_req }
|
|
when (write_done) { state := s_write_resp }
|
|
when (state === s_write_resp && io.mem.grant.valid) { state := s_read_req }
|
|
when (state === s_read_req && io.mem.acquire.ready) { state := s_read_resp }
|
|
when (read_done) { state := s_finished }
|
|
|
|
io.finished := state === s_finished
|
|
|
|
io.cache.req.valid := state === s_cache_req
|
|
io.cache.req.bits.cmd := M_XWR
|
|
io.cache.req.bits.addr := UInt(memStart)
|
|
io.cache.req.bits.typ := MT_D
|
|
io.cache.req.bits.tag := UInt(0)
|
|
io.cache.req.bits.data := UInt(0)
|
|
|
|
io.mem.acquire.valid := state.isOneOf(s_write_first_req, s_write_remaining_req, s_read_req)
|
|
io.mem.acquire.bits := Mux(state === s_read_req,
|
|
GetBlock(
|
|
client_xact_id = UInt(0),
|
|
addr_block = UInt(memStartBlock)),
|
|
PutBlock(
|
|
client_xact_id = UInt(0),
|
|
addr_block = UInt(memStartBlock),
|
|
addr_beat = write_cnt,
|
|
data = write_cnt + UInt(1)))
|
|
io.mem.grant.ready := state.isOneOf(s_write_resp, s_read_resp)
|
|
|
|
assert(!io.mem.grant.valid || !io.mem.grant.bits.hasData() ||
|
|
io.mem.grant.bits.data === read_cnt + UInt(1),
|
|
"PutAfterReleaseRegression: data mismatch")
|
|
}
|
|
|
|
object RegressionTests {
|
|
val l1sets = 16 // TODO
|
|
val l1ways = 1 // TODO
|
|
val l2sets = 32 // TODO
|
|
val l2ways = 2 // TODO
|
|
def cacheRegressions(implicit p: Parameters) = Seq(
|
|
Module(new PutBlockMergeRegression(l2sets)),
|
|
Module(new NoAllocPutHitRegression),
|
|
Module(new RepeatedNoAllocPutRegression),
|
|
Module(new WriteMaskedPutBlockRegression),
|
|
Module(new PrefetchHitRegression),
|
|
Module(new WritebackRegression(l2sets, l2ways)),
|
|
Module(new PutBeforePutBlockRegression),
|
|
Module(new MixedAllocPutRegression),
|
|
Module(new ReleaseRegression(l1sets, l1ways)),
|
|
Module(new MergedGetRegression(l2sets, l2ways)),
|
|
Module(new MergedPutRegression))
|
|
def broadcastRegressions(implicit p: Parameters) = Seq(
|
|
Module(new IOGetAfterPutBlockRegression),
|
|
Module(new WriteMaskedPutBlockRegression),
|
|
Module(new PutBeforePutBlockRegression),
|
|
Module(new ReleaseRegression(l1sets, l1ways)),
|
|
Module(new PutAfterReleaseRegression))
|
|
}
|
|
|
|
case object GroundTestRegressions extends Field[Parameters => Seq[Regression]]
|
|
|
|
class RegressionTest(implicit p: Parameters) extends GroundTest()(p) {
|
|
val regressions = p(GroundTestRegressions)(p)
|
|
val regress_idx = Reg(init = UInt(0, log2Up(regressions.size + 1)))
|
|
val cur_finished = Wire(init = Bool(false))
|
|
val all_done = (regress_idx === UInt(regressions.size))
|
|
val start = Reg(init = Bool(true))
|
|
|
|
// Some tests randomly backpressure grant; make this safe:
|
|
val grant = Queue(io.mem.head.grant, 16)
|
|
|
|
// default output values
|
|
io.mem.head.acquire.valid := Bool(false)
|
|
io.mem.head.acquire.bits := GetBlock(
|
|
client_xact_id = UInt(0),
|
|
addr_block = UInt(0))
|
|
grant.ready := Bool(false)
|
|
io.cache.head.req.valid := Bool(false)
|
|
io.cache.head.req.bits.addr := UInt(0)
|
|
io.cache.head.req.bits.typ := UInt(log2Ceil(64 / 8))
|
|
io.cache.head.req.bits.cmd := M_XRD
|
|
io.cache.head.req.bits.tag := UInt(0)
|
|
io.cache.head.req.bits.phys := Bool(true)
|
|
io.cache.head.req.bits.data := UInt(0)
|
|
io.cache.head.invalidate_lr := Bool(false)
|
|
|
|
regressions.zipWithIndex.foreach { case (regress, i) =>
|
|
val me = regress_idx === UInt(i)
|
|
regress.io.start := me && start
|
|
regress.io.mem.acquire.ready := io.mem.head.acquire.ready && me
|
|
regress.io.mem.grant.valid := grant.valid && me
|
|
regress.io.mem.grant.bits := grant.bits
|
|
regress.io.cache.req.ready := io.cache.head.req.ready && me
|
|
regress.io.cache.resp.valid := io.cache.head.resp.valid && me
|
|
regress.io.cache.resp.bits := io.cache.head.resp.bits
|
|
|
|
when (me) {
|
|
io.mem.head.acquire.valid := regress.io.mem.acquire.valid
|
|
io.mem.head.acquire.bits := regress.io.mem.acquire.bits
|
|
grant.ready := regress.io.mem.grant.ready
|
|
io.cache.head.req.valid := regress.io.cache.req.valid
|
|
io.cache.head.req.bits := regress.io.cache.req.bits
|
|
io.cache.head.invalidate_lr := regress.io.cache.invalidate_lr
|
|
io.status.error.valid := regress.io.errored
|
|
io.status.error.bits := UInt(i)
|
|
cur_finished := regress.io.finished
|
|
}
|
|
|
|
when (regress.io.start) {
|
|
printf(s"Starting regression ${regress.getClass.getSimpleName}\n")
|
|
}
|
|
}
|
|
|
|
when (cur_finished && !all_done) {
|
|
start := Bool(true)
|
|
regress_idx := regress_idx + UInt(1)
|
|
}
|
|
when (start) { start := Bool(false) }
|
|
|
|
val timeout = SimpleTimer(5000, start, cur_finished)
|
|
assert(!timeout, "Regression timed out")
|
|
|
|
io.status.finished := all_done
|
|
io.status.timeout.valid := timeout
|
|
io.status.timeout.bits := UInt(0)
|
|
|
|
assert(!(all_done && grant.valid),
|
|
"Getting grant after test completion")
|
|
|
|
when (all_done) {
|
|
io.status.error.valid := grant.valid
|
|
io.status.error.bits := UInt(regressions.size)
|
|
}
|
|
}
|