1
0

Merge pull request #1091 from freechipsproject/atomic-automata-errors

tilelink: AtomicAutomata should OR the Get error with the Put error
This commit is contained in:
Wesley W. Terpstra 2017-11-06 13:58:02 -08:00 committed by GitHub
commit 1f5fb5d643
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 22 deletions

View File

@ -273,12 +273,13 @@ class ICacheModule(outer: ICache) extends LazyModuleImp(outer)
val s1_scratchpad_hit = Mux(s1_slaveValid, lineInScratchpad(scratchpadLine(s1s3_slaveAddr)), addrInScratchpad(io.s1_paddr))
val s2_scratchpad_hit = RegEnable(s1_scratchpad_hit, s1_clk_en)
io.errors.correctable.foreach { c =>
c.valid := s2_valid && Mux(s2_scratchpad_hit, s2_data_decoded.correctable, s2_disparity)
c.bits := 0.U
c.valid := ((s2_valid || s2_slaveValid) && (s2_scratchpad_hit && s2_data_decoded.correctable)) || (s2_valid && !s2_scratchpad_hit && s2_disparity)
c.bits := Mux(s2_scratchpad_hit, scratchpadBase.get + s2_scratchpad_word_addr, 0.U)
}
io.errors.uncorrectable.foreach { u =>
u.valid := s2_valid && s2_scratchpad_hit && s2_data_decoded.uncorrectable
u.bits := scratchpadBase.get + s2_scratchpad_word_addr
u.valid := (s2_valid || s2_slaveValid) && (s2_scratchpad_hit && s2_data_decoded.uncorrectable)
// the Mux is not necessary, but saves HW in BusErrorUnit because it matches c.bits above
u.bits := Mux(s2_scratchpad_hit, scratchpadBase.get + s2_scratchpad_word_addr, 0.U)
}
tl_in.map { tl =>
@ -312,6 +313,7 @@ class ICacheModule(outer: ICache) extends LazyModuleImp(outer)
}
respValid := s2_slaveValid || (respValid && !tl.d.ready)
val respError = RegEnable(s2_scratchpad_hit && s2_data_decoded.uncorrectable, s2_slaveValid)
when (s2_slaveValid) {
when (edge_in.get.hasData(s1_a) || s2_data_decoded.correctable) { s3_slaveValid := true }
def byteEn(i: Int) = !(edge_in.get.hasData(s1_a) && s1_a.mask(i))
@ -323,6 +325,7 @@ class ICacheModule(outer: ICache) extends LazyModuleImp(outer)
edge_in.get.AccessAck(s1_a),
edge_in.get.AccessAck(s1_a, UInt(0)))
tl.d.bits.data := s1s3_slaveData
tl.d.bits.error := respError
// Tie off unused channels
tl.b.valid := false

View File

