Skip to content

Commit

Permalink
Add support for asynchronous reset (#1011)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jackkoenig committed Aug 13, 2019
1 parent fddb594 commit 24dddea
Show file tree
Hide file tree
Showing 13 changed files with 404 additions and 21 deletions.
105 changes: 104 additions & 1 deletion chiselFrontend/src/main/scala/chisel3/Bits.scala
Expand Up @@ -1183,7 +1183,106 @@ trait SIntFactory {

object SInt extends SIntFactory

sealed trait Reset extends Element with ToBoolable
sealed trait Reset extends Element with ToBoolable {
/** Casts this $coll to an [[AsyncReset]] */
final def asAsyncReset(): AsyncReset = macro SourceInfoWhiteboxTransform.noArg

/** @group SourceInfoTransformMacro */
def do_asAsyncReset(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): AsyncReset
}

object Reset {
def apply(): Reset = new ResetType
}

/** "Abstract" Reset Type inferred in FIRRTL to either [[AsyncReset]] or [[Bool]]
*
* @note This shares a common interface with [[AsyncReset]] and [[Bool]] but is not their actual
* super type due to Bool inheriting from abstract class UInt
*/
final class ResetType(private[chisel3] val width: Width = Width(1)) extends Element with Reset {
override def toString: String = s"Reset$bindingToString"

def cloneType: this.type = Reset().asInstanceOf[this.type]

private[chisel3] def typeEquivalent(that: Data): Boolean =
this.getClass == that.getClass

override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match {
case _: Reset => super.connect(that)(sourceInfo, connectCompileOptions)
case _ => super.badConnect(that)(sourceInfo)
}

override def litOption = None

/** Not really supported */
def toPrintable: Printable = PString("Reset")

override def do_asUInt(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref))

private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo,
compileOptions: CompileOptions): Unit = {
this := that
}

/** @group SourceInfoTransformMacro */
def do_asAsyncReset(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): AsyncReset =
pushOp(DefPrim(sourceInfo, AsyncReset(), AsAsyncResetOp, ref))

/** @group SourceInfoTransformMacro */
def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool =
pushOp(DefPrim(sourceInfo, Bool(), AsUIntOp, ref))

/** @group SourceInfoTransformMacro */
def do_toBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = do_asBool
}

object AsyncReset {
def apply(): AsyncReset = new AsyncReset
}

/** Data type representing asynchronous reset signals
*
* These signals are similar to [[Clock]]s in that they must be glitch-free for proper circuit
* operation. [[Reg]]s defined with the implicit reset being an [[AsyncReset]] will be
* asychronously reset registers.
*/
sealed class AsyncReset(private[chisel3] val width: Width = Width(1)) extends Element with Reset {
override def toString: String = s"AsyncReset$bindingToString"

def cloneType: this.type = AsyncReset().asInstanceOf[this.type]

private[chisel3] def typeEquivalent(that: Data): Boolean =
this.getClass == that.getClass

override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match {
case _: AsyncReset => super.connect(that)(sourceInfo, connectCompileOptions)
case _ => super.badConnect(that)(sourceInfo)
}

override def litOption = None

/** Not really supported */
def toPrintable: Printable = PString("AsyncReset")

override def do_asUInt(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref))

// TODO Is this right?
private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo,
compileOptions: CompileOptions): Unit = {
this := that.asBool.asAsyncReset
}

/** @group SourceInfoTransformMacro */
def do_asAsyncReset(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): AsyncReset = this

/** @group SourceInfoTransformMacro */
def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool =
pushOp(DefPrim(sourceInfo, Bool(), AsUIntOp, ref))

/** @group SourceInfoTransformMacro */
def do_toBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = do_asBool
}

