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:
commit
1f5fb5d643
@ -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 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)
|
val s2_scratchpad_hit = RegEnable(s1_scratchpad_hit, s1_clk_en)
|
||||||
io.errors.correctable.foreach { c =>
|
io.errors.correctable.foreach { c =>
|
||||||
c.valid := s2_valid && Mux(s2_scratchpad_hit, s2_data_decoded.correctable, s2_disparity)
|
c.valid := ((s2_valid || s2_slaveValid) && (s2_scratchpad_hit && s2_data_decoded.correctable)) || (s2_valid && !s2_scratchpad_hit && s2_disparity)
|
||||||
c.bits := 0.U
|
c.bits := Mux(s2_scratchpad_hit, scratchpadBase.get + s2_scratchpad_word_addr, 0.U)
|
||||||
}
|
}
|
||||||
io.errors.uncorrectable.foreach { u =>
|
io.errors.uncorrectable.foreach { u =>
|
||||||
u.valid := s2_valid && s2_scratchpad_hit && s2_data_decoded.uncorrectable
|
u.valid := (s2_valid || s2_slaveValid) && (s2_scratchpad_hit && s2_data_decoded.uncorrectable)
|
||||||
u.bits := scratchpadBase.get + s2_scratchpad_word_addr
|
// 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 =>
|
tl_in.map { tl =>
|
||||||
@ -312,6 +313,7 @@ class ICacheModule(outer: ICache) extends LazyModuleImp(outer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
respValid := s2_slaveValid || (respValid && !tl.d.ready)
|
respValid := s2_slaveValid || (respValid && !tl.d.ready)
|
||||||
|
val respError = RegEnable(s2_scratchpad_hit && s2_data_decoded.uncorrectable, s2_slaveValid)
|
||||||
when (s2_slaveValid) {
|
when (s2_slaveValid) {
|
||||||
when (edge_in.get.hasData(s1_a) || s2_data_decoded.correctable) { s3_slaveValid := true }
|
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))
|
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),
|
||||||
edge_in.get.AccessAck(s1_a, UInt(0)))
|
edge_in.get.AccessAck(s1_a, UInt(0)))
|
||||||
tl.d.bits.data := s1s3_slaveData
|
tl.d.bits.data := s1s3_slaveData
|
||||||
|
tl.d.bits.error := respError
|
||||||
|
|
||||||
// Tie off unused channels
|
// Tie off unused channels
|
||||||
tl.b.valid := false
|
tl.b.valid := false
|
||||||
|
@ -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_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_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_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
|
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
|
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) }
|
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) =>
|
(d_cam_sel zip cam_d) foreach { case (en, r) =>
|
||||||
when (en && d_ackd) {
|
when (en && d_ackd) {
|
||||||
r.data := out.d.bits.data
|
r.data := out.d.bits.data
|
||||||
|
r.error := out.d.bits.error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(d_cam_sel zip cam_s) foreach { case (en, r) =>
|
(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
|
when (d_replace) { // minimal muxes
|
||||||
in.d.bits.opcode := TLMessages.AccessAckData
|
in.d.bits.opcode := TLMessages.AccessAckData
|
||||||
in.d.bits.data := d_cam_data
|
in.d.bits.data := d_cam_data
|
||||||
|
in.d.bits.error := d_cam_error || out.d.bits.error
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
out.a.valid := in.a.valid
|
out.a.valid := in.a.valid
|
||||||
@ -271,24 +274,35 @@ object TLAtomicAutomata
|
|||||||
}
|
}
|
||||||
class CAM_D(params: CAMParams) extends GenericParameterizedBundle(params) {
|
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 */
|
/** Synthesizeable unit tests */
|
||||||
import freechips.rocketchip.unittest._
|
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 {
|
class TLRAMAtomicAutomata(txns: Int)(implicit p: Parameters) extends LazyModule {
|
||||||
val fuzz = LazyModule(new TLFuzzer(txns))
|
val fuzz = LazyModule(new TLFuzzer(txns))
|
||||||
val model = LazyModule(new TLRAMModel("AtomicAutomata"))
|
val model = LazyModule(new TLRAMModel("AtomicAutomata"))
|
||||||
val ram = LazyModule(new TLRAM(AddressSet(0x0, 0x3ff)))
|
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
|
(ram.node
|
||||||
|
:= TLErrorEvaluator(test)
|
||||||
:= TLFragmenter(4, 256)
|
:= TLFragmenter(4, 256)
|
||||||
:= TLDelayer(0.1)
|
:= TLDelayer(0.1)
|
||||||
:= TLAtomicAutomata()
|
:= TLAtomicAutomata()
|
||||||
:= TLDelayer(0.1)
|
:= TLDelayer(0.1)
|
||||||
|
:= TLErrorEvaluator(test, testOn=true, testOff=true)
|
||||||
:= model.node
|
:= model.node
|
||||||
:= fuzz.node)
|
:= fuzz.node)
|
||||||
|
|
||||||
|
@ -7,34 +7,50 @@ import freechips.rocketchip.config.Parameters
|
|||||||
import freechips.rocketchip.diplomacy._
|
import freechips.rocketchip.diplomacy._
|
||||||
import freechips.rocketchip.util._
|
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.
|
// 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 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.
|
// 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.
|
// 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()
|
val node = TLAdapterNode()
|
||||||
lazy val module = new LazyModuleImp(this) {
|
lazy val module = new LazyModuleImp(this) {
|
||||||
(node.in zip node.out) foreach { case ((in, edgeIn), (out, edgeOut)) =>
|
(node.in zip node.out) foreach { case ((in, edgeIn), (out, edgeOut)) =>
|
||||||
out <> in
|
out <> in
|
||||||
|
|
||||||
// Does the request overlap the pattern ?
|
// Should the request receive an error ?
|
||||||
val abase = in.a.bits.address
|
val inject_map = Mem(edgeIn.client.endSourceId, Bool())
|
||||||
val amask = UIntToOH1(in.a.bits.size, log2Up(edgeIn.maxTransfer))
|
val inject_now = test(in.a.bits)
|
||||||
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(_ || _)
|
|
||||||
|
|
||||||
val (d_first, d_last, _) = edgeOut.firstlast(out.d)
|
val (d_first, d_last, _) = edgeOut.firstlast(out.d)
|
||||||
|
|
||||||
val inject = Mem(edgeIn.client.endSourceId, Bool())
|
when (in.a.fire()) { inject_map.write(in.a.bits.source, inject_now) }
|
||||||
when (in.a.fire()) { inject.write(in.a.bits.source, overlaps) }
|
|
||||||
|
|
||||||
val bypass = in.a.fire() && in.a.bits.source === in.d.bits.source
|
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)
|
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!")
|
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
|
object TLErrorEvaluator
|
||||||
{
|
{
|
||||||
def apply(pattern: Seq[AddressSet], testOn: Boolean = false, testOff: Boolean = false)(implicit p: Parameters): TLNode =
|
def apply(test: RequestPattern, testOn: Boolean = false, testOff: Boolean = false)(implicit p: Parameters): TLNode =
|
||||||
LazyModule(new TLErrorEvaluator(pattern, testOn, testOff)).node
|
LazyModule(new TLErrorEvaluator(test, testOn, testOff)).node
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user