From 13d0bf68085644c99bc1d8b641b2caca5751c9e9 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 30 Oct 2017 11:25:59 -0700 Subject: [PATCH 01/11] tilelink: Monitor now enforces spec-defined error rules --- src/main/scala/tilelink/Monitor.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/scala/tilelink/Monitor.scala b/src/main/scala/tilelink/Monitor.scala index 9b258684..d15c5bbe 100644 --- a/src/main/scala/tilelink/Monitor.scala +++ b/src/main/scala/tilelink/Monitor.scala @@ -364,12 +364,14 @@ class TLMonitor(args: TLMonitorArgs) extends TLMonitorBase(args) val size = Reg(UInt()) val source = Reg(UInt()) val address = Reg(UInt()) + val error = RegEnable(c.bits.error, c.fire()) when (c.valid && !c_first) { assert (c.bits.opcode === opcode, "'C' channel opcode changed within multibeat operation" + extra) assert (c.bits.param === param, "'C' channel param changed within multibeat operation" + extra) assert (c.bits.size === size, "'C' channel size changed within multibeat operation" + extra) assert (c.bits.source === source, "'C' channel source changed within multibeat operation" + extra) assert (c.bits.address=== address,"'C' channel address changed with multibeat operation" + extra) + assert (c.bits.error || !error, "'C' channel burst lowered error" + extra) } when (c.fire() && c_first) { opcode := c.bits.opcode @@ -387,12 +389,14 @@ class TLMonitor(args: TLMonitorArgs) extends TLMonitorBase(args) val size = Reg(UInt()) val source = Reg(UInt()) val sink = Reg(UInt()) + val error = RegEnable(d.bits.error, d.fire()) when (d.valid && !d_first) { assert (d.bits.opcode === opcode, "'D' channel opcode changed within multibeat operation" + extra) assert (d.bits.param === param, "'D' channel param changed within multibeat operation" + extra) assert (d.bits.size === size, "'D' channel size changed within multibeat operation" + extra) assert (d.bits.source === source, "'D' channel source changed within multibeat operation" + extra) assert (d.bits.sink === sink, "'D' channel sink changed with multibeat operation" + extra) + assert (d.bits.error || !error, "'D' channel burst lowered error" + extra) } when (d.fire() && d_first) { opcode := d.bits.opcode From 0992a459bef20d45199bd9e935ad076e3ae5f4e9 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 30 Oct 2017 12:22:47 -0700 Subject: [PATCH 02/11] tilelink: Fragmenter should combine errors --- src/main/scala/tilelink/Fragmenter.scala | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/scala/tilelink/Fragmenter.scala b/src/main/scala/tilelink/Fragmenter.scala index b15585ec..d79af257 100644 --- a/src/main/scala/tilelink/Fragmenter.scala +++ b/src/main/scala/tilelink/Fragmenter.scala @@ -186,17 +186,18 @@ class TLFragmenter(val minSize: Int, val maxSize: Int, val alwaysMin: Boolean = in.d.bits.source := out.d.bits.source >> addedBits in.d.bits.size := Mux(dFirst, dFirst_size, dOrig) + // The specification requires that error transition LOW=>HIGH only once per burst. + // Since we fragmented a big burst into mulitple little bursts, we need to OR them. + val r_error = RegInit(Bool(false)) + val d_error = r_error || out.d.bits.error + when (out.d.fire()) { r_error := !dLast && d_error } + in.d.bits.error := d_error + if (earlyAck) { // If you do early Ack, errors may not be dropped - // ... which roughly means: Puts may not fail - assert (!out.d.valid || !out.d.bits.error || !drop) - in.d.bits.error := out.d.bits.error - } else { - // Combine the error flag - val r_error = RegInit(Bool(false)) - val d_error = r_error | out.d.bits.error - when (out.d.fire()) { r_error := Mux(drop, d_error, UInt(0)) } - in.d.bits.error := d_error + // ... which roughly means: Puts must error on the first burst + // (dPut && !dFirst) => d.error === r_error + assert (!out.d.valid || !dHasData || dFirst || out.d.bits.error === r_error, "Slave device error behaviour unsuitable for earlyAck") } // What maximum transfer sizes do downstream devices support? From 4c9d9c63311f05f702fe9d5ca9b5d9a56cf40b16 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 30 Oct 2017 13:19:58 -0700 Subject: [PATCH 03/11] tilelink: optimize WidthWidget error circuit to nothing --- src/main/scala/tilelink/WidthWidget.scala | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/main/scala/tilelink/WidthWidget.scala b/src/main/scala/tilelink/WidthWidget.scala index 3aca8dab..1f3b9dd1 100644 --- a/src/main/scala/tilelink/WidthWidget.scala +++ b/src/main/scala/tilelink/WidthWidget.scala @@ -49,13 +49,6 @@ class TLWidthWidget(innerBeatBytes: Int)(implicit p: Parameters) extends LazyMod Cat(mdata.reverse) } - def reduce(i: Bool): Bool = { - val state = Reg(Bool()) - val next = i || (!first && state) - when (in.fire()) { state := next } - next - } - in.ready := out.ready || !last out.valid := in.valid && last out.bits := in.bits @@ -66,8 +59,8 @@ class TLWidthWidget(innerBeatBytes: Int)(implicit p: Parameters) extends LazyMod (out.bits, in.bits) match { case (o: TLBundleA, i: TLBundleA) => o.mask := edgeOut.mask(o.address, o.size) & Mux(hasData, helper(i.mask), ~UInt(0, width=outBytes)) case (o: TLBundleB, i: TLBundleB) => o.mask := edgeOut.mask(o.address, o.size) & Mux(hasData, helper(i.mask), ~UInt(0, width=outBytes)) - case (o: TLBundleC, i: TLBundleC) => o.error := reduce(i.error) - case (o: TLBundleD, i: TLBundleD) => o.error := reduce(i.error) + case (o: TLBundleC, i: TLBundleC) => () // monotone errors: last beat's error taken combinationally is ok + case (o: TLBundleD, i: TLBundleD) => () case _ => require(false, "Impossible bundle combination in WidthWidget") } } @@ -119,8 +112,8 @@ class TLWidthWidget(innerBeatBytes: Int)(implicit p: Parameters) extends LazyMod (out.bits, in.bits) match { case (o: TLBundleA, i: TLBundleA) => o.mask := helper(i.mask, 1) case (o: TLBundleB, i: TLBundleB) => o.mask := helper(i.mask, 1) - case (o: TLBundleC, i: TLBundleC) => () // error handled by bulk connect - case (o: TLBundleD, i: TLBundleD) => () // error handled by bulk connect + case (o: TLBundleC, i: TLBundleC) => () // monotone errors: replicating error to all beats is ok + case (o: TLBundleD, i: TLBundleD) => () case _ => require(false, "Impossbile bundle combination in WidthWidget") } From d6f16128122a509227d0445bacb0f9bbdf223e99 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 30 Oct 2017 13:31:43 -0700 Subject: [PATCH 04/11] tilelink: ToAHB should make read errors sticky as well --- src/main/scala/tilelink/ToAHB.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/tilelink/ToAHB.scala b/src/main/scala/tilelink/ToAHB.scala index 5c4a2608..b935cbbc 100644 --- a/src/main/scala/tilelink/ToAHB.scala +++ b/src/main/scala/tilelink/ToAHB.scala @@ -168,7 +168,7 @@ class TLToAHB(val aFlow: Boolean = false)(implicit p: Parameters) extends LazyMo when (out.hreadyout) { d_valid := send.send && (send.last || !send.write) - when (out.hresp) { d_error := d_write } + when (out.hresp) { d_error := Bool(true) } when (send.first) { d_error := Bool(false) } } From 2d12ddb4edb34fcffa98e7a13f1308a0c7c8dfdf Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 30 Oct 2017 13:35:56 -0700 Subject: [PATCH 05/11] tilelink: ToAXI4 makes R channel errors sticky --- src/main/scala/tilelink/ToAXI4.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/scala/tilelink/ToAXI4.scala b/src/main/scala/tilelink/ToAXI4.scala index da43420b..1f5be336 100644 --- a/src/main/scala/tilelink/ToAXI4.scala +++ b/src/main/scala/tilelink/ToAXI4.scala @@ -176,7 +176,10 @@ class TLToAXI4(val combinational: Boolean = true, val adapterName: Option[String val r_error = out.r.bits.resp =/= AXI4Parameters.RESP_OKAY val b_error = out.b.bits.resp =/= AXI4Parameters.RESP_OKAY - val r_d = edgeIn.AccessAck(r_source, r_size, UInt(0), r_error) + val reg_error = RegInit(Bool(false)) + when (out.r.fire()) { reg_error := !out.r.bits.last && (reg_error || r_error) } + + val r_d = edgeIn.AccessAck(r_source, r_size, UInt(0), reg_error || r_error) val b_d = edgeIn.AccessAck(b_source, b_size, b_error) in.d.bits := Mux(r_wins, r_d, b_d) From 0280a1f21811918d5bd0754387f254508c43eb3f Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 30 Oct 2017 17:05:09 -0700 Subject: [PATCH 06/11] tilelink: add the ErrorEvaluator, a test bench error helper --- src/main/scala/tilelink/ErrorEvaluator.scala | 50 ++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/main/scala/tilelink/ErrorEvaluator.scala diff --git a/src/main/scala/tilelink/ErrorEvaluator.scala b/src/main/scala/tilelink/ErrorEvaluator.scala new file mode 100644 index 00000000..2a1bdb4b --- /dev/null +++ b/src/main/scala/tilelink/ErrorEvaluator.scala @@ -0,0 +1,50 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.tilelink + +import Chisel._ +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.util._ + +// An ErrorEvaluator is used for test harnesses. +// It creates errors in transactions which overlap an address in pattern. +// 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 +{ + 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(_ || _) + + 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) } + + 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 + 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(!testOff) || !out.d.fire() || !d_last || d_inject || !out.d.bits.error, "Error flag was set!") + } + } +} + +object TLErrorEvaluator +{ + def apply(pattern: Seq[AddressSet], testOn: Boolean = false, testOff: Boolean = false)(implicit p: Parameters): TLNode = + LazyModule(new TLErrorEvaluator(pattern, testOn, testOff)).node +} From ec70e5fb02bbdabc1118057c93553412b20b0af4 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 30 Oct 2017 14:00:53 -0700 Subject: [PATCH 07/11] apb: inject fuzzy errors --- src/main/scala/amba/apb/SRAM.scala | 8 +++++--- src/main/scala/amba/apb/Test.scala | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/scala/amba/apb/SRAM.scala b/src/main/scala/amba/apb/SRAM.scala index 91e1f210..b9063195 100644 --- a/src/main/scala/amba/apb/SRAM.scala +++ b/src/main/scala/amba/apb/SRAM.scala @@ -12,7 +12,9 @@ class APBRAM( executable: Boolean = true, beatBytes: Int = 4, devName: Option[String] = None, - errors: Seq[AddressSet] = Nil) + errors: Seq[AddressSet] = Nil, + fuzzReady: Boolean = false, + fuzzError: Boolean = false) (implicit p: Parameters) extends DiplomaticSRAM(address, beatBytes, devName) { val node = APBSlaveNode(Seq(APBSlavePortParameters( @@ -37,8 +39,8 @@ class APBRAM( mem.write(paddr, Vec.tabulate(beatBytes) { i => in.pwdata(8*(i+1)-1, 8*i) }, in.pstrb.toBools) } - in.pready := Bool(true) - in.pslverr := RegNext(!legal) + in.pready := Bool(!fuzzReady) || LFSR16(!in.penable)(0) + in.pslverr := RegEnable(!legal, !in.penable) || (Bool(fuzzError) && LFSR16(Bool(true))(0)) in.prdata := mem.readAndHold(paddr, read).asUInt } } diff --git a/src/main/scala/amba/apb/Test.scala b/src/main/scala/amba/apb/Test.scala index 2d88c031..5c57191a 100644 --- a/src/main/scala/amba/apb/Test.scala +++ b/src/main/scala/amba/apb/Test.scala @@ -21,7 +21,7 @@ class APBFuzzBridge(aFlow: Boolean, txns: Int)(implicit p: Parameters) extends L val fuzz = LazyModule(new TLFuzzer(txns)) val model = LazyModule(new TLRAMModel("APBFuzzMaster")) val xbar = LazyModule(new APBFanout) - val ram = LazyModule(new APBRAM(AddressSet(0x0, 0xff))) + val ram = LazyModule(new APBRAM(AddressSet(0x0, 0xff), fuzzReady = true, fuzzError = true)) val gpio = LazyModule(new RRTest0(0x100)) ram.node := xbar.node @@ -31,6 +31,7 @@ class APBFuzzBridge(aFlow: Boolean, txns: Int)(implicit p: Parameters) extends L := TLDelayer(0.2) := TLBuffer(BufferParams.flow) := TLDelayer(0.2) + := TLFragmenter(4, 8) := model.node := fuzz.node) From e8ed450f13ac3e9f5141a7e33bf2d1740f180e98 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 30 Oct 2017 15:01:17 -0700 Subject: [PATCH 08/11] unit tests: do not use LFSR16 which has a common seed! We want each LFSR to generate independent noise. --- src/main/scala/amba/ahb/SRAM.scala | 3 ++- src/main/scala/amba/apb/SRAM.scala | 5 +++-- src/main/scala/tilelink/Delayer.scala | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/scala/amba/ahb/SRAM.scala b/src/main/scala/amba/ahb/SRAM.scala index 8c9fbab7..3a553766 100644 --- a/src/main/scala/amba/ahb/SRAM.scala +++ b/src/main/scala/amba/ahb/SRAM.scala @@ -6,6 +6,7 @@ import Chisel._ import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.util._ +import freechips.rocketchip.tilelink.LFSRNoiseMaker class AHBRAM( address: AddressSet, @@ -92,7 +93,7 @@ class AHBRAM( when (a_request) { d_request := Bool(true) } // Finally, the outputs - in.hreadyout := (if(fuzzHreadyout) { !d_request || LFSR16(Bool(true))(0) } else { Bool(true) }) + in.hreadyout := (if(fuzzHreadyout) { !d_request || LFSRNoiseMaker(1)(0) } else { Bool(true) }) in.hresp := Mux(d_legal || !in.hreadyout, AHBParameters.RESP_OKAY, AHBParameters.RESP_ERROR) in.hrdata := Mux(in.hreadyout, muxdata.asUInt, UInt(0)) } diff --git a/src/main/scala/amba/apb/SRAM.scala b/src/main/scala/amba/apb/SRAM.scala index b9063195..4ac832c3 100644 --- a/src/main/scala/amba/apb/SRAM.scala +++ b/src/main/scala/amba/apb/SRAM.scala @@ -6,6 +6,7 @@ import Chisel._ import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.util._ +import freechips.rocketchip.tilelink.LFSRNoiseMaker class APBRAM( address: AddressSet, @@ -39,8 +40,8 @@ class APBRAM( mem.write(paddr, Vec.tabulate(beatBytes) { i => in.pwdata(8*(i+1)-1, 8*i) }, in.pstrb.toBools) } - in.pready := Bool(!fuzzReady) || LFSR16(!in.penable)(0) - in.pslverr := RegEnable(!legal, !in.penable) || (Bool(fuzzError) && LFSR16(Bool(true))(0)) + in.pready := Bool(!fuzzReady) || LFSRNoiseMaker(1)(0) + in.pslverr := RegEnable(!legal, !in.penable) || (Bool(fuzzError) && LFSRNoiseMaker(1)(0)) in.prdata := mem.readAndHold(paddr, read).asUInt } } diff --git a/src/main/scala/tilelink/Delayer.scala b/src/main/scala/tilelink/Delayer.scala index 1f2d256c..11f75c05 100644 --- a/src/main/scala/tilelink/Delayer.scala +++ b/src/main/scala/tilelink/Delayer.scala @@ -14,7 +14,7 @@ class TLDelayer(q: Double)(implicit p: Parameters) extends LazyModule lazy val module = new LazyModuleImp(this) { def feed[T <: Data](sink: DecoupledIO[T], source: DecoupledIO[T], noise: T) { - val allow = UInt((q * 65535.0).toInt) <= LFSR16(source.valid) + val allow = UInt((q * 65535.0).toInt) <= LFSRNoiseMaker(16, source.valid) sink.valid := source.valid && allow source.ready := sink.ready && allow sink.bits := source.bits From 2912a76a2bcdc7bfd85746dc2a4d4bc7851c56a2 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 30 Oct 2017 13:53:08 -0700 Subject: [PATCH 09/11] axi4: inject fuzzy errors --- src/main/scala/amba/axi4/Test.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/scala/amba/axi4/Test.scala b/src/main/scala/amba/axi4/Test.scala index 63c82e28..04064bdb 100644 --- a/src/main/scala/amba/axi4/Test.scala +++ b/src/main/scala/amba/axi4/Test.scala @@ -63,6 +63,8 @@ class AXI4FullFuzzRAMTest(txns: Int = 5000, timeout: Int = 500000)(implicit p: P trait HasFuzzTarget { val fuzzAddr = AddressSet(0x0, 0xfff) + val pattern = Seq(AddressSet(0x8, ~0x808), // ie: 0x8-0xf, 0x18-0x1f, ... 0x7f8-0x7ff + AddressSet(0x900, ~0x900)) // ie: 0x900-0x9ff, 0xb00-0xbff, ... 0xf00-0xfff } class AXI4FuzzMaster(txns: Int)(implicit p: Parameters) extends LazyModule with HasFuzzTarget @@ -78,6 +80,7 @@ class AXI4FuzzMaster(txns: Int)(implicit p: Parameters) extends LazyModule with := TLDelayer(0.1) := TLBuffer(BufferParams.flow) := TLDelayer(0.1) + := TLErrorEvaluator(pattern, testOn=true, testOff=true) := model.node := fuzz.node) @@ -90,14 +93,14 @@ class AXI4FuzzMaster(txns: Int)(implicit p: Parameters) extends LazyModule with } } -class AXI4FuzzSlave()(implicit p: Parameters) extends LazyModule with HasFuzzTarget +class AXI4FuzzSlave()(implicit p: Parameters) extends SimpleLazyModule with HasFuzzTarget { val node = AXI4IdentityNode() val xbar = LazyModule(new TLXbar) val ram = LazyModule(new TLRAM(fuzzAddr)) val error= LazyModule(new TLError(ErrorParams(Seq(AddressSet(0x1800, 0xff)), maxTransfer = 256))) - ram.node := TLFragmenter(4, 16) := xbar.node + ram.node := TLErrorEvaluator(pattern) := TLFragmenter(4, 16) := xbar.node error.node := xbar.node (xbar.node @@ -110,8 +113,6 @@ class AXI4FuzzSlave()(implicit p: Parameters) extends LazyModule with HasFuzzTar := AXI4Fragmenter() := AXI4IdIndexer(2) := node) - - lazy val module = new LazyModuleImp(this) { } } class AXI4FuzzBridge(txns: Int)(implicit p: Parameters) extends LazyModule From 6318d7d44ccdca5af6692e271d39a97a8dc963ad Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 30 Oct 2017 13:53:23 -0700 Subject: [PATCH 10/11] ahb: inject fuzzy errors --- src/main/scala/amba/ahb/Test.scala | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/scala/amba/ahb/Test.scala b/src/main/scala/amba/ahb/Test.scala index 40877721..a959f54c 100644 --- a/src/main/scala/amba/ahb/Test.scala +++ b/src/main/scala/amba/ahb/Test.scala @@ -39,10 +39,16 @@ class AHBNativeTest(aFlow: Boolean, txns: Int = 5000, timeout: Int = 500000)(imp io.finished := dut.io.finished } -class AHBFuzzMaster(aFlow: Boolean, txns: Int)(implicit p: Parameters) extends LazyModule +trait HasFuzzTarget { + val fuzzAddr = AddressSet(0x0, 0xfff) + val pattern = Seq(AddressSet(0x8, ~0x808), // ie: 0x8-0xf, 0x18-0x1f, ... 0x7f8-0x7ff + AddressSet(0x900, ~0x900)) // ie: 0x900-0x9ff, 0xb00-0xbff, ... 0xf00-0xfff +} + +class AHBFuzzMaster(aFlow: Boolean, txns: Int)(implicit p: Parameters) extends LazyModule with HasFuzzTarget { val node = AHBIdentityNode() - val fuzz = LazyModule(new TLFuzzer(txns)) + val fuzz = LazyModule(new TLFuzzer(txns, overrideAddress = Some(fuzzAddr))) val model = LazyModule(new TLRAMModel("AHBFuzzMaster")) (node @@ -50,6 +56,7 @@ class AHBFuzzMaster(aFlow: Boolean, txns: Int)(implicit p: Parameters) extends L := TLDelayer(0.2) := TLBuffer(BufferParams.flow) := TLDelayer(0.2) + := TLErrorEvaluator(pattern, testOn=true, testOff=true) := model.node := fuzz.node) @@ -62,20 +69,19 @@ class AHBFuzzMaster(aFlow: Boolean, txns: Int)(implicit p: Parameters) extends L } } -class AHBFuzzSlave()(implicit p: Parameters) extends LazyModule +class AHBFuzzSlave()(implicit p: Parameters) extends SimpleLazyModule with HasFuzzTarget { val node = AHBIdentityNode() - val ram = LazyModule(new TLTestRAM(AddressSet(0x0, 0xfff))) + val ram = LazyModule(new TLTestRAM(fuzzAddr)) (ram.node + := TLErrorEvaluator(pattern) := TLFragmenter(4, 16) := TLDelayer(0.2) := TLBuffer(BufferParams.flow) := TLDelayer(0.2) := AHBToTL() := node) - - lazy val module = new LazyModuleImp(this) { } } class AHBFuzzBridge(aFlow: Boolean, txns: Int)(implicit p: Parameters) extends LazyModule From 45a904b3965a0a1182961c6ec17a0e42ba8c5479 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Mon, 30 Oct 2017 18:07:25 -0700 Subject: [PATCH 11/11] ahb: ignore hrdata on an AHB error From the AHB spec: "A slave only has to provide valid data when a transfer completes with an OKAY response. ERROR responses do not require valid read data." --- src/main/scala/amba/ahb/Test.scala | 2 +- src/main/scala/amba/ahb/ToTL.scala | 9 ++++++++- src/main/scala/tilelink/RAMModel.scala | 7 +++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/scala/amba/ahb/Test.scala b/src/main/scala/amba/ahb/Test.scala index a959f54c..f50a1d09 100644 --- a/src/main/scala/amba/ahb/Test.scala +++ b/src/main/scala/amba/ahb/Test.scala @@ -49,7 +49,7 @@ class AHBFuzzMaster(aFlow: Boolean, txns: Int)(implicit p: Parameters) extends L { val node = AHBIdentityNode() val fuzz = LazyModule(new TLFuzzer(txns, overrideAddress = Some(fuzzAddr))) - val model = LazyModule(new TLRAMModel("AHBFuzzMaster")) + val model = LazyModule(new TLRAMModel("AHBFuzzMaster", ignoreErrorData=true)) (node := TLToAHB(aFlow) diff --git a/src/main/scala/amba/ahb/ToTL.scala b/src/main/scala/amba/ahb/ToTL.scala index ec76044c..4b0484ba 100644 --- a/src/main/scala/amba/ahb/ToTL.scala +++ b/src/main/scala/amba/ahb/ToTL.scala @@ -106,7 +106,14 @@ class AHBToTL()(implicit p: Parameters) extends LazyModule out.a.bits.mask := MaskGen(d_addr, d_size, beatBytes) out.d.ready := d_recv // backpressure AccessAckData arriving faster than AHB beats - in.hrdata := out.d.bits.data + + // NOTE: on error, we present the read result on the hreadyout LOW cycle + // This means that if you latch hrdata from an error, the result is garbage. + // To fix this would require a bus-wide register, and the AHB spec says this: + // "A slave only has to provide valid data when a transfer completes with an OKAY + // response. ERROR responses do not require valid read data." + // Therefore, we choose to accept this slight TL-AHB infidelity. + in.hrdata := out.d.bits.data // In a perfect world, we'd use these signals val hresp = d_error || (out.d.valid && out.d.bits.error) diff --git a/src/main/scala/tilelink/RAMModel.scala b/src/main/scala/tilelink/RAMModel.scala index 21c5cf14..4ffbb2ef 100644 --- a/src/main/scala/tilelink/RAMModel.scala +++ b/src/main/scala/tilelink/RAMModel.scala @@ -22,7 +22,7 @@ import freechips.rocketchip.util._ // put, get, getAck, putAck => ok: detected by getAck (it sees busy>0) impossible for FIFO // If FIFO, the getAck should check data even if its validity was wiped -class TLRAMModel(log: String = "")(implicit p: Parameters) extends LazyModule +class TLRAMModel(log: String = "", ignoreErrorData: Boolean = false)(implicit p: Parameters) extends LazyModule { val node = TLAdapterNode() @@ -288,6 +288,8 @@ class TLRAMModel(log: String = "")(implicit p: Parameters) extends LazyModule printf(", undefined (concurrent incomplete puts #%d)\n", d_inc(i) - d_dec(i)) } .elsewhen (!d_fifo && !d_valid) { printf(", undefined (concurrent completed put)\n") + } .elsewhen (Bool(ignoreErrorData) && d.error) { + printf(", undefined (error result)\n") } .otherwise { printf("\n") when (shadow.value =/= got) { printf("EXPECTED: 0x%x\n", shadow.value) } @@ -303,8 +305,9 @@ class TLRAMModel(log: String = "")(implicit p: Parameters) extends LazyModule when ((Cat(race.reverse) & d_mask).orR) { d_no_race := Bool(false) } when (d_last) { val must_match = d_crc_valid && (d_fifo || (d_valid && d_no_race)) + val error = Bool(ignoreErrorData) && d.error printf(log + " crc = 0x%x %d\n", d_crc, must_match.asUInt) - when (must_match && d_crc =/= d_crc_check) { printf("EXPECTED: 0x%x\n", d_crc_check) } + when (!error && must_match && d_crc =/= d_crc_check) { printf("EXPECTED: 0x%x\n", d_crc_check) } assert (!must_match || d_crc === d_crc_check) } }