diff --git a/src/main/scala/rocket/ICache.scala b/src/main/scala/rocket/ICache.scala index 60db666b..872ebc01 100644 --- a/src/main/scala/rocket/ICache.scala +++ b/src/main/scala/rocket/ICache.scala @@ -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 diff --git a/src/main/scala/tilelink/AtomicAutomata.scala b/src/main/scala/tilelink/AtomicAutomata.scala index 42a5d83f..b0200024 100644 --- a/src/main/scala/tilelink/AtomicAutomata.scala +++ b/src/main/scala/tilelink/AtomicAutomata.scala @@ -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) diff --git a/src/main/scala/tilelink/ErrorEvaluator.scala b/src/main/scala/tilelink/ErrorEvaluator.scala index 2a1bdb4b..1bcd71a0 100644 --- a/src/main/scala/tilelink/ErrorEvaluator.scala +++ b/src/main/scala/tilelink/ErrorEvaluator.scala @@ -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 }