1
0
Fork 0

Merge pull request #1078 from freechipsproject/error-support

Error support
This commit is contained in:
Wesley W. Terpstra 2017-10-30 22:21:20 -07:00 committed by GitHub
commit eaac0f6598
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 115 additions and 42 deletions

View File

@ -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))
}

View File

@ -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

View File

@ -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)

View File

@ -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
}
}

View File

@ -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)

View File

@ -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

View File

@ -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

View 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
}

View File

@ -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?

View File

@ -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

View File

@ -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)
}
}

View File

@ -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) }
}

View File

@ -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)

View File

@ -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")
}