From 24dddea6dccea5a570cece78324a5db624c7303a Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Tue, 13 Aug 2019 15:06:58 +0530 Subject: Add support for asynchronous reset (#1011) Adds new AsyncReset and "abstract" Reset types. Reset is inferred in FIRRTL to be either AsyncReset or Bool. The "reset type" of a register is set by the type of its reset signal: val asyncReset: AsyncReset = IO(Input(AsyncReset())) val syncReset: Bool = IO(Input(Bool())) val abstractReset: Reset = IO(Input(Reset())) val asyncReg = withReset(asyncReset) { RegInit(0.U) } val syncReg = withReset(syncReset) { RegInit(0.U) } val inferredReg = withReset(abstractReset) { RegInit(0.U) } AsyncReset can be cast to and from Bool. Whereas synchronous reset is equivalent to a mux in front of a flip-flop and thus can be driven by logic, asynchronous reset requires that the reset value is a constant. This is checked in FIRRTL. Inference of the concrete type of a Reset occurs based on the type the Reset's drivers. This inference is very simple, it is simple forward propagation of the type, but it allows for writing blocks and modules that are agnostic to the reset type. In particular, the implicit `reset` value in MultiIOModule and thus Module is now concretely an instance of Reset and thus will be inferred in FIRRTL.--- src/test/scala/chiselTests/AsyncResetSpec.scala | 172 +++++++++++++++++++++ src/test/scala/chiselTests/CloneModuleSpec.scala | 4 +- .../CompatibilityInteroperabilitySpec.scala | 46 ++++++ src/test/scala/chiselTests/ResetSpec.scala | 50 ++++++ src/test/scala/examples/SimpleVendingMachine.scala | 2 +- 5 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 src/test/scala/chiselTests/AsyncResetSpec.scala create mode 100644 src/test/scala/chiselTests/ResetSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/AsyncResetSpec.scala b/src/test/scala/chiselTests/AsyncResetSpec.scala new file mode 100644 index 00000000..78a29e99 --- /dev/null +++ b/src/test/scala/chiselTests/AsyncResetSpec.scala @@ -0,0 +1,172 @@ +// See LICENSE for license details. + +package chiselTests + +import chisel3._ +import chisel3.util.{Counter, Queue} +import chisel3.testers.BasicTester +import firrtl.checks.CheckResets.NonLiteralAsyncResetValueException + +class AsyncResetTester extends BasicTester { + val (_, cDiv) = Counter(true.B, 4) + // First rising edge when count === 3 + val slowClk = cDiv.asClock + + val (count, done) = Counter(true.B, 16) + + val asyncResetNext = RegInit(false.B) + asyncResetNext := count === 4.U + val asyncReset = asyncResetNext.asAsyncReset + + val reg = withClockAndReset(slowClk, asyncReset) { + RegInit(123.U(8.W)) + } + reg := 5.U // Normal connection + + when (count === 3.U) { + assert(reg === 5.U) + } + when (count >= 5.U && count < 7.U) { + assert(reg === 123.U) + } .elsewhen (count >= 7.U) { + assert(reg === 5.U) + } + + when (done) { + stop() + } +} + +class AsyncResetAggregateTester extends BasicTester { + class MyBundle extends Bundle { + val x = UInt(8.W) + val y = UInt(8.W) + } + val (_, cDiv) = Counter(true.B, 4) + // First rising edge when count === 3 + val slowClk = cDiv.asClock + + val (count, done) = Counter(true.B, 16) + + val asyncResetNext = RegInit(false.B) + asyncResetNext := count === 4.U + val asyncReset = asyncResetNext.asAsyncReset + + val reg = withClockAndReset(slowClk, asyncReset) { + val init = Wire(Vec(2, new MyBundle)) + init(0).x := 0.U + init(0).y := 0.U + init(1).x := 0.U + init(1).y := 0.U + RegInit(init) + } + reg(0).x := 5.U // Normal connections + reg(0).y := 6.U + reg(1).x := 7.U + reg(1).y := 8.U + + when (count === 3.U) { + assert(reg(0).x === 5.U) + assert(reg(0).y === 6.U) + assert(reg(1).x === 7.U) + assert(reg(1).y === 8.U) + } + when (count >= 5.U && count < 7.U) { + assert(reg(0).x === 0.U) + assert(reg(0).y === 0.U) + assert(reg(1).x === 0.U) + assert(reg(1).y === 0.U) + } .elsewhen (count >= 7.U) { + assert(reg(0).x === 5.U) + assert(reg(0).y === 6.U) + assert(reg(1).x === 7.U) + assert(reg(1).y === 8.U) + } + + when (done) { + stop() + } +} + +class AsyncResetQueueTester extends BasicTester { + val (_, cDiv) = Counter(true.B, 4) + val slowClk = cDiv.asClock + + val (count, done) = Counter(true.B, 16) + + val asyncResetNext = RegNext(false.B, false.B) + val asyncReset = asyncResetNext.asAsyncReset + + val queue = withClockAndReset (slowClk, asyncReset) { + Module(new Queue(UInt(8.W), 4)) + } + queue.io.enq.valid := true.B + queue.io.enq.bits := count + + queue.io.deq.ready := false.B + + val doCheck = RegNext(false.B, false.B) + when (queue.io.count === 3.U) { + asyncResetNext := true.B + doCheck := true.B + } + when (doCheck) { + assert(queue.io.count === 0.U) + } + + when (done) { + stop() + } +} + +class AsyncResetSpec extends ChiselFlatSpec { + + behavior of "AsyncReset" + + it should "be allowed with literal reset values" in { + elaborate(new BasicTester { + withReset(reset.asAsyncReset)(RegInit(123.U)) + }) + } + + it should "NOT be allowed with non-literal reset values" in { + a [NonLiteralAsyncResetValueException] shouldBe thrownBy { + compile(new BasicTester { + val x = WireInit(123.U + 456.U) + withReset(reset.asAsyncReset)(RegInit(x)) + }) + } + } + + it should "NOT be allowed to connect directly to a Bool" in { + a [ChiselException] shouldBe thrownBy { + elaborate(new BasicTester { + val bool = Wire(Bool()) + val areset = reset.asAsyncReset + bool := areset + }) + } + } + + it should "simulate correctly" in { + assertTesterPasses(new AsyncResetTester) + } + + it should "simulate correctly with aggregates" in { + assertTesterPasses(new AsyncResetAggregateTester) + } + + it should "allow casting to and from Bool" in { + elaborate(new BasicTester { + val r: Reset = reset + val a: AsyncReset = WireInit(r.asAsyncReset) + val b: Bool = a.asBool + val c: AsyncReset = b.asAsyncReset + }) + } + + it should "allow changing the reset type of whole modules like Queue" in { + assertTesterPasses(new AsyncResetQueueTester) + } + +} diff --git a/src/test/scala/chiselTests/CloneModuleSpec.scala b/src/test/scala/chiselTests/CloneModuleSpec.scala index 59ba2eb5..ca8bd007 100644 --- a/src/test/scala/chiselTests/CloneModuleSpec.scala +++ b/src/test/scala/chiselTests/CloneModuleSpec.scala @@ -9,7 +9,7 @@ import chisel3.testers.BasicTester class MultiIOQueue[T <: Data](gen: T, val entries: Int) extends MultiIOModule { val clk = IO(Input(Clock())) - val rst = IO(Input(Bool())) + val rst = IO(Input(Reset())) val enq = IO(Flipped(EnqIO(gen))) val deq = IO(Flipped(DeqIO(gen))) val count = IO(Output(UInt(log2Ceil(entries + 1).W))) @@ -28,7 +28,7 @@ class QueueClone(multiIO: Boolean = false) extends Module { q1.rst := reset q1.enq <> io.enq q2_io("clk").asInstanceOf[Clock] := clock - q2_io("rst").asInstanceOf[Bool] := reset + q2_io("rst").asInstanceOf[Reset] := reset q2_io("enq").asInstanceOf[q1.enq.type] <> q1.deq io.deq <> q2_io("deq").asInstanceOf[q1.deq.type] io.count := q1.count + q2_io("count").asInstanceOf[q1.count.type] diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala index 861b3fdd..4ca7dcda 100644 --- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala @@ -243,5 +243,51 @@ class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec { } } } + + "Compatibility Modules" should "have Bool as their reset type" in { + compile { + import Chisel._ + class Intf extends Bundle { + val in = Bool(INPUT) + val en = Bool(INPUT) + val out = Bool(OUTPUT) + } + class Child extends Module { + val io = new Intf + io.out := Mux(io.en, io.in, reset) + } + new Module { + val io = new Intf + val child = Module(new Child) + io <> child.io + } + } + } + + "Compatibility Modules" should "be instantiable inside chisel3 Modules" in { + compile { + object Compat { + import Chisel._ + class Intf extends Bundle { + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + } + class OldMod extends Module { + val io = IO(new Intf) + io.out := Reg(next = io.in) + } + } + import chisel3._ + import Compat._ + new Module { + val io = IO(new Intf) + io <> Module(new Module { + val io = IO(new Intf) + val inst = Module(new OldMod) + io <> inst.io + }).io + } + } + } } diff --git a/src/test/scala/chiselTests/ResetSpec.scala b/src/test/scala/chiselTests/ResetSpec.scala new file mode 100644 index 00000000..297c5516 --- /dev/null +++ b/src/test/scala/chiselTests/ResetSpec.scala @@ -0,0 +1,50 @@ +// See LICENSE for license details. + +package chiselTests + +import chisel3._ +import chisel3.experimental.{IO, RawModule} +import chisel3.util.{Counter, Queue} +import chisel3.testers.BasicTester + +class ResetAgnosticModule extends RawModule { + val clk = IO(Input(Clock())) + val rst = IO(Input(Reset())) + val out = IO(Output(UInt(8.W))) + + val reg = withClockAndReset(clk, rst)(RegInit(0.U(8.W))) + reg := reg + 1.U + out := reg +} + + +class ResetSpec extends ChiselFlatSpec { + + behavior of "Reset" + + it should "allow writing modules that are reset agnostic" in { + val sync = compile(new Module { + val io = IO(new Bundle { + val out = Output(UInt(8.W)) + }) + val inst = Module(new ResetAgnosticModule) + inst.clk := clock + inst.rst := reset + assert(inst.rst.isInstanceOf[chisel3.ResetType]) + io.out := inst.out + }) + sync should include ("always @(posedge clk)") + + val async = compile(new Module { + val io = IO(new Bundle { + val out = Output(UInt(8.W)) + }) + val inst = Module(new ResetAgnosticModule) + inst.clk := clock + inst.rst := reset.asTypeOf(AsyncReset()) + assert(inst.rst.isInstanceOf[chisel3.ResetType]) + io.out := inst.out + }) + async should include ("always @(posedge clk or posedge rst)") + } +} diff --git a/src/test/scala/examples/SimpleVendingMachine.scala b/src/test/scala/examples/SimpleVendingMachine.scala index 4bb6027a..2021ece8 100644 --- a/src/test/scala/examples/SimpleVendingMachine.scala +++ b/src/test/scala/examples/SimpleVendingMachine.scala @@ -52,7 +52,7 @@ class VerilogVendingMachine extends BlackBox { // Because this is a blackbox, we must explicity add clock and reset val io = IO(new SimpleVendingMachineIO { val clock = Input(Clock()) - val reset = Input(Bool()) + val reset = Input(Reset()) }) } -- cgit v1.2.3