diff --git a/build.sbt b/build.sbt index 371692c..6926eeb 100644 --- a/build.sbt +++ b/build.sbt @@ -1,51 +1,15 @@ -def scalacOptionsVersion(scalaVersion: String): Seq[String] = { - Seq() ++ { - // If we're building with Scala > 2.11, enable the compile option - // switch to support our anonymous Bundle definitions: - // https://github.com/scala/bug/issues/10047 - CrossVersion.partialVersion(scalaVersion) match { - case Some((2, scalaMajor: Long)) if scalaMajor < 12 => Seq() - case _ => Seq("-Xsource:2.11") - } - } -} +organization := "edu.berkeley.cs" -def javacOptionsVersion(scalaVersion: String): Seq[String] = { - Seq() ++ { - // Scala 2.12 requires Java 8. We continue to generate - // Java 7 compatible code for Scala 2.11 - // for compatibility with old clients. - CrossVersion.partialVersion(scalaVersion) match { - case Some((2, scalaMajor: Long)) if scalaMajor < 12 => - Seq("-source", "1.7", "-target", "1.7") - case _ => - Seq("-source", "1.8", "-target", "1.8") - } - } -} +version := "2.0.0" name := "asyncqueue-lite" -version := "1.0.0" +scalaVersion := "2.13.10" -scalaVersion := "2.12.4" +val chiselVersion = "3.6.0" -crossScalaVersions := Seq("2.11.12", "2.12.4") +libraryDependencies += "edu.berkeley.cs" %% "chisel3" % chiselVersion +libraryDependencies += "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.4" +Test / testForkedParallel := true -resolvers ++= Seq( - Resolver.sonatypeRepo("snapshots"), - Resolver.sonatypeRepo("releases") -) - -// Provide a managed dependency on X if -DXVersion="" is supplied on the command line. -val defaultVersions = Map( - "chisel3" -> "3.1.+", - "chisel-iotesters" -> "1.2.+" - ) - -libraryDependencies ++= (Seq("chisel3","chisel-iotesters").map { - dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) }) - -scalacOptions ++= scalacOptionsVersion(scalaVersion.value) - -javacOptions ++= javacOptionsVersion(scalaVersion.value) +addCompilerPlugin("edu.berkeley.cs" % "chisel3-plugin" % chiselVersion cross CrossVersion.full) diff --git a/import.sh b/import.sh index 1e2daee..11b0c58 100755 --- a/import.sh +++ b/import.sh @@ -2,8 +2,8 @@ # Helper script to update this from master rocket-chip. set -ex -#rm -rf rocket-chip -#git clone https://github.com/freechipsproject/rocket-chip.git --depth 1 +rm -rf rocket-chip +git clone https://github.com/freechipsproject/rocket-chip.git --depth 1 cd rocket-chip git rev-parse HEAD > rocket-chip-revision.txt @@ -14,9 +14,11 @@ cp rocket-chip/src/main/scala/util/AsyncQueue.scala src/main/scala cp rocket-chip/src/main/scala/util/Crossing.scala src/main/scala cp rocket-chip/src/main/scala/util/ShiftReg.scala src/main/scala cp rocket-chip/src/main/scala/util/AsyncResetReg.scala src/main/scala +cp rocket-chip/src/main/scala/util/SynchronizerReg.scala src/main/scala +cp rocket-chip/src/main/scala/util/CompileOptions.scala src/main/scala mkdir -p src/main/resources/vsrc cp rocket-chip/src/main/resources/vsrc/AsyncResetReg.v src/main/resources/vsrc/ - pushd src/main/scala sed -i "s/package freechips.rocketchip.util/package freechips.asyncqueue/g" * +sed -i "s/import freechips.rocketchip.util.CompileOptions.NotStrictInferReset//g" * popd diff --git a/project/build.properties b/project/build.properties index 210243d..8fd7d2e 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version = 1.1.1 +sbt.version = 1.9.0 diff --git a/rocket-chip-revision.txt b/rocket-chip-revision.txt index 23b41e9..ee73d0c 100644 --- a/rocket-chip-revision.txt +++ b/rocket-chip-revision.txt @@ -1 +1 @@ -89637f6945dcd3a7f625b3d87ca585f8f6f051a8 +67ceb1ddbfd1c6f50d2b4fdadf68f304f5e62287 diff --git a/src/main/resources/vsrc/AsyncResetReg.v b/src/main/resources/vsrc/AsyncResetReg.v index 067fb57..be2450e 100644 --- a/src/main/resources/vsrc/AsyncResetReg.v +++ b/src/main/resources/vsrc/AsyncResetReg.v @@ -54,7 +54,7 @@ input wire rst; // that, yet Chisel codebase is absolutely intolerant // of Xs. `ifndef SYNTHESIS - initial begin + initial begin:B0 `ifdef RANDOMIZE integer initvar; reg [31:0] _RAND; @@ -62,7 +62,7 @@ input wire rst; q = _RAND[0]; `endif // RANDOMIZE if (rst) begin - q = RESET_VALUE; + q = RESET_VALUE[0]; end end `endif @@ -70,7 +70,7 @@ input wire rst; always @(posedge clk or posedge rst) begin if (rst) begin - q <= RESET_VALUE; + q <= RESET_VALUE[0]; end else if (en) begin q <= d; end diff --git a/src/main/scala/AsyncQueue.scala b/src/main/scala/AsyncQueue.scala index 04f2ef9..9a57874 100644 --- a/src/main/scala/AsyncQueue.scala +++ b/src/main/scala/AsyncQueue.scala @@ -49,18 +49,22 @@ class AsyncBundle[T <: Data](private val gen: T, val params: AsyncQueueParams = object GrayCounter { def apply(bits: Int, increment: Bool = true.B, clear: Bool = false.B, name: String = "binary"): UInt = { val incremented = Wire(UInt(bits.W)) - val binary = AsyncResetReg(incremented, name) - incremented := Mux(clear, 0.U, binary + increment.asUInt()) + val binary = RegNext(next=incremented, init=0.U).suggestName(name) + incremented := Mux(clear, 0.U, binary + increment.asUInt) incremented ^ (incremented >> 1) } } -class AsyncValidSync(sync: Int, desc: String) extends Module { +class AsyncValidSync(sync: Int, desc: String) extends RawModule { val io = IO(new Bundle { val in = Input(Bool()) val out = Output(Bool()) }) - io.out := AsyncResetSynchronizerShiftReg(io.in, sync, Some(desc)) + val clock = IO(Input(Clock())) + val reset = IO(Input(AsyncReset())) + withClockAndReset(clock, reset){ + io.out := AsyncResetSynchronizerShiftReg(io.in, sync, Some(desc)) + } } class AsyncQueueSource[T <: Data](gen: T, params: AsyncQueueParams = AsyncQueueParams()) extends Module { @@ -74,17 +78,17 @@ class AsyncQueueSource[T <: Data](gen: T, params: AsyncQueueParams = AsyncQueueP val bits = params.bits val sink_ready = WireInit(true.B) val mem = Reg(Vec(params.depth, gen)) // This does NOT need to be reset at all. - val widx = GrayCounter(bits+1, io.enq.fire(), !sink_ready, "widx_bin") + val widx = withReset(reset.asAsyncReset)(GrayCounter(bits+1, io.enq.fire(), !sink_ready, "widx_bin")) val ridx = AsyncResetSynchronizerShiftReg(io.async.ridx, params.sync, Some("ridx_gray")) val ready = sink_ready && widx =/= (ridx ^ (params.depth | params.depth >> 1).U) val index = if (bits == 0) 0.U else io.async.widx(bits-1, 0) ^ (io.async.widx(bits, bits) << (bits-1)) when (io.enq.fire()) { mem(index) := io.enq.bits } - val ready_reg = AsyncResetReg(ready.asUInt, "ready_reg")(0) + val ready_reg = withReset(reset.asAsyncReset)(RegNext(next=ready, init=false.B).suggestName("ready_reg")) io.enq.ready := ready_reg && sink_ready - val widx_reg = AsyncResetReg(widx, "widx_gray") + val widx_reg = withReset(reset.asAsyncReset)(RegNext(next=widx, init=0.U).suggestName("widx_gray")) io.async.widx := widx_reg io.async.index match { @@ -93,18 +97,28 @@ class AsyncQueueSource[T <: Data](gen: T, params: AsyncQueueParams = AsyncQueueP } io.async.safe.foreach { sio => - val source_valid = Module(new AsyncValidSync(params.sync+1, "source_valid")) - val sink_extend = Module(new AsyncValidSync(1, "sink_extend")) - val sink_valid = Module(new AsyncValidSync(params.sync, "sink_valid")) - source_valid.reset := reset.toBool || !sio.sink_reset_n - sink_extend .reset := reset.toBool || !sio.sink_reset_n + val source_valid_0 = Module(new AsyncValidSync(params.sync, "source_valid_0")) + val source_valid_1 = Module(new AsyncValidSync(params.sync, "source_valid_1")) - source_valid.io.in := true.B - sio.widx_valid := source_valid.io.out + val sink_extend = Module(new AsyncValidSync(params.sync, "sink_extend")) + val sink_valid = Module(new AsyncValidSync(params.sync, "sink_valid")) + source_valid_0.reset := (reset.asBool || !sio.sink_reset_n).asAsyncReset + source_valid_1.reset := (reset.asBool || !sio.sink_reset_n).asAsyncReset + sink_extend .reset := (reset.asBool || !sio.sink_reset_n).asAsyncReset + sink_valid .reset := reset.asAsyncReset + + source_valid_0.clock := clock + source_valid_1.clock := clock + sink_extend .clock := clock + sink_valid .clock := clock + + source_valid_0.io.in := true.B + source_valid_1.io.in := source_valid_0.io.out + sio.widx_valid := source_valid_1.io.out sink_extend.io.in := sio.ridx_valid sink_valid.io.in := sink_extend.io.out sink_ready := sink_valid.io.out - sio.source_reset_n := !reset.toBool + sio.source_reset_n := !reset.asBool // Assert that if there is stuff in the queue, then reset cannot happen // Impossible to write because dequeue can occur on the receiving side, @@ -112,8 +126,8 @@ class AsyncQueueSource[T <: Data](gen: T, params: AsyncQueueParams = AsyncQueueP // occurred. // TODO: write some sort of sanity check assertion for users // that denote don't reset when there is activity -// assert (!(reset || !sio.sink_reset_n) || !io.enq.valid, "Enque while sink is reset and AsyncQueueSource is unprotected") -// assert (!reset_rise || prev_idx_match.toBool, "Sink reset while AsyncQueueSource not empty") + // assert (!(reset || !sio.sink_reset_n) || !io.enq.valid, "Enqueue while sink is reset and AsyncQueueSource is unprotected") + // assert (!reset_rise || prev_idx_match.asBool, "Sink reset while AsyncQueueSource not empty") } } @@ -127,7 +141,7 @@ class AsyncQueueSink[T <: Data](gen: T, params: AsyncQueueParams = AsyncQueuePar val bits = params.bits val source_ready = WireInit(true.B) - val ridx = GrayCounter(bits+1, io.deq.fire(), !source_ready, "ridx_bin") + val ridx = withReset(reset.asAsyncReset)(GrayCounter(bits+1, io.deq.fire(), !source_ready, "ridx_bin")) val widx = AsyncResetSynchronizerShiftReg(io.async.widx, params.sync, Some("widx_gray")) val valid = source_ready && ridx =/= widx @@ -141,37 +155,47 @@ class AsyncQueueSink[T <: Data](gen: T, params: AsyncQueueParams = AsyncQueuePar // be considered unless the asynchronously reset deq valid register is set. // It is possible that bits latches when the source domain is reset / has power cut // This is safe, because isolation gates brought mem low before the zeroed widx reached us - val deq_bits_nxt = Mux(valid, io.async.mem(if (params.narrow) 0.U else index), io.deq.bits) - io.deq.bits := SynchronizerShiftReg(deq_bits_nxt, sync = 1, name = Some("deq_bits_reg")) + val deq_bits_nxt = io.async.mem(if (params.narrow) 0.U else index) + io.deq.bits := ClockCrossingReg(deq_bits_nxt, en = valid, doInit = false, name = Some("deq_bits_reg")) - val valid_reg = AsyncResetReg(valid.asUInt, "valid_reg")(0) + val valid_reg = withReset(reset.asAsyncReset)(RegNext(next=valid, init=false.B).suggestName("valid_reg")) io.deq.valid := valid_reg && source_ready - val ridx_reg = AsyncResetReg(ridx, "ridx_gray") + val ridx_reg = withReset(reset.asAsyncReset)(RegNext(next=ridx, init=0.U).suggestName("ridx_gray")) io.async.ridx := ridx_reg io.async.safe.foreach { sio => - val sink_valid = Module(new AsyncValidSync(params.sync+1, "sink_valid")) - val source_extend = Module(new AsyncValidSync(1, "source_extend")) - val source_valid = Module(new AsyncValidSync(params.sync, "source_valid")) - sink_valid .reset := reset.toBool || !sio.source_reset_n - source_extend.reset := reset.toBool || !sio.source_reset_n + val sink_valid_0 = Module(new AsyncValidSync(params.sync, "sink_valid_0")) + val sink_valid_1 = Module(new AsyncValidSync(params.sync, "sink_valid_1")) - sink_valid.io.in := true.B - sio.ridx_valid := sink_valid.io.out + val source_extend = Module(new AsyncValidSync(params.sync, "source_extend")) + val source_valid = Module(new AsyncValidSync(params.sync, "source_valid")) + sink_valid_0 .reset := (reset.asBool || !sio.source_reset_n).asAsyncReset + sink_valid_1 .reset := (reset.asBool || !sio.source_reset_n).asAsyncReset + source_extend.reset := (reset.asBool || !sio.source_reset_n).asAsyncReset + source_valid .reset := reset.asAsyncReset + + sink_valid_0 .clock := clock + sink_valid_1 .clock := clock + source_extend.clock := clock + source_valid .clock := clock + + sink_valid_0.io.in := true.B + sink_valid_1.io.in := sink_valid_0.io.out + sio.ridx_valid := sink_valid_1.io.out source_extend.io.in := sio.widx_valid source_valid.io.in := source_extend.io.out source_ready := source_valid.io.out - sio.sink_reset_n := !reset.toBool - - val reset_and_extend = !source_ready || !sio.source_reset_n || reset.toBool - val reset_and_extend_prev = RegNext(reset_and_extend, true.B) - val reset_rise = !reset_and_extend_prev && reset_and_extend - val prev_idx_match = AsyncResetReg(updateData=(io.async.widx===io.async.ridx), resetData=0) + sio.sink_reset_n := !reset.asBool // TODO: write some sort of sanity check assertion for users // that denote don't reset when there is activity -// assert (!reset_rise || prev_idx_match.toBool, "Source reset while AsyncQueueSink not empty") + // + // val reset_and_extend = !source_ready || !sio.source_reset_n || reset.asBool + // val reset_and_extend_prev = RegNext(reset_and_extend, true.B) + // val reset_rise = !reset_and_extend_prev && reset_and_extend + // val prev_idx_match = AsyncResetReg(updateData=(io.async.widx===io.async.ridx), resetData=0) + // assert (!reset_rise || prev_idx_match.asBool, "Source reset while AsyncQueueSink not empty") } } @@ -197,13 +221,8 @@ object ToAsyncBundle class AsyncQueue[T <: Data](gen: T, params: AsyncQueueParams = AsyncQueueParams()) extends Crossing[T] { val io = IO(new CrossingIO(gen)) - val source = Module(new AsyncQueueSource(gen, params)) - val sink = Module(new AsyncQueueSink (gen, params)) - - source.clock := io.enq_clock - source.reset := io.enq_reset - sink.clock := io.deq_clock - sink.reset := io.deq_reset + val source = withClockAndReset(io.enq_clock, io.enq_reset) { Module(new AsyncQueueSource(gen, params)) } + val sink = withClockAndReset(io.deq_clock, io.deq_reset) { Module(new AsyncQueueSink (gen, params)) } source.io.enq <> io.enq io.deq <> sink.io.deq diff --git a/src/main/scala/AsyncResetReg.scala b/src/main/scala/AsyncResetReg.scala index 3bff7d2..1ddc530 100644 --- a/src/main/scala/AsyncResetReg.scala +++ b/src/main/scala/AsyncResetReg.scala @@ -2,9 +2,9 @@ package freechips.asyncqueue -import Chisel._ -import chisel3.util.HasBlackBoxResource -import chisel3.core.IntParam + + +import chisel3._ /** This black-boxes an Async Reset * (or Set) @@ -32,48 +32,39 @@ import chisel3.core.IntParam * */ -class AsyncResetReg(resetValue: Int = 0) - extends BlackBox(Map("RESET_VALUE" -> IntParam(resetValue))) with HasBlackBoxResource -{ - val io = new Bundle { - val d = Bool(INPUT) - val q = Bool(OUTPUT) - val en = Bool(INPUT) +class AsyncResetReg(resetValue: Int = 0) extends RawModule { + val io = IO(new Bundle { + val d = Input(Bool()) + val q = Output(Bool()) + val en = Input(Bool()) - val clk = Clock(INPUT) - val rst = Bool(INPUT) - } + val clk = Input(Clock()) + val rst = Input(Reset()) + }) - setResource("/vsrc/AsyncResetReg.v") + val reg = withClockAndReset(io.clk, io.rst.asAsyncReset)(RegInit(resetValue.U(1.W))) + when (io.en) { + reg := io.d + } + io.q := reg } class SimpleRegIO(val w: Int) extends Bundle{ - val d = UInt(INPUT, width = w) - val q = UInt(OUTPUT, width = w) - val en = Bool(INPUT) + val d = Input(UInt(w.W)) + val q = Output(UInt(w.W)) + val en = Input(Bool()) } class AsyncResetRegVec(val w: Int, val init: BigInt) extends Module { - val io = new SimpleRegIO(w) + override def desiredName = s"AsyncResetRegVec_w${w}_i${init}" - val async_regs = List.tabulate(w) { idx => - val on = if (init.testBit(idx)) 1 else 0 - Module(new AsyncResetReg(on)) - } + val io = IO(new SimpleRegIO(w)) - val q = for ((reg, idx) <- async_regs.zipWithIndex) yield { - reg.io.clk := clock - reg.io.rst := reset - reg.io.d := io.d(idx) - reg.io.en := io.en - reg.suggestName(s"reg_$idx") - reg.io.q + val reg = withReset(reset.asAsyncReset)(RegInit(init.U(w.W))) + when (io.en) { + reg := io.d } - - io.q := q.asUInt - - override def desiredName = s"AsyncResetRegVec_w${w}_i${init}" - + io.q := reg } object AsyncResetReg { @@ -83,13 +74,13 @@ object AsyncResetReg { reg.io.d := d reg.io.clk := clk reg.io.rst := rst - reg.io.en := Bool(true) + reg.io.en := true.B name.foreach(reg.suggestName(_)) reg.io.q } - def apply(d: Bool, clk: Clock, rst: Bool): Bool = apply(d, clk, rst, false, None) - def apply(d: Bool, clk: Clock, rst: Bool, name: String): Bool = apply(d, clk, rst, false, Some(name)) + def apply(d: Bool, clk: Clock, rst: Bool): Bool = apply(d, clk, rst, init = false, None) + def apply(d: Bool, clk: Clock, rst: Bool, name: String): Bool = apply(d, clk, rst, init = false, Some(name)) // Create Vectors of Registers def apply(updateData: UInt, resetData: BigInt, enable: Bool, name: Option[String] = None): UInt = { @@ -104,13 +95,13 @@ object AsyncResetReg { resetData, enable, Some(name)) - def apply(updateData: UInt, resetData: BigInt): UInt = apply(updateData, resetData, enable=Bool(true)) - def apply(updateData: UInt, resetData: BigInt, name: String): UInt = apply(updateData, resetData, enable=Bool(true), Some(name)) + def apply(updateData: UInt, resetData: BigInt): UInt = apply(updateData, resetData, enable = true.B) + def apply(updateData: UInt, resetData: BigInt, name: String): UInt = apply(updateData, resetData, enable = true.B, Some(name)) def apply(updateData: UInt, enable: Bool): UInt = apply(updateData, resetData=BigInt(0), enable) - def apply(updateData: UInt, enable: Bool, name: String): UInt = apply(updateData, resetData=BigInt(0), enable, Some(name)) + def apply(updateData: UInt, enable: Bool, name: String): UInt = apply(updateData, resetData = BigInt(0), enable, Some(name)) - def apply(updateData: UInt): UInt = apply(updateData, resetData=BigInt(0), enable=Bool(true)) - def apply(updateData: UInt, name:String): UInt = apply(updateData, resetData=BigInt(0), enable=Bool(true), Some(name)) + def apply(updateData: UInt): UInt = apply(updateData, resetData = BigInt(0), enable = true.B) + def apply(updateData: UInt, name:String): UInt = apply(updateData, resetData = BigInt(0), enable = true.B, Some(name)) } diff --git a/src/main/scala/CompileOptions.scala b/src/main/scala/CompileOptions.scala new file mode 100644 index 0000000..23e278c --- /dev/null +++ b/src/main/scala/CompileOptions.scala @@ -0,0 +1,10 @@ +// See LICENSE.SiFive for license details. + +package freechips.asyncqueue + +import chisel3.ExplicitCompileOptions.NotStrict + +object CompileOptions { + /** Compatibility mode semantics except Module implicit reset should be inferred instead of Bool */ + implicit val NotStrictInferReset = NotStrict.copy(inferModuleReset = true) +} diff --git a/src/main/scala/Crossing.scala b/src/main/scala/Crossing.scala index df1ae1d..08abfed 100644 --- a/src/main/scala/Crossing.scala +++ b/src/main/scala/Crossing.scala @@ -2,20 +2,20 @@ package freechips.asyncqueue -import Chisel._ -import chisel3.util.{DecoupledIO, Decoupled, Irrevocable, IrrevocableIO, ReadyValidIO} +import chisel3._ +import chisel3.util._ class CrossingIO[T <: Data](gen: T) extends Bundle { // Enqueue clock domain - val enq_clock = Clock(INPUT) - val enq_reset = Bool(INPUT) // synchronously deasserted wrt. enq_clock - val enq = Decoupled(gen).flip + val enq_clock = Input(Clock()) + val enq_reset = Input(Bool()) // synchronously deasserted wrt. enq_clock + val enq = Flipped(Decoupled(gen)) // Dequeue clock domain - val deq_clock = Clock(INPUT) - val deq_reset = Bool(INPUT) // synchronously deasserted wrt. deq_clock + val deq_clock = Input(Clock()) + val deq_reset = Input(Bool()) // synchronously deasserted wrt. deq_clock val deq = Decoupled(gen) } -abstract class Crossing[T <: Data] extends Module { +abstract class Crossing[T <: Data] extends RawModule { val io: CrossingIO[T] } diff --git a/src/main/scala/ShiftReg.scala b/src/main/scala/ShiftReg.scala index 90e9660..dcdc138 100644 --- a/src/main/scala/ShiftReg.scala +++ b/src/main/scala/ShiftReg.scala @@ -2,7 +2,7 @@ package freechips.asyncqueue -import Chisel._ +import chisel3._ // Similar to the Chisel ShiftRegister but allows the user to suggest a // name to the registers that get instantiated, and @@ -12,7 +12,7 @@ object ShiftRegInit { (0 until n).foldRight(in) { case (i, next) => { - val r = Reg(next, next = next, init = init) + val r = RegNext(next, init = init) name.foreach { na => r.suggestName(s"${na}_${i}") } r } @@ -26,20 +26,18 @@ object ShiftRegInit { * rather than buffering. * * The different types vary in their reset behavior: - * AsyncResetShiftReg -- This is identical to the AsyncResetSynchronizerShiftReg, - * it is just named differently to distinguish its use case. - * This is an async ShiftRegister meant for timing, - * not for synchronization. - * AsyncResetSynchronizerShiftReg -- asynchronously reset to specific value. - * SyncResetSynchronizerShiftReg -- reset to specific value. - * SynchronizerShiftReg -- no reset, pipeline only. + * AsyncResetShiftReg -- Asynchronously reset register array + * A W(width) x D(depth) sized array is constructed from D instantiations of a + * W-wide register vector. Functionally identical to AsyncResetSyncrhonizerShiftReg, + * but only used for timing applications */ abstract class AbstractPipelineReg(w: Int = 1) extends Module { - val io = new Bundle { - val d = UInt(INPUT, width = w) - val q = UInt(OUTPUT, width = w) + val io = IO(new Bundle { + val d = Input(UInt(w.W)) + val q = Output(UInt(w.W)) } + ) } object AbstractPipelineReg { @@ -61,11 +59,11 @@ class AsyncResetShiftReg(w: Int = 1, depth: Int = 1, init: Int = 0, name: String } chain.last.io.d := io.d - chain.last.io.en := Bool(true) + chain.last.io.en := true.B (chain.init zip chain.tail).foreach { case (sink, source) => sink.io.d := source.io.q - sink.io.en := Bool(true) + sink.io.en := true.B } io.q := chain.head.io.q } @@ -83,64 +81,3 @@ object AsyncResetShiftReg { def apply [T <: Chisel.Data](in: T, depth: Int, init: T): T = apply (in, depth, init.litValue.toInt, None) } - -// Note that it is important to override "name" in order to ensure that the Chisel dedup does -// not try to merge instances of this with instances of the superclass. -class AsyncResetSynchronizerShiftReg(w: Int = 1, sync: Int = 3, init: Int = 0) extends AsyncResetShiftReg(w, depth = sync, init, name = "sync") { - require(sync > 0, "Sync must be greater than 0.") - override def desiredName = s"AsyncResetSynchronizerShiftReg_w${w}_d${sync}_i${init}" -} - -object AsyncResetSynchronizerShiftReg { - def apply [T <: Chisel.Data](in: T, depth: Int, init: Int = 0, name: Option[String] = None): T = - AbstractPipelineReg(new AsyncResetSynchronizerShiftReg(in.getWidth, depth, init), in, name) - - def apply [T <: Chisel.Data](in: T, depth: Int, name: Option[String]): T = - apply(in, depth, 0, name) - - def apply [T <: Chisel.Data](in: T, depth: Int, init: T, name: Option[String]): T = - apply(in, depth, init.litValue.toInt, name) - - def apply [T <: Chisel.Data](in: T, depth: Int, init: T): T = - apply (in, depth, init.litValue.toInt, None) -} - -class SynchronizerShiftReg(w: Int = 1, sync: Int = 3) extends AbstractPipelineReg(w) { - require(sync > 0, "Sync must be greater than 0.") - - override def desiredName = s"SynchronizerShiftReg_w${w}_d${sync}" - - val syncv = List.tabulate(sync) { i => - val r = Reg(UInt(width = w)) - r.suggestName(s"sync_${i}") - } - - syncv.last := io.d - - (syncv.init zip syncv.tail).foreach { case (sink, source) => - sink := source - } - io.q := syncv.head -} - - -object SynchronizerShiftReg { - def apply [T <: Chisel.Data](in: T, sync: Int = 3, name: Option[String] = None): T = { - if (sync == 0) in - else AbstractPipelineReg(new SynchronizerShiftReg(in.getWidth, sync), in, name) - } -} - -class SyncResetSynchronizerShiftReg(w: Int = 1, sync: Int = 3, init: Int = 0) extends AbstractPipelineReg(w) { - require (sync >= 0, "Sync must be greater than or equal to 0") - - override def desiredName = s"SyncResetSynchronizerShiftReg_w${w}_d${sync}_i${init}" - - io.q := ShiftRegInit(io.d, n = sync, init = init.U, name = Some("sync")) - -} - -object SyncResetSynchronizerShiftReg { - def apply [T <: Chisel.Data](in: T, sync: Int = 3, init: T, name: Option[String] = None): T = - AbstractPipelineReg(new SyncResetSynchronizerShiftReg(in.getWidth, sync, init.litValue.toInt), in, name) -} diff --git a/src/main/scala/SynchronizerReg.scala b/src/main/scala/SynchronizerReg.scala new file mode 100644 index 0000000..07594a8 --- /dev/null +++ b/src/main/scala/SynchronizerReg.scala @@ -0,0 +1,213 @@ +// See LICENSE.SiFive for license details. + +package freechips.asyncqueue + +import chisel3._ +import chisel3.util.{RegEnable, Cat} + +/** These wrap behavioral + * shift and next registers into specific modules to allow for + * backend flows to replace or constrain + * them properly when used for CDC synchronization, + * rather than buffering. + * + * + * These are built up of *ResetSynchronizerPrimitiveShiftReg, + * intended to be replaced by the integrator's metastable flops chains or replaced + * at this level if they have a multi-bit wide synchronizer primitive. + * The different types vary in their reset behavior: + * NonSyncResetSynchronizerShiftReg -- Register array which does not have a reset pin + * AsyncResetSynchronizerShiftReg -- Asynchronously reset register array, constructed from W instantiations of D deep + * 1-bit-wide shift registers. + * SyncResetSynchronizerShiftReg -- Synchronously reset register array, constructed similarly to AsyncResetSynchronizerShiftReg + * + * [Inferred]ResetSynchronizerShiftReg -- TBD reset type by chisel3 reset inference. + * + * ClockCrossingReg -- Not made up of SynchronizerPrimitiveShiftReg. This is for single-deep flops which cross + * Clock Domains. +*/ + +object SynchronizerResetType extends Enumeration { + val NonSync, Inferred, Sync, Async = Value +} + + +// Note: this should not be used directly. +// Use the companion object to generate this with the correct reset type mixin. +private class SynchronizerPrimitiveShiftReg( + sync: Int, + init: Boolean, + resetType: SynchronizerResetType.Value) + extends AbstractPipelineReg(1) { + + val initInt = if (init) 1 else 0 + val initPostfix = resetType match { + case SynchronizerResetType.NonSync => "" + case _ => s"_i${initInt}" + } + override def desiredName = s"${resetType.toString}ResetSynchronizerPrimitiveShiftReg_d${sync}${initPostfix}" + + val chain = List.tabulate(sync) { i => + val reg = if (resetType == SynchronizerResetType.NonSync) Reg(Bool()) else RegInit(init.B) + reg.suggestName(s"sync_$i") + } + chain.last := io.d.asBool + + (chain.init zip chain.tail).foreach { case (sink, source) => + sink := source + } + io.q := chain.head.asUInt +} + +private object SynchronizerPrimitiveShiftReg { + def apply (in: Bool, sync: Int, init: Boolean, resetType: SynchronizerResetType.Value): Bool = { + val gen: () => SynchronizerPrimitiveShiftReg = resetType match { + case SynchronizerResetType.NonSync => + () => new SynchronizerPrimitiveShiftReg(sync, init, resetType) + case SynchronizerResetType.Async => + () => new SynchronizerPrimitiveShiftReg(sync, init, resetType) with RequireAsyncReset + case SynchronizerResetType.Sync => + () => new SynchronizerPrimitiveShiftReg(sync, init, resetType) with RequireSyncReset + case SynchronizerResetType.Inferred => + () => new SynchronizerPrimitiveShiftReg(sync, init, resetType) + } + AbstractPipelineReg(gen(), in) + } +} + +// Note: This module may end up with a non-AsyncReset type reset. +// But the Primitives within will always have AsyncReset type. +class AsyncResetSynchronizerShiftReg(w: Int = 1, sync: Int, init: Int) + extends AbstractPipelineReg(w) { + require(sync > 1, s"Sync must be greater than 1, not ${sync}.") + override def desiredName = s"AsyncResetSynchronizerShiftReg_w${w}_d${sync}_i${init}" + val output = Seq.tabulate(w) { i => + val initBit = ((init >> i) & 1) > 0 + withReset(reset.asAsyncReset){ + SynchronizerPrimitiveShiftReg(io.d(i), sync, initBit, SynchronizerResetType.Async) + } + } + io.q := Cat(output.reverse) +} + +object AsyncResetSynchronizerShiftReg { + def apply [T <: Data](in: T, sync: Int, init: Int, name: Option[String] = None): T = + AbstractPipelineReg(new AsyncResetSynchronizerShiftReg(in.getWidth, sync, init), in, name) + + def apply [T <: Data](in: T, sync: Int, name: Option[String]): T = + apply (in, sync, 0, name) + + def apply [T <: Data](in: T, sync: Int): T = + apply (in, sync, 0, None) + + def apply [T <: Data](in: T, sync: Int, init: T, name: Option[String]): T = + apply(in, sync, init.litValue.toInt, name) + + def apply [T <: Data](in: T, sync: Int, init: T): T = + apply (in, sync, init.litValue.toInt, None) +} + +// Note: This module may end up with a non-Bool type reset. +// But the Primitives within will always have Bool reset type. +@deprecated("SyncResetSynchronizerShiftReg is unecessary with Chisel3 inferred resets. Use ResetSynchronizerShiftReg which will use the inferred reset type.", "rocket-chip 1.2") +class SyncResetSynchronizerShiftReg(w: Int = 1, sync: Int, init: Int) extends AbstractPipelineReg(w) { + require(sync > 1, s"Sync must be greater than 1, not ${sync}.") + override def desiredName = s"SyncResetSynchronizerShiftReg_w${w}_d${sync}_i${init}" + val output = Seq.tabulate(w) { i => + val initBit = ((init >> i) & 1) > 0 + withReset(reset.asBool){ + SynchronizerPrimitiveShiftReg(io.d(i), sync, initBit, SynchronizerResetType.Sync) + } + } + io.q := Cat(output.reverse) +} + +object SyncResetSynchronizerShiftReg { + def apply [T <: Data](in: T, sync: Int, init: Int, name: Option[String] = None): T = + if (sync == 0) in else AbstractPipelineReg(new SyncResetSynchronizerShiftReg(in.getWidth, sync, init), in, name) + + def apply [T <: Data](in: T, sync: Int, name: Option[String]): T = + apply (in, sync, 0, name) + + def apply [T <: Data](in: T, sync: Int): T = + apply (in, sync, 0, None) + + def apply [T <: Data](in: T, sync: Int, init: T, name: Option[String]): T = + apply(in, sync, init.litValue.toInt, name) + + def apply [T <: Data](in: T, sync: Int, init: T): T = + apply (in, sync, init.litValue.toInt, None) +} + +class ResetSynchronizerShiftReg(w: Int = 1, sync: Int, init: Int) extends AbstractPipelineReg(w) { + require(sync > 1, s"Sync must be greater than 1, not ${sync}.") + override def desiredName = s"ResetSynchronizerShiftReg_w${w}_d${sync}_i${init}" + val output = Seq.tabulate(w) { i => + val initBit = ((init >> i) & 1) > 0 + SynchronizerPrimitiveShiftReg(io.d(i), sync, initBit, SynchronizerResetType.Inferred) + } + io.q := Cat(output.reverse) +} + +object ResetSynchronizerShiftReg { + def apply [T <: Data](in: T, sync: Int, init: Int, name: Option[String] = None): T = + AbstractPipelineReg(new ResetSynchronizerShiftReg(in.getWidth, sync, init), in, name) + + def apply [T <: Data](in: T, sync: Int, name: Option[String]): T = + apply (in, sync, 0, name) + + def apply [T <: Data](in: T, sync: Int): T = + apply (in, sync, 0, None) + + def apply [T <: Data](in: T, sync: Int, init: T, name: Option[String]): T = + apply(in, sync, init.litValue.toInt, name) + + def apply [T <: Data](in: T, sync: Int, init: T): T = + apply (in, sync, init.litValue.toInt, None) +} + +class SynchronizerShiftReg(w: Int = 1, sync: Int = 3) extends AbstractPipelineReg(w) { + require(sync > 1, s"Sync must be greater than 1, not ${sync}.") + override def desiredName = s"SynchronizerShiftReg_w${w}_d${sync}" + val output = Seq.tabulate(w) { i => + SynchronizerPrimitiveShiftReg(io.d(i), sync, false, SynchronizerResetType.NonSync) + } + io.q := Cat(output.reverse) +} + +object SynchronizerShiftReg { + def apply [T <: Data](in: T, sync: Int, name: Option[String] = None): T = + if (sync == 0) in else AbstractPipelineReg(new SynchronizerShiftReg(in.getWidth, sync), in, name) + + def apply [T <: Data](in: T, sync: Int): T = + apply (in, sync, None) + + def apply [T <: Data](in: T): T = + apply (in, 3, None) + + +} + +class ClockCrossingReg(w: Int = 1, doInit: Boolean) extends Module { + + override def desiredName = s"ClockCrossingReg_w${w}" + + val io = IO(new Bundle{ + val d = Input(UInt(w.W)) + val q = Output(UInt(w.W)) + val en = Input(Bool()) + }) + + val cdc_reg = if (doInit) RegEnable(io.d, 0.U(w.W), io.en) else RegEnable(io.d, io.en) + io.q := cdc_reg +} + +object ClockCrossingReg { + def apply [T <: Data](in: T, en: Bool, doInit: Boolean, name: Option[String] = None): T = { + val cdc_reg = Module(new ClockCrossingReg(in.getWidth, doInit)) + name.foreach{ cdc_reg.suggestName(_) } + cdc_reg.io.d := in.asUInt + cdc_reg.io.en := en + cdc_reg.io.q.asTypeOf(in) + } +}