Merge pull request #400 from ucb-bar/better-crossing-asserts
Better crossing reset handling
This commit is contained in:
		@@ -41,7 +41,10 @@ class RegisterCrossingAssertion extends Module {
 | 
			
		||||
    val slave_reset = Bool(INPUT)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  assert (io.master_bypass || !io.slave_reset)
 | 
			
		||||
  val up = RegInit(Bool(false))
 | 
			
		||||
  up := !io.slave_reset
 | 
			
		||||
 | 
			
		||||
  assert (io.master_bypass || !up || !io.slave_reset)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegField should support connecting to one of these
 | 
			
		||||
@@ -88,7 +91,7 @@ class RegisterWriteCrossing[T <: Data](gen: T, sync: Int = 3) extends Module {
 | 
			
		||||
  val io = new RegisterWriteCrossingIO(gen)
 | 
			
		||||
  // The crossing must only allow one item inflight at a time
 | 
			
		||||
  val control = Module(new BusyRegisterCrossing)
 | 
			
		||||
  val crossing = Module(new AsyncQueue(gen, 1, sync))
 | 
			
		||||
  val crossing = Module(new AsyncQueue(gen, 1, sync, safe=false))
 | 
			
		||||
 | 
			
		||||
  control.clock := io.master_clock
 | 
			
		||||
  control.reset := io.master_reset
 | 
			
		||||
@@ -141,7 +144,7 @@ class RegisterReadCrossing[T <: Data](gen: T, sync: Int = 3) extends Module {
 | 
			
		||||
  val io = new RegisterReadCrossingIO(gen)
 | 
			
		||||
  // The crossing must only allow one item inflight at a time
 | 
			
		||||
  val control = Module(new BusyRegisterCrossing)
 | 
			
		||||
  val crossing = Module(new AsyncQueue(gen, 1, sync))
 | 
			
		||||
  val crossing = Module(new AsyncQueue(gen, 1, sync, safe=false))
 | 
			
		||||
 | 
			
		||||
  control.clock := io.master_clock
 | 
			
		||||
  control.reset := io.master_reset
 | 
			
		||||
 
 | 
			
		||||
@@ -222,9 +222,11 @@ object TLBundleSnoop
 | 
			
		||||
final class AsyncBundle[T <: Data](val depth: Int, gen: T) extends Bundle
 | 
			
		||||
{
 | 
			
		||||
  require (isPow2(depth))
 | 
			
		||||
  val mem  = Vec(depth, gen)
 | 
			
		||||
  val ridx = UInt(width = log2Up(depth)+1).flip
 | 
			
		||||
  val widx = UInt(width = log2Up(depth)+1)
 | 
			
		||||
  val mem  = Vec(depth, gen)
 | 
			
		||||
  val ridx_valid = Bool().flip
 | 
			
		||||
  val widx_valid = Bool()
 | 
			
		||||
  val source_reset_n = Bool()
 | 
			
		||||
  val sink_reset_n = Bool().flip
 | 
			
		||||
 | 
			
		||||
@@ -236,7 +238,9 @@ object FromAsyncBundle
 | 
			
		||||
  def apply[T <: Data](x: AsyncBundle[T], sync: Int = 3): DecoupledIO[T] = {
 | 
			
		||||
    val sink = Module(new AsyncQueueSink(x.mem(0), x.depth, sync))
 | 
			
		||||
    x.ridx := sink.io.ridx
 | 
			
		||||
    x.ridx_valid := sink.io.ridx_valid
 | 
			
		||||
    sink.io.widx := x.widx
 | 
			
		||||
    sink.io.widx_valid := x.widx_valid
 | 
			
		||||
    sink.io.mem  := x.mem
 | 
			
		||||
    sink.io.source_reset_n := x.source_reset_n
 | 
			
		||||
    x.sink_reset_n := !sink.reset
 | 
			
		||||
@@ -257,8 +261,10 @@ object ToAsyncBundle
 | 
			
		||||
    x.ready := source.io.enq.ready
 | 
			
		||||
    val out = Wire(new AsyncBundle(depth, x.bits))
 | 
			
		||||
    source.io.ridx := out.ridx
 | 
			
		||||
    source.io.ridx_valid := out.ridx_valid
 | 
			
		||||
    out.mem := source.io.mem
 | 
			
		||||
    out.widx := source.io.widx
 | 
			
		||||
    out.widx_valid := source.io.widx_valid
 | 
			
		||||
    source.io.sink_reset_n := out.sink_reset_n
 | 
			
		||||
    out.source_reset_n := !source.reset
 | 
			
		||||
    out
 | 
			
		||||
 
 | 
			
		||||
@@ -25,15 +25,10 @@ class TLAsyncCrossingSource(sync: Int = 3) extends LazyModule
 | 
			
		||||
      out.a <> ToAsyncBundle(in.a, depth, sync)
 | 
			
		||||
      in.d <> FromAsyncBundle(out.d, sync)
 | 
			
		||||
 | 
			
		||||
      assert (!in.a.valid || sink_reset_n, "A channel request sent to a missing manager")
 | 
			
		||||
 | 
			
		||||
      if (bce) {
 | 
			
		||||
        in.b <> FromAsyncBundle(out.b, sync)
 | 
			
		||||
        out.c <> ToAsyncBundle(in.c, depth, sync)
 | 
			
		||||
        out.e <> ToAsyncBundle(in.e, depth, sync)
 | 
			
		||||
 | 
			
		||||
        assert (!in.c.valid || sink_reset_n, "C channel response sent to a missing manager")
 | 
			
		||||
        assert (!in.e.valid || sink_reset_n, "E channel response sent to a missing manager")
 | 
			
		||||
      } else {
 | 
			
		||||
        in.b.valid := Bool(false)
 | 
			
		||||
        in.c.ready := Bool(true)
 | 
			
		||||
@@ -63,14 +58,10 @@ class TLAsyncCrossingSink(depth: Int = 8, sync: Int = 3) extends LazyModule
 | 
			
		||||
      out.a <> FromAsyncBundle(in.a, sync)
 | 
			
		||||
      in.d <> ToAsyncBundle(out.d, depth, sync)
 | 
			
		||||
 | 
			
		||||
      assert (!out.d.valid || source_reset_n, "D channel respose sent to missing client")
 | 
			
		||||
 | 
			
		||||
      if (bce) {
 | 
			
		||||
        in.b <> ToAsyncBundle(out.b, depth, sync)
 | 
			
		||||
        out.c <> FromAsyncBundle(in.c, sync)
 | 
			
		||||
        out.e <> FromAsyncBundle(in.e, sync)
 | 
			
		||||
 | 
			
		||||
        assert (!out.b.valid || source_reset_n, "B channel request sent to missing client")
 | 
			
		||||
      } else {
 | 
			
		||||
        in.b.widx := UInt(0)
 | 
			
		||||
        in.c.ridx := UInt(0)
 | 
			
		||||
 
 | 
			
		||||
@@ -22,44 +22,47 @@ class TLIsolation(fOut: (Bool, UInt) => UInt, fIn: (Bool, UInt) => UInt) extends
 | 
			
		||||
    def ISOo[T <: Data](x: T): T = x.fromBits(fOut(io.iso_out, x.asUInt))
 | 
			
		||||
    def ISOi[T <: Data](x: T): T = x.fromBits(fIn (io.iso_in,  x.asUInt))
 | 
			
		||||
 | 
			
		||||
    def ABo[T <: Data](x: AsyncBundle[T], y: AsyncBundle[T]) {
 | 
			
		||||
      x.mem            := ISOo(y.mem)
 | 
			
		||||
      x.widx           := ISOo(y.widx)
 | 
			
		||||
      x.widx_valid     := ISOo(y.widx_valid)
 | 
			
		||||
      x.source_reset_n := ISOo(y.source_reset_n)
 | 
			
		||||
      y.ridx           := ISOi(x.ridx)
 | 
			
		||||
      y.ridx_valid     := ISOi(x.ridx_valid)
 | 
			
		||||
      y.sink_reset_n   := ISOi(x.sink_reset_n)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def ABi[T <: Data](x: AsyncBundle[T], y: AsyncBundle[T]) {
 | 
			
		||||
      x.mem            := ISOi(y.mem)
 | 
			
		||||
      x.widx           := ISOi(y.widx)
 | 
			
		||||
      x.widx_valid     := ISOi(y.widx_valid)
 | 
			
		||||
      x.source_reset_n := ISOi(y.source_reset_n)
 | 
			
		||||
      y.ridx           := ISOo(x.ridx)
 | 
			
		||||
      y.ridx_valid     := ISOo(x.ridx_valid)
 | 
			
		||||
      y.sink_reset_n   := ISOo(x.sink_reset_n)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def ABz[T <: Data](x: AsyncBundle[T], y: AsyncBundle[T]) {
 | 
			
		||||
      x.widx           := UInt(0)
 | 
			
		||||
      x.widx_valid     := Bool(false)
 | 
			
		||||
      x.source_reset_n := Bool(false)
 | 
			
		||||
      y.ridx           := UInt(0)
 | 
			
		||||
      y.ridx_valid     := Bool(false)
 | 
			
		||||
      y.sink_reset_n   := Bool(false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
 | 
			
		||||
 | 
			
		||||
      out.a.mem  := ISOo(in .a.mem)
 | 
			
		||||
      out.a.widx := ISOo(in .a.widx)
 | 
			
		||||
      in .a.ridx := ISOi(out.a.ridx)
 | 
			
		||||
      out.d.ridx := ISOo(in .d.ridx)
 | 
			
		||||
      in .d.widx := ISOi(out.d.widx)
 | 
			
		||||
      in .d.mem  := ISOi(out.d.mem)
 | 
			
		||||
 | 
			
		||||
      out.a.source_reset_n := ISOo(in .a.source_reset_n)
 | 
			
		||||
      in .a.sink_reset_n   := ISOi(out.a.sink_reset_n)
 | 
			
		||||
      out.d.sink_reset_n   := ISOo(in .d.sink_reset_n)
 | 
			
		||||
      in .d.source_reset_n := ISOi(out.d.source_reset_n)
 | 
			
		||||
      ABo(out.a, in .a)
 | 
			
		||||
      ABi(in .d, out.d)
 | 
			
		||||
 | 
			
		||||
      if (edgeOut.manager.base.anySupportAcquire && edgeOut.client.base.anySupportProbe) {
 | 
			
		||||
        in .b.widx := ISOi(out.b.widx)
 | 
			
		||||
        in .c.ridx := ISOi(out.c.ridx)
 | 
			
		||||
        in .e.ridx := ISOi(out.e.ridx)
 | 
			
		||||
        out.b.ridx := ISOo(in .b.ridx)
 | 
			
		||||
        out.c.widx := ISOo(in .c.widx)
 | 
			
		||||
        out.e.widx := ISOo(in .e.widx)
 | 
			
		||||
        in .b.mem  := ISOi(out.b.mem)
 | 
			
		||||
        out.c.mem  := ISOo(in .c.mem)
 | 
			
		||||
        out.e.mem  := ISOo(in .e.mem)
 | 
			
		||||
 | 
			
		||||
        out.b.sink_reset_n   := ISOo(in .b.sink_reset_n)
 | 
			
		||||
        in .b.source_reset_n := ISOi(out.b.source_reset_n)
 | 
			
		||||
        out.c.source_reset_n := ISOo(in .c.source_reset_n)
 | 
			
		||||
        in .c.sink_reset_n   := ISOi(out.c.sink_reset_n)
 | 
			
		||||
        out.e.source_reset_n := ISOo(in .e.source_reset_n)
 | 
			
		||||
        in .e.sink_reset_n   := ISOi(out.e.sink_reset_n)
 | 
			
		||||
        ABi(in .b, out.b)
 | 
			
		||||
        ABo(out.c, in .c)
 | 
			
		||||
        ABo(out.e, in .e)
 | 
			
		||||
      } else {
 | 
			
		||||
        in .b.widx := UInt(0)
 | 
			
		||||
        in .c.ridx := UInt(0)
 | 
			
		||||
        in .e.ridx := UInt(0)
 | 
			
		||||
        out.b.ridx := UInt(0)
 | 
			
		||||
        out.c.widx := UInt(0)
 | 
			
		||||
        out.e.widx := UInt(0)
 | 
			
		||||
        ABz(in .b, out.b)
 | 
			
		||||
        ABz(out.c, in .c)
 | 
			
		||||
        ABz(out.e, in .e)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,15 @@ object UIntSyncChain {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class AsyncQueueSource[T <: Data](gen: T, depth: Int, sync: Int) extends Module {
 | 
			
		||||
class AsyncValidSync(sync: Int, desc: String) extends Module {
 | 
			
		||||
  val io = new Bundle {
 | 
			
		||||
    val in = Bool(INPUT)
 | 
			
		||||
    val out = Bool(OUTPUT)
 | 
			
		||||
  }
 | 
			
		||||
  io.out := UIntSyncChain(io.in.asUInt, sync, desc)(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class AsyncQueueSource[T <: Data](gen: T, depth: Int, sync: Int, safe: Boolean = true) extends Module {
 | 
			
		||||
  val bits = log2Ceil(depth)
 | 
			
		||||
  val io = new Bundle {
 | 
			
		||||
    // These come from the source domain
 | 
			
		||||
@@ -36,17 +44,15 @@ class AsyncQueueSource[T <: Data](gen: T, depth: Int, sync: Int) extends Module
 | 
			
		||||
    val ridx = UInt(INPUT,  width = bits+1)
 | 
			
		||||
    val widx = UInt(OUTPUT, width = bits+1)
 | 
			
		||||
    val mem  = Vec(depth, gen).asOutput
 | 
			
		||||
    // Reset for the other side
 | 
			
		||||
    val sink_reset_n = Bool().flip
 | 
			
		||||
    // Signals used to self-stabilize a safe AsyncQueue
 | 
			
		||||
    val sink_reset_n = Bool(INPUT)
 | 
			
		||||
    val ridx_valid = Bool(INPUT)
 | 
			
		||||
    val widx_valid = Bool(OUTPUT)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // extend the sink reset to a full cycle (assertion latency <= 1 cycle)
 | 
			
		||||
  val catch_sink_reset_n = AsyncResetReg(Bool(true), clock, !io.sink_reset_n, "catch_sink_reset_n")
 | 
			
		||||
  // reset_n has a 1 cycle shorter path to ready than ridx does
 | 
			
		||||
  val sink_reset_n = UIntSyncChain(catch_sink_reset_n.asUInt, sync, "sink_reset_n")(0)
 | 
			
		||||
 | 
			
		||||
  val mem = Reg(Vec(depth, gen)) //This does NOT need to be asynchronously reset.
 | 
			
		||||
  val widx = GrayCounter(bits+1, io.enq.fire(), !sink_reset_n, "widx_bin")
 | 
			
		||||
  val sink_ready = Wire(init = Bool(true))
 | 
			
		||||
  val mem = Reg(Vec(depth, gen)) // This does NOT need to be reset at all.
 | 
			
		||||
  val widx = GrayCounter(bits+1, io.enq.fire(), !sink_ready, "widx_bin")
 | 
			
		||||
  val ridx = UIntSyncChain(io.ridx, sync, "ridx_gray")
 | 
			
		||||
  val ready = widx =/= (ridx ^ UInt(depth | depth >> 1))
 | 
			
		||||
 | 
			
		||||
@@ -54,18 +60,33 @@ class AsyncQueueSource[T <: Data](gen: T, depth: Int, sync: Int) extends Module
 | 
			
		||||
  when (io.enq.fire()) { mem(index) := io.enq.bits }
 | 
			
		||||
 | 
			
		||||
  val ready_reg = AsyncResetReg(ready.asUInt, "ready_reg")(0)
 | 
			
		||||
  io.enq.ready := ready_reg && sink_reset_n
 | 
			
		||||
  io.enq.ready := ready_reg && sink_ready
 | 
			
		||||
 | 
			
		||||
  val widx_reg = AsyncResetReg(widx, "widx_gray")
 | 
			
		||||
  io.widx := widx_reg
 | 
			
		||||
 | 
			
		||||
  io.mem := mem
 | 
			
		||||
 | 
			
		||||
  // It is a fatal error to reset half a Queue while it still has data
 | 
			
		||||
  assert (sink_reset_n || widx === ridx)
 | 
			
		||||
  io.widx_valid := Bool(true)
 | 
			
		||||
  if (safe) {
 | 
			
		||||
    val source_valid = Module(new AsyncValidSync(sync+1, "source_valid"))
 | 
			
		||||
    val sink_extend  = Module(new AsyncValidSync(1, "sink_extend"))
 | 
			
		||||
    val sink_valid   = Module(new AsyncValidSync(sync, "sink_valid"))
 | 
			
		||||
    source_valid.reset := reset || !io.sink_reset_n
 | 
			
		||||
    sink_extend .reset := reset || !io.sink_reset_n
 | 
			
		||||
 | 
			
		||||
    source_valid.io.in := Bool(true)
 | 
			
		||||
    io.widx_valid := source_valid.io.out
 | 
			
		||||
    sink_extend.io.in := io.ridx_valid
 | 
			
		||||
    sink_valid.io.in := sink_extend.io.out
 | 
			
		||||
    sink_ready := sink_valid.io.out
 | 
			
		||||
 | 
			
		||||
    assert (io.sink_reset_n || !sink_ready || !io.enq.valid, "Enque while sink is reset and AsyncQueueSource is unprotected")
 | 
			
		||||
    assert (io.sink_reset_n || widx === ridx, "Sink reset while AsyncQueueSource not empty")
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class AsyncQueueSink[T <: Data](gen: T, depth: Int, sync: Int) extends Module {
 | 
			
		||||
class AsyncQueueSink[T <: Data](gen: T, depth: Int, sync: Int, safe: Boolean = true) extends Module {
 | 
			
		||||
  val bits = log2Ceil(depth)
 | 
			
		||||
  val io = new Bundle {
 | 
			
		||||
    // These come from the sink domain
 | 
			
		||||
@@ -74,16 +95,14 @@ class AsyncQueueSink[T <: Data](gen: T, depth: Int, sync: Int) extends Module {
 | 
			
		||||
    val ridx = UInt(OUTPUT, width = bits+1)
 | 
			
		||||
    val widx = UInt(INPUT,  width = bits+1)
 | 
			
		||||
    val mem  = Vec(depth, gen).asInput
 | 
			
		||||
    // Reset for the other side
 | 
			
		||||
    val source_reset_n = Bool().flip
 | 
			
		||||
    // Signals used to self-stabilize a safe AsyncQueue
 | 
			
		||||
    val source_reset_n = Bool(INPUT)
 | 
			
		||||
    val ridx_valid = Bool(OUTPUT)
 | 
			
		||||
    val widx_valid = Bool(INPUT)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // extend the source reset to a full cycle (assertion latency <= 1 cycle)
 | 
			
		||||
  val catch_source_reset_n = AsyncResetReg(Bool(true), clock, !io.source_reset_n, "catch_source_reset_n")
 | 
			
		||||
  // reset_n has a 1 cycle shorter path to valid than widx does
 | 
			
		||||
  val source_reset_n = UIntSyncChain(catch_source_reset_n.asUInt, sync, "source_reset_n")(0)
 | 
			
		||||
 | 
			
		||||
  val ridx = GrayCounter(bits+1, io.deq.fire(), !source_reset_n, "ridx_bin")
 | 
			
		||||
  val source_ready = Wire(init = Bool(true))
 | 
			
		||||
  val ridx = GrayCounter(bits+1, io.deq.fire(), !source_ready, "ridx_bin")
 | 
			
		||||
  val widx = UIntSyncChain(io.widx, sync, "widx_gray")
 | 
			
		||||
  val valid = ridx =/= widx
 | 
			
		||||
 | 
			
		||||
@@ -99,22 +118,36 @@ class AsyncQueueSink[T <: Data](gen: T, depth: Int, sync: Int) extends Module {
 | 
			
		||||
  io.deq.bits  := RegEnable(io.mem(index), valid)
 | 
			
		||||
 | 
			
		||||
  val valid_reg = AsyncResetReg(valid.asUInt, "valid_reg")(0)
 | 
			
		||||
  io.deq.valid := valid_reg && source_reset_n
 | 
			
		||||
  io.deq.valid := valid_reg && source_ready
 | 
			
		||||
 | 
			
		||||
  val ridx_reg = AsyncResetReg(ridx, "ridx_gray")
 | 
			
		||||
  io.ridx := ridx_reg
 | 
			
		||||
 | 
			
		||||
  // It is a fatal error to reset half a Queue while it still has data
 | 
			
		||||
  assert (source_reset_n || widx === ridx)
 | 
			
		||||
  io.ridx_valid := Bool(true)
 | 
			
		||||
  if (safe) {
 | 
			
		||||
    val sink_valid    = Module(new AsyncValidSync(sync+1, "sink_valid"))
 | 
			
		||||
    val source_extend = Module(new AsyncValidSync(1, "source_extend"))
 | 
			
		||||
    val source_valid  = Module(new AsyncValidSync(sync, "source_valid"))
 | 
			
		||||
    sink_valid   .reset := reset || !io.source_reset_n
 | 
			
		||||
    source_extend.reset := reset || !io.source_reset_n
 | 
			
		||||
 | 
			
		||||
    sink_valid.io.in := Bool(true)
 | 
			
		||||
    io.ridx_valid := sink_valid.io.out
 | 
			
		||||
    source_extend.io.in := io.widx_valid
 | 
			
		||||
    source_valid.io.in := source_extend.io.out
 | 
			
		||||
    source_ready := source_valid.io.out
 | 
			
		||||
 | 
			
		||||
    assert (io.source_reset_n || widx === ridx, "Source reset while AsyncQueueSink not empty")
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class AsyncQueue[T <: Data](gen: T, depth: Int = 8, sync: Int = 3) extends Crossing[T] {
 | 
			
		||||
class AsyncQueue[T <: Data](gen: T, depth: Int = 8, sync: Int = 3, safe: Boolean = true) extends Crossing[T] {
 | 
			
		||||
  require (sync >= 2)
 | 
			
		||||
  require (depth > 0 && isPow2(depth))
 | 
			
		||||
 | 
			
		||||
  val io = new CrossingIO(gen)
 | 
			
		||||
  val source = Module(new AsyncQueueSource(gen, depth, sync))
 | 
			
		||||
  val sink   = Module(new AsyncQueueSink  (gen, depth, sync))
 | 
			
		||||
  val source = Module(new AsyncQueueSource(gen, depth, sync, safe))
 | 
			
		||||
  val sink   = Module(new AsyncQueueSink  (gen, depth, sync, safe))
 | 
			
		||||
 | 
			
		||||
  source.clock := io.enq_clock
 | 
			
		||||
  source.reset := io.enq_reset
 | 
			
		||||
@@ -130,4 +163,6 @@ class AsyncQueue[T <: Data](gen: T, depth: Int = 8, sync: Int = 3) extends Cross
 | 
			
		||||
  sink.io.mem := source.io.mem
 | 
			
		||||
  sink.io.widx := source.io.widx
 | 
			
		||||
  source.io.ridx := sink.io.ridx
 | 
			
		||||
  sink.io.widx_valid := source.io.widx_valid
 | 
			
		||||
  source.io.ridx_valid := sink.io.ridx_valid
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user