Merge pull request #1078 from freechipsproject/error-support
Error support
This commit is contained in:
commit
eaac0f6598
@ -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))
|
||||
}
|
||||
|
@ -39,17 +39,24 @@ 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 model = LazyModule(new TLRAMModel("AHBFuzzMaster"))
|
||||
val fuzz = LazyModule(new TLFuzzer(txns, overrideAddress = Some(fuzzAddr)))
|
||||
val model = LazyModule(new TLRAMModel("AHBFuzzMaster", ignoreErrorData=true))
|
||||
|
||||
(node
|
||||
:= TLToAHB(aFlow)
|
||||
:= 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
|
||||
|
@ -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)
|
||||
|
@ -6,13 +6,16 @@ import Chisel._
|
||||
import freechips.rocketchip.config.Parameters
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.util._
|
||||
import freechips.rocketchip.tilelink.LFSRNoiseMaker
|
||||
|
||||
class APBRAM(
|
||||
address: AddressSet,
|
||||
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 +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(true)
|
||||
in.pslverr := RegNext(!legal)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
50
src/main/scala/tilelink/ErrorEvaluator.scala
Normal file
50
src/main/scala/tilelink/ErrorEvaluator.scala
Normal file
@ -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
|
||||
}
|
@ -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?
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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) }
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user