diff --git a/src/main/scala/uncore/tilelink2/RationalCrossing.scala b/src/main/scala/uncore/tilelink2/RationalCrossing.scala index e13bbbbf..4cfa94c9 100644 --- a/src/main/scala/uncore/tilelink2/RationalCrossing.scala +++ b/src/main/scala/uncore/tilelink2/RationalCrossing.scala @@ -212,10 +212,13 @@ class TLRAMRationalCrossing(implicit p: Parameters) extends LazyModule { // Generate slower clock val slow = Module(new util.Pow2ClockDivider(2)) - sym_slow_source.module.clock := slow.io.clock_out - sym_slow_sink .module.clock := slow.io.clock_out fix_slow_source.module.clock := slow.io.clock_out fix_slow_sink .module.clock := slow.io.clock_out + + val odd = Module(new util.ClockDivider3) + odd.io.clk_in := clock + sym_slow_source.module.clock := odd.io.clk_out + sym_slow_sink .module.clock := odd.io.clk_out } } diff --git a/src/main/scala/util/ClockDivider.scala b/src/main/scala/util/ClockDivider.scala index bbe07279..91a280fd 100644 --- a/src/main/scala/util/ClockDivider.scala +++ b/src/main/scala/util/ClockDivider.scala @@ -19,6 +19,12 @@ class ClockDivider2 extends BlackBox { val clk_in = Clock(INPUT) } } +class ClockDivider3 extends BlackBox { + val io = new Bundle { + val clk_out = Clock(OUTPUT) + val clk_in = Clock(INPUT) + } +} /** Divide the clock by power of 2 times. * @param pow2 divides the clock 2 ^ pow2 times diff --git a/src/main/scala/util/RationalCrossing.scala b/src/main/scala/util/RationalCrossing.scala index 2c55b4f0..13eaab25 100644 --- a/src/main/scala/util/RationalCrossing.scala +++ b/src/main/scala/util/RationalCrossing.scala @@ -1,8 +1,8 @@ // See LICENSE.SiFive for license details. -// If you know two clocks are related with a N:1 or 1:N relationship, you -// can cross the clock domains with lower latency than an AsyncQueue. This -// crossing adds 1 cycle in the target clock domain. +// If you know two clocks are related with an N:M relationship, you +// can cross the clock domains with lower latency than an AsyncQueue. +// This crossing adds 1 cycle in the target clock domain. package util import Chisel._ @@ -18,17 +18,30 @@ sealed trait RationalDirection { // place registers on both sides of the crossing, by splitting // a Queue into flow and pipe parts on either side. This is safe // for all possible clock ratios, but has the downside that the -// path from the slow domain must close timing in the fast domain. +// timing must be met for the least-common-multiple of the clocks. case object Symmetric extends RationalDirection { def flip = Symmetric } -// If the source is fast, place the registers at the sink. +// Like Symmetric, this crossing works for all ratios N:M. +// However, unlike the other crossing options, this varient adds +// a full flow+pipe buffer on both sides of the crossing. This +// ends up costing potentially two cycles of delay, but gives +// both clock domains a full clock period to close timing. +case object Flexible extends RationalDirection { + def flip = Flexible +} + +// If the source is N:1 of the sink, place the registers at the sink. +// This imposes only a single clock cycle of delay and both side of +// the crossing have a full clock period to close timing. case object FastToSlow extends RationalDirection { def flip = SlowToFast } -// If the source is slow, place the registers at the source. +// If the source is 1:N of the sink, place the registers at the source. +// This imposes only a single clock cycle of delay and both side of +// the crossing have a full clock period to close timing. case object SlowToFast extends RationalDirection { def flip = FastToSlow } @@ -59,6 +72,7 @@ class RationalCrossingSource[T <: Data](gen: T, direction: RationalDirection = S val deq = io.deq val enq = direction match { case Symmetric => Queue(io.enq, 1, flow=true) + case Flexible => Queue(io.enq, 2) case FastToSlow => io.enq case SlowToFast => Queue(io.enq, 2) } @@ -76,6 +90,7 @@ class RationalCrossingSource[T <: Data](gen: T, direction: RationalDirection = S // Ensure the clocking is setup correctly direction match { case Symmetric => () // always safe + case Flexible => () case FastToSlow => assert (equal || count(1) === deq.sink(0)) case SlowToFast => assert (equal || count(1) =/= deq.sink(0)) } @@ -92,6 +107,7 @@ class RationalCrossingSink[T <: Data](gen: T, direction: RationalDirection = Sym val deq = Wire(io.deq) direction match { case Symmetric => io.deq <> Queue(deq, 1, pipe=true) + case Flexible => io.deq <> Queue(deq, 2) case FastToSlow => io.deq <> Queue(deq, 2) case SlowToFast => io.deq <> deq } @@ -109,6 +125,7 @@ class RationalCrossingSink[T <: Data](gen: T, direction: RationalDirection = Sym // Ensure the clocking is setup correctly direction match { case Symmetric => () // always safe + case Flexible => () case FastToSlow => assert (equal || count(1) =/= enq.source(0)) case SlowToFast => assert (equal || count(1) === enq.source(0)) } diff --git a/vsim/Makefrag b/vsim/Makefrag index 1429ba1c..50e3a1a3 100644 --- a/vsim/Makefrag +++ b/vsim/Makefrag @@ -7,6 +7,7 @@ bb_vsrcs = \ $(base_dir)/vsrc/jtag_vpi.v \ $(base_dir)/vsrc/ClockDivider2.v \ + $(base_dir)/vsrc/ClockDivider3.v \ $(base_dir)/vsrc/AsyncResetReg.v \ sim_vsrcs = \ diff --git a/vsrc/ClockDivider3.v b/vsrc/ClockDivider3.v new file mode 100644 index 00000000..f9fba48c --- /dev/null +++ b/vsrc/ClockDivider3.v @@ -0,0 +1,37 @@ +// See LICENSE.SiFive for license details. + +/** This black-boxes a Clock Divider by 3. + * The output clock is phase-aligned to the input clock. + * Do NOT use this in synthesis; the duty cycle is 2:1. + * + * Because Chisel does not support + * blocking assignments, it is impossible + * to create a deterministic divided clock. + * + * @param clk_out Divided Clock + * @param clk_in Clock Input + * + */ + +module ClockDivider3 (output reg clk_out, input clk_in); + + reg delay; + + initial begin + clk_out = 1'b0; + delay = 1'b0; + end + + always @(posedge clk_in) begin + if (clk_out == 1'b0) begin + clk_out = 1'b1; + delay <= 1'b0; + end else if (delay == 1'b1) begin + clk_out = 1'b0; + delay <= 1'b0; + end else begin + delay <= 1'b1; + end + end + +endmodule // ClockDivider3