From c33c0944be8e8cd8a2b2485ec68f797dd92d645b Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Tue, 12 Jul 2016 19:41:10 -0700 Subject: [PATCH] crossing: first clock crossing, the handshaker --- junctions/src/main/scala/crossing.scala | 150 ++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 junctions/src/main/scala/crossing.scala diff --git a/junctions/src/main/scala/crossing.scala b/junctions/src/main/scala/crossing.scala new file mode 100644 index 00000000..a4f974c0 --- /dev/null +++ b/junctions/src/main/scala/crossing.scala @@ -0,0 +1,150 @@ +package junctions +import Chisel._ + +class Crossing[T <: Data](gen: T, enq_sync: Boolean, deq_sync: Boolean) extends Bundle { + val enq = Decoupled(gen.cloneType).flip() + val deq = Decoupled(gen.cloneType) + val enq_clock = if (enq_sync) Some(Clock(INPUT)) else None + val deq_clock = if (deq_sync) Some(Clock(INPUT)) else None + val enq_reset = if (enq_sync) Some(Bool(INPUT)) else None + val deq_reset = if (deq_sync) Some(Bool(INPUT)) else None +} + +// Output is 1 for one cycle after any edge of 'in' +object AsyncHandshakePulse { + def apply(in: Bool, sync: Int): Bool = { + val syncv = RegInit(Vec.fill(sync+1){Bool(false)}) + syncv.last := in + (syncv.init zip syncv.tail).foreach { case (sink, source) => sink := source } + syncv(0) =/= syncv(1) + } +} + +class AsyncHandshakeSource[T <: Data](gen: T, sync: Int, clock: Clock, reset: Bool) + extends Module(_clock = clock, _reset = reset) { + val io = new Bundle { + // These come from the source clock domain + val enq = Decoupled(gen.cloneType).flip() + // These cross to the sink clock domain + val bits = gen.cloneType + val push = Bool(OUTPUT) + val pop = Bool(INPUT) + } + + val ready = RegInit(Bool(true)) + val bits = Reg(gen.cloneType) + val push = RegInit(Bool(false)) + + io.enq.ready := ready + io.bits := bits + io.push := push + + val pop = AsyncHandshakePulse(io.pop, sync) + assert (!pop || !ready) + + when (pop) { + ready := Bool(true) + } + + when (io.enq.fire()) { + ready := Bool(false) + bits := io.enq.bits + push := !push + } +} + +class AsyncHandshakeSink[T <: Data](gen: T, sync: Int, clock: Clock, reset: Bool) + extends Module(_clock = clock, _reset = reset) { + val io = new Bundle { + // These cross to the source clock domain + val bits = gen.cloneType.flip() + val push = Bool(INPUT) + val pop = Bool(OUTPUT) + // These go to the sink clock domain + val deq = Decoupled(gen.cloneType) + } + + val valid = RegInit(Bool(false)) + val bits = Reg(gen.cloneType) + val pop = RegInit(Bool(false)) + + io.deq.valid := valid + io.deq.bits := bits + io.pop := pop + + val push = AsyncHandshakePulse(io.push, sync) + assert (!push || !valid) + + when (push) { + valid := Bool(true) + bits := io.bits + } + + when (io.deq.fire()) { + valid := Bool(false) + pop := !pop + } +} + +class AsyncHandshake[T <: Data](gen: T, sync: Int = 2) extends Module { + val io = new Crossing(gen, true, true) + require (sync >= 2) + + val source = Module(new AsyncHandshakeSource(gen, sync, io.enq_clock.get, io.enq_reset.get)) + val sink = Module(new AsyncHandshakeSink (gen, sync, io.deq_clock.get, io.deq_reset.get)) + + source.io.enq <> io.enq + io.deq <> sink.io.deq + + sink.io.bits := source.io.bits + sink.io.push := source.io.push + source.io.pop := sink.io.pop +} + +class AsyncDecoupledTo[T <: Data](gen: T, depth: Int = 0, sync: Int = 2) extends Module { + val io = new Crossing(gen, false, true) + + // !!! if depth == 0 { use Handshake } else { use AsyncFIFO } + val crossing = Module(new AsyncHandshake(gen, sync)).io + crossing.enq_clock.get := clock + crossing.enq_reset.get := reset + crossing.enq <> io.enq + crossing.deq_clock.get := io.deq_clock.get + crossing.deq_reset.get := io.deq_reset.get + io.deq <> crossing.deq +} + +object AsyncDecoupledTo { + // source is in our clock domain, output is in the 'to' clock domain + def apply[T <: Data](to_clock: Clock, to_reset: Bool, source: DecoupledIO[T], depth: Int = 0, sync: Int = 2): DecoupledIO[T] = { + val to = Module(new AsyncDecoupledTo(source.bits, depth, sync)) + to.io.deq_clock.get := to_clock + to.io.deq_reset.get := to_reset + to.io.enq <> source + to.io.deq + } +} + +class AsyncDecoupledFrom[T <: Data](gen: T, depth: Int = 0, sync: Int = 2) extends Module { + val io = new Crossing(gen, true, false) + + // !!! if depth == 0 { use Handshake } else { use AsyncFIFO } + val crossing = Module(new AsyncHandshake(gen, sync)).io + crossing.enq_clock.get := io.enq_clock.get + crossing.enq_reset.get := io.enq_reset.get + crossing.enq <> io.enq + crossing.deq_clock.get := clock + crossing.deq_reset.get := reset + io.deq <> crossing.deq +} + +object AsyncDecoupledFrom { + // source is in the 'from' clock domain, output is in our clock domain + def apply[T <: Data](from_clock: Clock, from_reset: Bool, source: DecoupledIO[T], depth: Int = 0, sync: Int = 2): DecoupledIO[T] = { + val from = Module(new AsyncDecoupledFrom(source.bits, depth, sync)) + from.io.enq_clock.get := from_clock + from.io.enq_reset.get := from_reset + from.io.enq <> source + from.io.deq + } +}