2016-09-14 00:30:09 +02:00
|
|
|
// See LICENSE for license details.
|
|
|
|
|
2016-09-28 06:27:07 +02:00
|
|
|
package util
|
2016-09-14 00:30:09 +02:00
|
|
|
import Chisel._
|
|
|
|
|
|
|
|
object GrayCounter {
|
2016-10-07 07:31:42 +02:00
|
|
|
def apply(bits: Int, increment: Bool = Bool(true), clear: Bool = Bool(false)): UInt = {
|
2016-09-26 20:08:38 +02:00
|
|
|
val incremented = Wire(UInt(width=bits))
|
2016-10-07 07:31:42 +02:00
|
|
|
val binary = AsyncResetReg(incremented)
|
|
|
|
incremented := Mux(clear, UInt(0), binary + increment.asUInt())
|
2016-09-14 00:30:09 +02:00
|
|
|
incremented ^ (incremented >> UInt(1))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
object AsyncGrayCounter {
|
2016-10-09 05:15:45 +02:00
|
|
|
def apply(in: UInt, sync: Int): UInt = {
|
|
|
|
val syncv = List.fill(sync)(Module (new AsyncResetRegVec(w = in.getWidth, 0)))
|
2016-09-26 20:08:38 +02:00
|
|
|
syncv.last.io.d := in
|
|
|
|
syncv.last.io.en := Bool(true)
|
2016-09-28 06:27:07 +02:00
|
|
|
(syncv.init zip syncv.tail).foreach { case (sink, source) =>
|
2016-09-26 20:08:38 +02:00
|
|
|
sink.io.d := source.io.q
|
|
|
|
sink.io.en := Bool(true)
|
|
|
|
}
|
2016-10-05 06:02:06 +02:00
|
|
|
syncv.head.io.q
|
2016-09-14 00:30:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-30 02:12:13 +02:00
|
|
|
class AsyncQueueSource[T <: Data](gen: T, depth: Int, sync: Int) extends Module {
|
2016-09-14 00:30:09 +02:00
|
|
|
val bits = log2Ceil(depth)
|
|
|
|
val io = new Bundle {
|
|
|
|
// These come from the source domain
|
2016-10-07 05:41:21 +02:00
|
|
|
val enq = Decoupled(gen).flip
|
2016-09-14 00:30:09 +02:00
|
|
|
// These cross to the sink clock domain
|
|
|
|
val ridx = UInt(INPUT, width = bits+1)
|
|
|
|
val widx = UInt(OUTPUT, width = bits+1)
|
|
|
|
val mem = Vec(depth, gen).asOutput
|
2016-10-07 05:27:34 +02:00
|
|
|
// Reset for the other side
|
2016-10-07 05:41:21 +02:00
|
|
|
val sink_reset_n = Bool().flip
|
2016-09-14 00:30:09 +02:00
|
|
|
}
|
|
|
|
|
2016-10-07 07:31:42 +02:00
|
|
|
// extend the sink reset to a full cycle (assertion latency <= 1 cycle)
|
|
|
|
val catch_reset_n = AsyncResetReg(Bool(true), clock, !io.sink_reset_n)
|
|
|
|
// reset_n has a 1 cycle shorter path to ready than ridx does
|
|
|
|
val reset_n = AsyncGrayCounter(catch_reset_n.asUInt, sync)(0)
|
|
|
|
|
2016-09-26 20:08:38 +02:00
|
|
|
val mem = Reg(Vec(depth, gen)) //This does NOT need to be asynchronously reset.
|
2016-10-07 07:31:42 +02:00
|
|
|
val widx = GrayCounter(bits+1, io.enq.fire(), !reset_n)
|
2016-10-09 05:15:45 +02:00
|
|
|
val ridx = AsyncGrayCounter(io.ridx, sync)
|
2016-09-14 00:30:09 +02:00
|
|
|
val ready = widx =/= (ridx ^ UInt(depth | depth >> 1))
|
|
|
|
|
|
|
|
val index = if (depth == 1) UInt(0) else io.widx(bits-1, 0) ^ (io.widx(bits, bits) << (bits-1))
|
2016-10-07 05:42:51 +02:00
|
|
|
when (io.enq.fire()) { mem(index) := io.enq.bits }
|
2016-10-07 05:27:34 +02:00
|
|
|
|
2016-10-07 07:31:42 +02:00
|
|
|
val ready_reg = AsyncResetReg(ready.asUInt)(0)
|
|
|
|
io.enq.ready := ready_reg && reset_n
|
2016-09-26 20:08:38 +02:00
|
|
|
|
2016-10-07 07:31:42 +02:00
|
|
|
val widx_reg = AsyncResetReg(widx)
|
2016-09-26 20:08:38 +02:00
|
|
|
io.widx := widx_reg
|
|
|
|
|
2016-09-14 00:30:09 +02:00
|
|
|
io.mem := mem
|
|
|
|
}
|
|
|
|
|
2016-09-30 02:12:13 +02:00
|
|
|
class AsyncQueueSink[T <: Data](gen: T, depth: Int, sync: Int) extends Module {
|
2016-09-14 00:30:09 +02:00
|
|
|
val bits = log2Ceil(depth)
|
|
|
|
val io = new Bundle {
|
|
|
|
// These come from the sink domain
|
|
|
|
val deq = Decoupled(gen)
|
|
|
|
// These cross to the source clock domain
|
|
|
|
val ridx = UInt(OUTPUT, width = bits+1)
|
|
|
|
val widx = UInt(INPUT, width = bits+1)
|
|
|
|
val mem = Vec(depth, gen).asInput
|
2016-10-07 05:27:34 +02:00
|
|
|
// Reset for the other side
|
2016-10-07 05:41:21 +02:00
|
|
|
val source_reset_n = Bool().flip
|
2016-09-14 00:30:09 +02:00
|
|
|
}
|
|
|
|
|
2016-10-07 07:31:42 +02:00
|
|
|
// extend the source reset to a full cycle (assertion latency <= 1 cycle)
|
|
|
|
val catch_reset_n = AsyncResetReg(Bool(true), clock, !io.source_reset_n)
|
|
|
|
// reset_n has a 1 cycle shorter path to valid than widx does
|
|
|
|
val reset_n = AsyncGrayCounter(catch_reset_n.asUInt, sync)(0)
|
|
|
|
|
|
|
|
val ridx = GrayCounter(bits+1, io.deq.fire(), !reset_n)
|
2016-10-09 05:15:45 +02:00
|
|
|
val widx = AsyncGrayCounter(io.widx, sync)
|
2016-09-14 00:30:09 +02:00
|
|
|
val valid = ridx =/= widx
|
|
|
|
|
|
|
|
// The mux is safe because timing analysis ensures ridx has reached the register
|
|
|
|
// On an ASIC, changes to the unread location cannot affect the selected value
|
|
|
|
// On an FPGA, only one input changes at a time => mem updates don't cause glitches
|
|
|
|
// The register only latches when the selected valued is not being written
|
|
|
|
val index = if (depth == 1) UInt(0) else ridx(bits-1, 0) ^ (ridx(bits, bits) << (bits-1))
|
2016-09-26 20:08:38 +02:00
|
|
|
// This register does not NEED to be reset, as its contents will not
|
|
|
|
// be considered unless the asynchronously reset deq valid register is set.
|
2016-10-07 07:31:42 +02:00
|
|
|
io.deq.bits := RegEnable(io.mem(index), valid)
|
|
|
|
|
|
|
|
val valid_reg = AsyncResetReg(valid.asUInt)(0)
|
|
|
|
io.deq.valid := valid_reg && reset_n
|
2016-09-26 20:08:38 +02:00
|
|
|
|
2016-10-07 07:31:42 +02:00
|
|
|
val ridx_reg = AsyncResetReg(ridx)
|
|
|
|
io.ridx := ridx_reg
|
2016-09-14 00:30:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
class AsyncQueue[T <: Data](gen: T, depth: Int = 8, sync: Int = 3) extends Crossing[T] {
|
|
|
|
require (sync >= 2)
|
|
|
|
require (depth > 0 && isPow2(depth))
|
|
|
|
|
|
|
|
val io = new CrossingIO(gen)
|
2016-09-30 02:12:13 +02:00
|
|
|
val source = Module(new AsyncQueueSource(gen, depth, sync))
|
|
|
|
val sink = Module(new AsyncQueueSink (gen, depth, sync))
|
|
|
|
|
|
|
|
source.clock := io.enq_clock
|
|
|
|
source.reset := io.enq_reset
|
|
|
|
sink.clock := io.deq_clock
|
|
|
|
sink.reset := io.deq_reset
|
2016-09-14 00:30:09 +02:00
|
|
|
|
2016-10-07 05:27:34 +02:00
|
|
|
source.io.sink_reset_n := !io.deq_reset
|
|
|
|
sink.io.source_reset_n := !io.enq_reset
|
|
|
|
|
2016-09-14 00:30:09 +02:00
|
|
|
source.io.enq <> io.enq
|
|
|
|
io.deq <> sink.io.deq
|
|
|
|
|
|
|
|
sink.io.mem := source.io.mem
|
|
|
|
sink.io.widx := source.io.widx
|
|
|
|
source.io.ridx := sink.io.ridx
|
|
|
|
}
|