diff options
9 files changed, 243 insertions, 14 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Assert.scala b/chiselFrontend/src/main/scala/chisel3/core/Assert.scala index 4782a845..5673e298 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Assert.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Assert.scala @@ -51,12 +51,12 @@ object assert { // scalastyle:ignore object.name } def apply_impl_do(cond: Bool, line: String, message: Option[String], data: Bits*)(implicit sourceInfo: SourceInfo) { - when (!(cond || Builder.forcedModule.reset)) { + when (!(cond || Builder.forcedReset)) { message match { case Some(str) => printf.printfWithoutReset(s"Assertion failed: $str\n at $line\n", data:_*) case None => printf.printfWithoutReset(s"Assertion failed\n at $line\n", data:_*) } - pushCommand(Stop(sourceInfo, Node(Builder.forcedModule.clock), 1)) + pushCommand(Stop(sourceInfo, Node(Builder.forcedClock), 1)) } } @@ -76,8 +76,8 @@ object assert { // scalastyle:ignore object.name object stop { // scalastyle:ignore object.name /** Terminate execution with a failure code. */ def apply(code: Int)(implicit sourceInfo: SourceInfo): Unit = { - when (!Builder.forcedModule.reset) { - pushCommand(Stop(sourceInfo, Node(Builder.forcedModule.clock), code)) + when (!Builder.forcedReset) { + pushCommand(Stop(sourceInfo, Node(Builder.forcedClock), code)) } } diff --git a/chiselFrontend/src/main/scala/chisel3/core/Mem.scala b/chiselFrontend/src/main/scala/chisel3/core/Mem.scala index ca416b1e..a48af15a 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Mem.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Mem.scala @@ -27,7 +27,7 @@ object Mem { // TODO(twigg): Remove need for this Binding val mem = new Mem(mt, size) - pushCommand(DefMemory(sourceInfo, mem, mt, size)) // TODO multi-clock + pushCommand(DefMemory(sourceInfo, mem, mt, size)) mem } } @@ -92,7 +92,7 @@ sealed abstract class MemBase[T <: Data](t: T, val length: Int) extends HasId wi val port = pushCommand( DefMemPort(sourceInfo, - t.chiselCloneType, Node(this), dir, i.ref, Node(i._parent.get.clock)) + t.chiselCloneType, Node(this), dir, i.ref, Node(Builder.forcedClock)) ).id // Bind each element of port to being a MemoryPort Binding.bind(port, MemoryPortBinder(Builder.forcedModule), "Error: Fresh t") @@ -128,7 +128,7 @@ object SyncReadMem { // TODO(twigg): Remove need for this Binding val mem = new SyncReadMem(mt, size) - pushCommand(DefSeqMemory(sourceInfo, mem, mt, size)) // TODO multi-clock + pushCommand(DefSeqMemory(sourceInfo, mem, mt, size)) mem } } diff --git a/chiselFrontend/src/main/scala/chisel3/core/Module.scala b/chiselFrontend/src/main/scala/chisel3/core/Module.scala index 3ff68772..1388fb80 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Module.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Module.scala @@ -32,11 +32,13 @@ object Module { Builder.readyForModuleConstr = true val parent: Option[Module] = Builder.currentModule val whenDepth: Int = Builder.whenDepth + val clockAndReset: Option[ClockAndReset] = Builder.currentClockAndReset // Execute the module, this has the following side effects: // - set currentModule // - unset readyForModuleConstr // - reset whenDepth to 0 + // - set currentClockAndReset val m = bc.setRefs() m._commands.prepend(DefInvalid(childSourceInfo, m.io.ref)) // init module outputs @@ -50,6 +52,7 @@ object Module { } Builder.currentModule = parent // Back to parent! Builder.whenDepth = whenDepth + Builder.currentClockAndReset = clockAndReset // Back to clock and reset scope val ports = m.computePorts // Blackbox inherits from Module so we have to match on it first TODO fix @@ -69,6 +72,11 @@ object Module { } m } + + /** Returns the implicit Clock */ + def clock: Clock = Builder.forcedClock + /** Returns the implicit Reset */ + def reset: Bool = Builder.forcedReset } /** Abstract base class for Modules, which behave much like Verilog modules. @@ -156,6 +164,9 @@ extends HasId { val clock = Port(Input(Clock())) val reset = Port(Input(Bool())) + // Setup ClockAndReset + Builder.currentClockAndReset = Some(ClockAndReset(clock, reset)) + private[chisel3] def addId(d: HasId) { _ids += d } private[core] def ports: Seq[(String,Data)] = Vector( diff --git a/chiselFrontend/src/main/scala/chisel3/core/MultiClock.scala b/chiselFrontend/src/main/scala/chisel3/core/MultiClock.scala new file mode 100644 index 00000000..62163318 --- /dev/null +++ b/chiselFrontend/src/main/scala/chisel3/core/MultiClock.scala @@ -0,0 +1,54 @@ +// See LICENSE for license details. + +package chisel3.core + +import scala.language.experimental.macros + +import chisel3.internal._ +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.{SourceInfo} + +private[chisel3] final case class ClockAndReset(clock: Clock, reset: Bool) + +object withClockAndReset { // scalastyle:ignore object.name + /** Creates a new Clock and Reset scope + * + * @param clock the new implicit Clock + * @param reset the new implicit Reset + * @param block the block of code to run with new implicit Clock and Reset + * @return the result of the block + */ + def apply[T](clock: Clock, reset: Bool)(block: => T): T = { + // Save parentScope + val parentScope = Builder.currentClockAndReset + Builder.currentClockAndReset = Some(ClockAndReset(clock, reset)) + val res = block // execute block + // Return to old scope + Builder.currentClockAndReset = parentScope + res + } +} + +object withClock { // scalastyle:ignore object.name + /** Creates a new Clock scope + * + * @param clock the new implicit Clock + * @param block the block of code to run with new implicit Clock + * @return the result of the block + */ + def apply[T](clock: Clock)(block: => T): T = + withClockAndReset(clock, Module.reset)(block) +} + +object withReset { // scalastyle:ignore object.name + /** Creates a new Reset scope + * + * @param reset the new implicit Reset + * @param block the block of code to run with new implicit Reset + * @return the result of the block + */ + def apply[T](reset: Bool)(block: => T): T = + withClockAndReset(Module.clock, reset)(block) +} + diff --git a/chiselFrontend/src/main/scala/chisel3/core/Printf.scala b/chiselFrontend/src/main/scala/chisel3/core/Printf.scala index 4ec13751..81210f45 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Printf.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Printf.scala @@ -34,9 +34,8 @@ object printf { // scalastyle:ignore object.name * reset). If your definition of reset is not the encapsulating Module's * reset, you will need to gate this externally. * - * May be called outside of a Module (like defined in a function), so - * functions using printf make the standard Module assumptions (single clock - * and single reset). + * May be called outside of a Module (like defined in a function), uses + * whatever clock and reset are in scope. * * @param fmt printf format string * @param data format string varargs containing data to print @@ -56,13 +55,13 @@ object printf { // scalastyle:ignore object.name * @param pable [[Printable]] to print */ def apply(pable: Printable)(implicit sourceInfo: SourceInfo): Unit = { - when (!Builder.forcedModule.reset) { + when (!Builder.forcedReset) { printfWithoutReset(pable) } } private[chisel3] def printfWithoutReset(pable: Printable)(implicit sourceInfo: SourceInfo): Unit = { - val clock = Builder.forcedModule.clock + val clock = Builder.forcedClock pushCommand(Printf(sourceInfo, Node(clock), pable)) } private[chisel3] def printfWithoutReset(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo): Unit = diff --git a/chiselFrontend/src/main/scala/chisel3/core/Reg.scala b/chiselFrontend/src/main/scala/chisel3/core/Reg.scala index 30abe5e5..1287ac2f 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Reg.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Reg.scala @@ -68,7 +68,8 @@ object Reg { // to resolve all use cases. If the type inferencer / implicit resolution // system improves, this may be changed. val x = makeType(compileOptions, t, next, init) - val clock = Node(x._parent.get.clock) // TODO multi-clock + val clock = Node(Builder.forcedClock) + val reset = Node(Builder.forcedReset) // Bind each element of x to being a Reg Binding.bind(x, RegBinder(Builder.forcedModule), "Error: t") @@ -77,7 +78,7 @@ object Reg { pushCommand(DefReg(sourceInfo, x, clock)) } else { Binding.checkSynthesizable(init, s"'init' ($init)") - pushCommand(DefRegInit(sourceInfo, x, clock, Node(x._parent.get.reset), init.ref)) + pushCommand(DefRegInit(sourceInfo, x, clock, reset, init.ref)) } if (next != null) { Binding.checkSynthesizable(next, s"'next' ($next)") diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala index 9e389788..e6e69e03 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala @@ -151,6 +151,7 @@ private[chisel3] class DynamicContext() { // Used to distinguish between no Module() wrapping, multiple wrappings, and rewrapping var readyForModuleConstr: Boolean = false var whenDepth: Int = 0 // Depth of when nesting + var currentClockAndReset: Option[ClockAndReset] = None val errors = new ErrorLog val namingStack = new internal.naming.NamingStack } @@ -186,6 +187,16 @@ private[chisel3] object Builder { def whenDepth_=(target: Int): Unit = { dynamicContext.whenDepth = target } + def currentClockAndReset: Option[ClockAndReset] = dynamicContext.currentClockAndReset + def currentClockAndReset_=(target: Option[ClockAndReset]): Unit = { + dynamicContext.currentClockAndReset = target + } + def forcedClockAndReset: ClockAndReset = currentClockAndReset match { + case Some(clockAndReset) => clockAndReset + case None => throwException("Error: No implicit clock and reset.") + } + def forcedClock: Clock = forcedClockAndReset.clock + def forcedReset: Bool = forcedClockAndReset.reset // TODO(twigg): Ideally, binding checks and new bindings would all occur here // However, rest of frontend can't support this yet. diff --git a/src/main/scala/chisel3/package.scala b/src/main/scala/chisel3/package.scala index a236d3da..0ccdfc29 100644 --- a/src/main/scala/chisel3/package.scala +++ b/src/main/scala/chisel3/package.scala @@ -268,6 +268,10 @@ package object chisel3 { // scalastyle:ignore package.object.name val Analog = chisel3.core.Analog val attach = chisel3.core.attach + val withClockAndReset = chisel3.core.withClockAndReset + val withClock = chisel3.core.withClock + val withReset = chisel3.core.withReset + // Implicit conversions for BlackBox Parameters implicit def fromIntToIntParam(x: Int): IntParam = IntParam(BigInt(x)) implicit def fromLongToIntParam(x: Long): IntParam = IntParam(BigInt(x)) diff --git a/src/test/scala/chiselTests/MultiClockSpec.scala b/src/test/scala/chiselTests/MultiClockSpec.scala new file mode 100644 index 00000000..933ce2b9 --- /dev/null +++ b/src/test/scala/chiselTests/MultiClockSpec.scala @@ -0,0 +1,149 @@ +// See LICENSE for license details. + +package chiselTests + +import chisel3._ +import chisel3.experimental.{withClockAndReset, withClock, withReset} +import chisel3.util.{Counter, RegInit} +import chisel3.testers.BasicTester + +/** Multi-clock test of a Reg using a different clock via withClock */ +class ClockDividerTest extends BasicTester { + val cDiv = RegInit(true.B) // start with falling edge to simplify clock relationship assert + cDiv := !cDiv + val clock2 = cDiv.asClock + + val reg1 = RegInit(0.U(8.W)) + reg1 := reg1 + 1.U + val reg2 = withClock(clock2) { RegInit(0.U(8.W)) } + reg2 := reg2 + 1.U + + when (reg1 < 10.U) { + assert(reg2 === reg1 / 2.U) // 1:2 clock relationship + } + + when (reg1 === 10.U) { + stop() + } +} + +/** Test withReset changing the reset of a Reg */ +class WithResetTest extends BasicTester { + val reset2 = Wire(init = false.B) + val reg = withReset(reset2 || reset) { RegInit(0.U(8.W)) } + reg := reg + 1.U + + val (cycle, done) = Counter(true.B, 10) + when (cycle < 7.U) { + assert(reg === cycle) + } .elsewhen (cycle === 7.U) { + reset2 := true.B + } .elsewhen (cycle === 8.U) { + assert(reg === 0.U) + } + when (done) { stop() } +} + +/** Test Mem ports with different clocks */ +class MultiClockMemTest extends BasicTester { + val cDiv = RegInit(true.B) + cDiv := !cDiv + val clock2 = cDiv.asClock + + val mem = Mem(8, UInt(32.W)) + + val (cycle, done) = Counter(true.B, 20) + + // Write port 1 walks through writing 123 + val waddr = RegInit(0.U(3.W)) + waddr := waddr + 1.U + when (cycle < 8.U) { + mem(waddr) := 123.U + } + + val raddr = waddr - 1.U + val rdata = mem(raddr) + + // Check each write from write port 1 + when (cycle > 0.U && cycle < 9.U) { + assert(rdata === 123.U) + } + + // Write port 2 walks through writing 456 on 2nd time through + withClock(clock2) { + when (cycle >= 8.U && cycle < 16.U) { + mem(waddr) := 456.U // write 456 to different address + } + } + + // Check that every even address gets 456 + when (cycle > 8.U && cycle < 17.U) { + when (raddr % 2.U === 0.U) { + assert(rdata === 456.U) + } .otherwise { + assert(rdata === 123.U) + } + } + + when (done) { stop() } +} + +class MultiClockSpec extends ChiselFlatSpec { + + "withClock" should "scope the clock of registers" in { + assertTesterPasses(new ClockDividerTest) + } + + it should "scope ports of memories" in { + assertTesterPasses(new MultiClockMemTest) + } + + it should "return like a normal Scala block" in { + elaborate(new BasicTester { + assert(withClock(this.clock) { 5 } == 5) + }) + } + + "withReset" should "scope the reset of registers" in { + assertTesterPasses(new WithResetTest) + } + + it should "return like a normal Scala block" in { + elaborate(new BasicTester { + assert(withReset(this.reset) { 5 } == 5) + }) + } + + "withClockAndReset" should "return like a normal Scala block" in { + elaborate(new BasicTester { + assert(withClockAndReset(this.clock, this.reset) { 5 } == 5) + }) + } + + it should "scope the clocks and resets of asserts" in { + // Check that assert can fire + assertTesterFails(new BasicTester { + withClockAndReset(clock, reset) { + chisel3.assert(0.U === 1.U) + } + val (_, done) = Counter(true.B, 2) + when (done) { stop() } + }) + // Check that reset will block + assertTesterPasses(new BasicTester { + withClockAndReset(clock, true.B) { + chisel3.assert(0.U === 1.U) + } + val (_, done) = Counter(true.B, 2) + when (done) { stop() } + }) + // Check that no rising edge will block + assertTesterPasses(new BasicTester { + withClockAndReset(false.B.asClock, reset) { + chisel3.assert(0.U === 1.U) + } + val (_, done) = Counter(true.B, 2) + when (done) { stop() } + }) + } +} |