@ -188,6 +188,7 @@ class TLAtomicAutomata(logical: Boolean = true, arithmetic: Boolean = true, conc
val d_cam_sel_raw = cam_a.map(_.bits.source === in.d.bits.source)
val d_cam_sel_match = (d_cam_sel_raw zip cam_dmatch) map { case (a,b) => a&&b }
val d_cam_data = Mux1H(d_cam_sel_match, cam_d.map(_.data))
val d_cam_error = Mux1H(d_cam_sel_match, cam_d.map(_.error))
val d_cam_sel_bypass = if (edgeOut.manager.minLatency > 0) Bool(false) else
out.d.bits.source === in.a.bits.source && in.a.valid && !a_isSupported
val d_cam_sel = (a_cam_sel_free zip d_cam_sel_match) map { case (a,d) => Mux(d_cam_sel_bypass, a, d) }
@ -199,6 +200,7 @@ class TLAtomicAutomata(logical: Boolean = true, arithmetic: Boolean = true, conc
(d_cam_sel zip cam_d) foreach { case (en, r) =>
when (en && d_ackd) {
r.data := out.d.bits.data
r.error := out.d.bits.error
}
}
(d_cam_sel zip cam_s) foreach { case (en, r) =>
@ -219,6 +221,7 @@ class TLAtomicAutomata(logical: Boolean = true, arithmetic: Boolean = true, conc
when (d_replace) { // minimal muxes
in.d.bits.opcode := TLMessages.AccessAckData
in.d.bits.data := d_cam_data
in.d.bits.error := d_cam_error || out.d.bits.error
}
} else {
out.a.valid := in.a.valid
@ -270,25 +273,36 @@ object TLAtomicAutomata
val lut = UInt(width = 4)
}
class CAM_D(params: CAMParams) extends GenericParameterizedBundle(params) {
val data = UInt(width = params.a.dataBits)
val data = UInt(width = params.a.dataBits)
val error = Bool()
}
}
/** Synthesizeable unit tests */
import freechips.rocketchip.unittest._
//TODO ensure handler will pass through operations to clients that can handle them themselves
class TLRAMAtomicAutomata(txns: Int)(implicit p: Parameters) extends LazyModule {
val fuzz = LazyModule(new TLFuzzer(txns))
val model = LazyModule(new TLRAMModel("AtomicAutomata"))
val ram = LazyModule(new TLRAM(AddressSet(0x0, 0x3ff)))
// Confirm that the AtomicAutomata combines read + write errors
import TLMessages._
val test = new RequestPattern({a: TLBundleA =>
val doesA = a.opcode === ArithmeticData || a.opcode === LogicalData
val doesR = a.opcode === Get || doesA
val doesW = a.opcode === PutFullData || a.opcode === PutPartialData || doesA
(doesR && RequestPattern.overlaps(Seq(AddressSet(0x08, ~0x08)))(a)) ||
(doesW && RequestPattern.overlaps(Seq(AddressSet(0x10, ~0x10)))(a))
})
(ram.node
:= TLErrorEvaluator(test)
:= TLFragmenter(4, 256)
:= TLDelayer(0.1)
:= TLAtomicAutomata()
:= TLDelayer(0.1)
:= TLErrorEvaluator(test, testOn=true, testOff=true)
:= model.node
:= fuzz.node)

View File

@ -7,34 +7,50 @@ import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.util._
// Check if a request satisfies some interesting property
class RequestPattern(test: TLBundleA => Bool)
{
def apply(a: TLBundleA) = test(a)
}
object RequestPattern
{
// Does the request address range overlap a particular pattern of addresses?
def overlaps(pattern: Seq[AddressSet])(a: TLBundleA) = {
val amask = UIntToOH1(a.size, a.params.addressBits)
val abase = a.address
pattern.map { case p =>
val pbase = UInt(p.base)
val pmask = UInt(p.mask & ((BigInt(1) << a.params.addressBits) - 1))
(amask | pmask | ~(abase ^ pbase)).andR
}.reduce(_ || _)
}
implicit def apply(pattern: Seq[AddressSet]): RequestPattern = new RequestPattern(overlaps(pattern) _)
}
// An ErrorEvaluator is used for test harnesses.
// It creates errors in transactions which overlap an address in pattern.
// It creates errors in transactions which match the provided test function.
// If testOn is true, it will assert fail if these transactions do not already error.
// If testOff is true, it will assert fail if these transactions otherwise error.
// This helps when building unit tests to confirm that errors are propagated correctly.
class TLErrorEvaluator(pattern: Seq[AddressSet], testOn: Boolean, testOff: Boolean)(implicit p: Parameters) extends LazyModule
class TLErrorEvaluator(test: RequestPattern, testOn: Boolean, testOff: Boolean)(implicit p: Parameters) extends LazyModule
{
val node = TLAdapterNode()
lazy val module = new LazyModuleImp(this) {
(node.in zip node.out) foreach { case ((in, edgeIn), (out, edgeOut)) =>
out <> in
// Does the request overlap the pattern ?
val abase = in.a.bits.address
val amask = UIntToOH1(in.a.bits.size, log2Up(edgeIn.maxTransfer))
val overlaps = pattern.map { case a =>
val pbase = UInt(a.base)
val pmask = UInt(a.mask & ((BigInt(1) << edgeIn.bundle.addressBits) - 1))
(amask | pmask | ~(abase ^ pbase)).andR
}.reduce(_ || _)
// Should the request receive an error ?
val inject_map = Mem(edgeIn.client.endSourceId, Bool())
val inject_now = test(in.a.bits)
val (d_first, d_last, _) = edgeOut.firstlast(out.d)
val inject = Mem(edgeIn.client.endSourceId, Bool())
when (in.a.fire()) { inject.write(in.a.bits.source, overlaps) }
when (in.a.fire()) { inject_map.write(in.a.bits.source, inject_now) }
val bypass = in.a.fire() && in.a.bits.source === in.d.bits.source
val d_inject = Mux(bypass, overlaps, inject.read(in.d.bits.source)) holdUnless d_first
val d_inject = Mux(bypass, inject_now, inject_map.read(in.d.bits.source)) holdUnless d_first
in.d.bits.error := out.d.bits.error || (d_last && d_inject)
assert (Bool(!testOn) || !out.d.fire() || !d_last || !d_inject || out.d.bits.error, "Error flag was not set!")
@ -45,6 +61,6 @@ class TLErrorEvaluator(pattern: Seq[AddressSet], testOn: Boolean, testOff: Boole
object TLErrorEvaluator
{
def apply(pattern: Seq[AddressSet], testOn: Boolean = false, testOff: Boolean = false)(implicit p: Parameters): TLNode =
LazyModule(new TLErrorEvaluator(pattern, testOn, testOff)).node
def apply(test: RequestPattern, testOn: Boolean = false, testOff: Boolean = false)(implicit p: Parameters): TLNode =
LazyModule(new TLErrorEvaluator(test, testOn, testOff)).node
}