Merge pull request #742 from ucb-bar/true-rational
RationalCrossing: now supporting true rational N:M crossings
This commit is contained in:
commit
ad087dd18d
@ -212,10 +212,13 @@ class TLRAMRationalCrossing(implicit p: Parameters) extends LazyModule {
|
|||||||
|
|
||||||
// Generate slower clock
|
// Generate slower clock
|
||||||
val slow = Module(new util.Pow2ClockDivider(2))
|
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_source.module.clock := slow.io.clock_out
|
||||||
fix_slow_sink .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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,12 @@ class ClockDivider2 extends BlackBox {
|
|||||||
val clk_in = Clock(INPUT)
|
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.
|
/** Divide the clock by power of 2 times.
|
||||||
* @param pow2 divides the clock 2 ^ pow2 times
|
* @param pow2 divides the clock 2 ^ pow2 times
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// See LICENSE.SiFive for license details.
|
// See LICENSE.SiFive for license details.
|
||||||
|
|
||||||
// If you know two clocks are related with a N:1 or 1:N relationship, you
|
// 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
|
// can cross the clock domains with lower latency than an AsyncQueue.
|
||||||
// crossing adds 1 cycle in the target clock domain.
|
// This crossing adds 1 cycle in the target clock domain.
|
||||||
|
|
||||||
package util
|
package util
|
||||||
import Chisel._
|
import Chisel._
|
||||||
@ -18,17 +18,30 @@ sealed trait RationalDirection {
|
|||||||
// place registers on both sides of the crossing, by splitting
|
// place registers on both sides of the crossing, by splitting
|
||||||
// a Queue into flow and pipe parts on either side. This is safe
|
// a Queue into flow and pipe parts on either side. This is safe
|
||||||
// for all possible clock ratios, but has the downside that the
|
// 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 {
|
case object Symmetric extends RationalDirection {
|
||||||
def flip = Symmetric
|
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 {
|
case object FastToSlow extends RationalDirection {
|
||||||
def flip = SlowToFast
|
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 {
|
case object SlowToFast extends RationalDirection {
|
||||||
def flip = FastToSlow
|
def flip = FastToSlow
|
||||||
}
|
}
|
||||||
@ -59,6 +72,7 @@ class RationalCrossingSource[T <: Data](gen: T, direction: RationalDirection = S
|
|||||||
val deq = io.deq
|
val deq = io.deq
|
||||||
val enq = direction match {
|
val enq = direction match {
|
||||||
case Symmetric => Queue(io.enq, 1, flow=true)
|
case Symmetric => Queue(io.enq, 1, flow=true)
|
||||||
|
case Flexible => Queue(io.enq, 2)
|
||||||
case FastToSlow => io.enq
|
case FastToSlow => io.enq
|
||||||
case SlowToFast => Queue(io.enq, 2)
|
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
|
// Ensure the clocking is setup correctly
|
||||||
direction match {
|
direction match {
|
||||||
case Symmetric => () // always safe
|
case Symmetric => () // always safe
|
||||||
|
case Flexible => ()
|
||||||
case FastToSlow => assert (equal || count(1) === deq.sink(0))
|
case FastToSlow => assert (equal || count(1) === deq.sink(0))
|
||||||
case SlowToFast => 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)
|
val deq = Wire(io.deq)
|
||||||
direction match {
|
direction match {
|
||||||
case Symmetric => io.deq <> Queue(deq, 1, pipe=true)
|
case Symmetric => io.deq <> Queue(deq, 1, pipe=true)
|
||||||
|
case Flexible => io.deq <> Queue(deq, 2)
|
||||||
case FastToSlow => io.deq <> Queue(deq, 2)
|
case FastToSlow => io.deq <> Queue(deq, 2)
|
||||||
case SlowToFast => io.deq <> deq
|
case SlowToFast => io.deq <> deq
|
||||||
}
|
}
|
||||||
@ -109,6 +125,7 @@ class RationalCrossingSink[T <: Data](gen: T, direction: RationalDirection = Sym
|
|||||||
// Ensure the clocking is setup correctly
|
// Ensure the clocking is setup correctly
|
||||||
direction match {
|
direction match {
|
||||||
case Symmetric => () // always safe
|
case Symmetric => () // always safe
|
||||||
|
case Flexible => ()
|
||||||
case FastToSlow => assert (equal || count(1) =/= enq.source(0))
|
case FastToSlow => assert (equal || count(1) =/= enq.source(0))
|
||||||
case SlowToFast => assert (equal || count(1) === enq.source(0))
|
case SlowToFast => assert (equal || count(1) === enq.source(0))
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
bb_vsrcs = \
|
bb_vsrcs = \
|
||||||
$(base_dir)/vsrc/jtag_vpi.v \
|
$(base_dir)/vsrc/jtag_vpi.v \
|
||||||
$(base_dir)/vsrc/ClockDivider2.v \
|
$(base_dir)/vsrc/ClockDivider2.v \
|
||||||
|
$(base_dir)/vsrc/ClockDivider3.v \
|
||||||
$(base_dir)/vsrc/AsyncResetReg.v \
|
$(base_dir)/vsrc/AsyncResetReg.v \
|
||||||
|
|
||||||
sim_vsrcs = \
|
sim_vsrcs = \
|
||||||
|
37
vsrc/ClockDivider3.v
Normal file
37
vsrc/ClockDivider3.v
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user