Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 8 additions & 44 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -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)
8 changes: 5 additions & 3 deletions import.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version = 1.1.1
sbt.version = 1.9.0
2 changes: 1 addition & 1 deletion rocket-chip-revision.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
89637f6945dcd3a7f625b3d87ca585f8f6f051a8
67ceb1ddbfd1c6f50d2b4fdadf68f304f5e62287
6 changes: 3 additions & 3 deletions src/main/resources/vsrc/AsyncResetReg.v
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,23 @@ 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;
_RAND = {1{$random}};
q = _RAND[0];
`endif // RANDOMIZE
if (rst) begin
q = RESET_VALUE;
q = RESET_VALUE[0];
end
end
`endif

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
Expand Down
105 changes: 62 additions & 43 deletions src/main/scala/AsyncQueue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -93,27 +97,37 @@ 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,
// then reset allowed to happen, but write side cannot know that dequeue
// 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")
}
}

Expand All @@ -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

Expand All @@ -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")
}
}

Expand All @@ -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
Expand Down
Loading