// REVIEW TODO: Why does this extend UInt and not Bits? Does defining airth
// operations on a Bool make sense?
Expand Down Expand Up @@ -1286,6 +1385,10 @@ sealed class Bool() extends UInt(1.W) with Reset {

/** @group SourceInfoTransformMacro */
def do_asClock(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Clock = pushOp(DefPrim(sourceInfo, Clock(), AsClockOp, ref))

/** @group SourceInfoTransformMacro */
def do_asAsyncReset(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): AsyncReset =
pushOp(DefPrim(sourceInfo, AsyncReset(), AsAsyncResetOp, ref))
}

trait BoolFactory {
Expand Down
12 changes: 9 additions & 3 deletions chiselFrontend/src/main/scala/chisel3/CompileOptions.scala
Expand Up @@ -20,6 +20,8 @@ trait CompileOptions {
val checkSynthesizable: Boolean
// Require explicit assignment of DontCare to generate "x is invalid"
val explicitInvalidate: Boolean
// Should the reset type of Module be a Bool or a Reset
val inferModuleReset: Boolean
}

object CompileOptions {
Expand Down Expand Up @@ -48,7 +50,9 @@ object ExplicitCompileOptions {
// Check that referenced Data have actually been declared.
val checkSynthesizable: Boolean,
// Require an explicit DontCare assignment to generate a firrtl DefInvalid
val explicitInvalidate: Boolean
val explicitInvalidate: Boolean,
// Should the reset type of Module be a Bool or a Reset
val inferModuleReset: Boolean
) extends CompileOptions

// Collection of "not strict" connection compile options.
Expand All @@ -59,7 +63,8 @@ object ExplicitCompileOptions {
dontTryConnectionsSwapped = false,
dontAssumeDirectionality = false,
checkSynthesizable = false,
explicitInvalidate = false
explicitInvalidate = false,
inferModuleReset = false
)

// Collection of "strict" connection compile options, preferred for new code.
Expand All @@ -69,6 +74,7 @@ object ExplicitCompileOptions {
dontTryConnectionsSwapped = true,
dontAssumeDirectionality = true,
checkSynthesizable = true,
explicitInvalidate = true
explicitInvalidate = true,
inferModuleReset = true
)
}
17 changes: 7 additions & 10 deletions chiselFrontend/src/main/scala/chisel3/RawModule.scala
Expand Up @@ -143,7 +143,11 @@ abstract class MultiIOModule(implicit moduleCompileOptions: CompileOptions)
extends RawModule {
// Implicit clock and reset pins
val clock: Clock = IO(Input(Clock()))
val reset: Reset = IO(Input(Bool()))
val reset: Reset = {
// Top module and compatibility mode use Bool for reset
val inferReset = _parent.isDefined && moduleCompileOptions.inferModuleReset
IO(Input(if (inferReset) Reset() else Bool()))
}

// Setup ClockAndReset
Builder.currentClock = Some(clock)
Expand Down Expand Up @@ -219,14 +223,7 @@ abstract class LegacyModule(implicit moduleCompileOptions: CompileOptions)
pushCommand(DefInvalid(sourceInfo, io.ref))
}

override_clock match {
case Some(override_clock) => clock := override_clock
case _ => clock := Builder.forcedClock
}

override_reset match {
case Some(override_reset) => reset := override_reset
case _ => reset := Builder.forcedReset
}
clock := override_clock.getOrElse(Builder.forcedClock)
reset := override_reset.getOrElse(Builder.forcedReset)
}
}
6 changes: 3 additions & 3 deletions chiselFrontend/src/main/scala/chisel3/Reg.scala
Expand Up @@ -152,12 +152,12 @@ object RegInit {
requireIsChiselType(t, "reg type")
}
val reg = t.cloneTypeFull
val clock = Builder.forcedClock.ref
val reset = Builder.forcedReset.ref
val clock = Builder.forcedClock
val reset = Builder.forcedReset

reg.bind(RegBinding(Builder.forcedUserModule))
requireIsHardware(init, "reg initializer")
pushCommand(DefRegInit(sourceInfo, reg, clock, reset, init.ref))
pushCommand(DefRegInit(sourceInfo, reg, clock.ref, reset.ref, init.ref))
reg
}

Expand Down
Expand Up @@ -87,6 +87,10 @@ private[chisel3] object MonoConnect {
elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
case (sink_e: Clock, source_e: Clock) =>
elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
case (sink_e: AsyncReset, source_e: AsyncReset) =>
elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
case (sink_e: ResetType, source_e: Reset) =>
elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
case (sink_e: EnumType, source_e: UnsafeEnum) =>
elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
case (sink_e: EnumType, source_e: EnumType) if sink_e.typeEquivalent(source_e) =>
Expand Down
Expand Up @@ -214,6 +214,8 @@ private[chisel3] object Converter {

def extractType(data: Data, clearDir: Boolean = false): fir.Type = data match { // scalastyle:ignore cyclomatic.complexity line.size.limit
case _: Clock => fir.ClockType
case _: AsyncReset => fir.AsyncResetType
case _: ResetType => fir.ResetType
case d: EnumType => fir.UIntType(convert(d.width))
case d: UInt => fir.UIntType(convert(d.width))
case d: SInt => fir.SIntType(convert(d.width))
Expand Down
Expand Up @@ -47,6 +47,7 @@ object PrimOp {
val AsFixedPointOp = PrimOp("asFixedPoint")
val SetBinaryPoint = PrimOp("bpset")
val AsClockOp = PrimOp("asClock")
val AsAsyncResetOp = PrimOp("asAsyncReset")
}

abstract class Arg {
Expand Down
4 changes: 3 additions & 1 deletion src/main/scala/chisel3/internal/firrtl/Emitter.scala
Expand Up @@ -27,7 +27,9 @@ private class Emitter(circuit: Circuit) {

private def emitType(d: Data, clearDir: Boolean = false): String = d match { // scalastyle:ignore cyclomatic.complexity line.size.limit
case d: Clock => "Clock"
case d: EnumType => s"UInt${d.width}"
case _: AsyncReset => "AsyncReset"
case _: ResetType => "Reset"
case d: chisel3.core.EnumType => s"UInt${d.width}"
case d: UInt => s"UInt${d.width}"
case d: SInt => s"SInt${d.width}"
case d: FixedPoint => s"Fixed${d.width}${d.binaryPoint}"
Expand Down

0 comments on commit 24dddea

Please sign in to comment.