Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for asynchronous reset #1011

Merged
merged 16 commits into from
Aug 13, 2019
Merged

Add support for asynchronous reset #1011

merged 16 commits into from
Aug 13, 2019

Conversation

jackkoenig
Copy link
Contributor

@jackkoenig jackkoenig commented Feb 8, 2019

Add new AsyncReset type that extends trait Reset. The "reset-type" of a
register is set by the type of the in scope Reset:

val asyncReset: AsyncReset = IO(Input(AsyncReset()))
val syncReset: Bool = IO(Input(Bool()))

val asyncReg = withReset(asyncReset) { RegInit(0.U) }
val syncReg = withReset(syncReset) { 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.

Fixes #343

This relies on chipsalliance/firrtl#1011 and chipsalliance/firrtl#1068

Related issue:

Type of change: other enhancement

Impact: API addition (no impact on existing code)

Development Phase: implementation

Release Notes

Add support for asynchronous reset

Add new AsyncReset type that extends trait Reset. The "reset-type" of a
register is set by the type of the in scope Reset:

  val asyncReset: AsyncReset = IO(Input(AsyncReset()))
  val syncReset: Bool = IO(Input(Bool()))

  val asyncReg = withReset(asyncReset) { RegInit(0.U) }
  val syncReg = withReset(syncReset) { 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.
@jackkoenig jackkoenig requested a review from a team as a code owner February 8, 2019 20:36
Copy link
Contributor

@ducky64 ducky64 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some concerns and questions in the code comments

chiselFrontend/src/main/scala/chisel3/core/Reg.scala Outdated Show resolved Hide resolved
chiselFrontend/src/main/scala/chisel3/core/RawModule.scala Outdated Show resolved Hide resolved
src/test/scala/chiselTests/AsyncResetSpec.scala Outdated Show resolved Hide resolved
@ducky64 ducky64 added this to the 3.2.0 milestone Feb 15, 2019
@jackkoenig
Copy link
Contributor Author

jackkoenig commented Feb 15, 2019

Summary from Chisel Dev meeting 15/02/2019:

  • Make abstract type Reset instantiable in Chisel
  • Add abstract type Reset to FIRRTL
  • Reset propagation will occur in FIRRTL
  • Ensure that Chisel API to specify Reset-type in Chisel is concise and clear

Per that last point, I think the Chisel API on a Module-level is great:

class MyModule extends RawModule {
  val reset = IO(Input(AsyncReset()))
  ...
}
// elsewhere
val mod = Module(new MyModule)
mod.reset := nonAsyncReset

If nonAsyncReset is known to be a Bool by Chisel, it can error right away. If the runtime type of nonAsyncReset is just Reset, then FIRRTL will error.

The case I'm a little less clear on is how you do something like this in a function. The best I can come up with:

def myFunc(x: UInt): UInt = {
  // I need to specify that the reset *must* be async, but Module.reset may have type Reset
  val myAsyncReg = withReset(WireInit(AsyncReset(), Module.reset)) {
    RegInit(123.U)
  }
  myAsyncReg := x
  myAsyncReg
}

Note that it might feel obvious that something like this would work, but it will not:

val myAsyncReset = withReset(Module.reset.asAsyncReset) { RegInit(123.U) }

.as* casts are reinterpretations, as such myBool.asAsyncReset works just fine.

EDIT: to clarify a bit, using Module.reset.asAsyncReset will result in an async reset register, but the use case I'm thinking of is the user trying to constrain the use of this function saying that it only works in the context of AsyncReset

On the bright side, the future "check async resets are properly used" (similar to a check for legal clock crossings), will catch misuse here, so maybe this just isn't a problem

@johnsbrew
Copy link
Contributor

@jackkoenig any updates on this PR?
I'am looking forward to deploying Preset integration in chisel-tester but it first requires a proper integration of AsyncReset in chisel.
May I be of any help?

@jackkoenig
Copy link
Contributor Author

jackkoenig commented Apr 1, 2019

@johnsbrew I apologize for the delay (and then my delay in responding). I am working on it, you can see my WIP (https://github.com/freechipsproject/firrtl/tree/infer-reset). Reset inference has definitely been trickier than I thought, but I think I'm relatively close, I'll let you know when it seems to work 🙂

Copy link
Contributor

@ducky64 ducky64 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly looks good, but potential leftover / unused code.
Also, you still have a documentation TODO.

def apply(): AsyncReset = new AsyncReset
}

// TODO: Document this.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO that needs doing

@@ -43,6 +43,8 @@ object Module extends SourceInfoDoc {
// Save then clear clock and reset to prevent leaking scope, must be set again in the Module
val clockAndReset: Option[ClockAndReset] = Builder.currentClockAndReset
Builder.currentClockAndReset = None
// Record the outer reset for determining type of implicit reset in MultiIOModules
Builder.outerReset = clockAndReset.map(_.reset)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used anywhere?

chiselFrontend/src/main/scala/chisel3/core/Reg.scala Outdated Show resolved Hide resolved
src/test/scala/chiselTests/ChiselSpec.scala Outdated Show resolved Hide resolved
@jackkoenig
Copy link
Contributor Author

So there's a remaining issue with interoperability with compatibility mode.

Here are the two options I see:

  1. Make reset in chisel3.Module Reset, keep as Bool() in Chisel.Module
    • Every single time a chisel3 Module instantiates a Chisel Module, reset connection will fail
  2. Make reset in Chisel Module a Reset()
    • Problem in connections
      • Caught in Firrtl
    • Problem in Mux
      • Caught in Chisel

(1) is the current issue, it seems like it'd be the best at first blush but I think every single instantiation of a Chisel Module in chisel3 code breaks
(2) We can fix the Mux part fairly easily in Chisel I think, the connections issue is harder but maybe also okay. Both solutions feel a little haphazard but fair more constrained then the problems that fall out of (1)

@sequencer
Copy link
Member

Is AsyncReset work in the chisel3 without legacy support now?
I'm going to try to use this branch directly.

@edwardcwang
Copy link
Contributor

@jackkoenig I just noticed that a piece of code that used to work with Bool fails in VerilogEmitter when replaced with AsyncReset:

import chisel3._
import chisel3.experimental.RawModule

class Hello extends RawModule {
  val clock = IO(Input(Clock()))
  val reset = IO(Input(Reset()))
  val in = IO(Input(UInt(16.W)))
  val out = IO(Output(UInt(16.W)))
  withClockAndReset(clock, reset) {
    out := RegNext(in, 12345.U)
  }
}

class HelloWorld extends RawModule {
  val clock = IO(Input(Clock()))
  
  val mux = IO(Input(Bool()))
  val in = IO(Input(UInt(16.W)))
  val out = IO(Output(UInt(16.W)))

  // reset2 fails with AsyncReset, works with Bool()
  //val reset = IO(Input(Bool()))
  val reset = IO(Input(AsyncReset()))

  // Neither of the two variants below work if reset is AsyncReset
  // Both variants work if reset is Bool
  val reset2 = IO(chiselTypeOf(reset))
  //val reset2 = IO(Input(chiselTypeOf(reset)))

  val m = Module(new Hello)
  m.clock := clock
  m.reset := Mux(mux, reset, reset2)
  m.in := in
  out := m.out
}

chisel3.Driver.execute(Array[String](), () => new HelloWorld)

Error message:

firrtl.FIRRTLException: Internal Error! unrecognized cast: WRef(reset,AsyncResetType,PortKind,MALE)
Please file an issue at https://github.com/ucb-bar/firrtl/issues
  firrtl.Utils$.error(Utils.scala:437)
  firrtl.Utils$.throwInternalError(Utils.scala:178)
  firrtl.VerilogEmitter.cast$1(Emitter.scala:229)
  firrtl.VerilogEmitter.emit(Emitter.scala:235)
  firrtl.VerilogEmitter.$anonfun$emit$1(Emitter.scala:257)
  firrtl.VerilogEmitter.$anonfun$emit$1$adapted(Emitter.scala:257)
  scala.collection.immutable.List.foreach(List.scala:389)
  firrtl.VerilogEmitter.emit(Emitter.scala:257)
  firrtl.VerilogEmitter.$anonfun$emit$1(Emitter.scala:257)
  firrtl.VerilogEmitter.$anonfun$emit$1$adapted(Emitter.scala:257)
  scala.collection.immutable.List.foreach(List.scala:389)
  firrtl.VerilogEmitter.emit(Emitter.scala:257)
  firrtl.VerilogEmitter.emit(Emitter.scala:222)
  firrtl.VerilogEmitter$VerilogRender.$anonfun$emit_streams$5(Emitter.scala:834)
  firrtl.VerilogEmitter$VerilogRender.$anonfun$emit_streams$5$adapted(Emitter.scala:834)
  scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:59)
  scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:52)
  scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
  firrtl.VerilogEmitter$VerilogRender.emit_streams(Emitter.scala:834)
  firrtl.VerilogEmitter$VerilogRender.emit_verilog(Emitter.scala:918)
  firrtl.VerilogEmitter.$anonfun$emit$3(Emitter.scala:969)
  scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:59)
  scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:52)
  scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
  firrtl.VerilogEmitter.emit(Emitter.scala:963)
  firrtl.VerilogEmitter.$anonfun$execute$3(Emitter.scala:978)
  scala.collection.TraversableLike.$anonfun$flatMap$1(TraversableLike.scala:241)
  scala.collection.immutable.List.foreach(List.scala:389)
  scala.collection.TraversableLike.flatMap(TraversableLike.scala:241)
  scala.collection.TraversableLike.flatMap$(TraversableLike.scala:238)
  scala.collection.immutable.List.flatMap(List.scala:352)
  firrtl.VerilogEmitter.execute(Emitter.scala:975)

@jackkoenig
Copy link
Contributor Author

@edwardcwang thanks for the heads up! I'll investigate

Copy link
Contributor

@ducky64 ducky64 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few documentation nits (including a TODO that maybe needs to be done) and a question about a test.

chiselFrontend/src/main/scala/chisel3/Bits.scala Outdated Show resolved Hide resolved
chiselFrontend/src/main/scala/chisel3/Bits.scala Outdated Show resolved Hide resolved
chiselFrontend/src/main/scala/chisel3/Reg.scala Outdated Show resolved Hide resolved

behavior of "Reset"

it should "allow writing modules that are reset agnostic" in {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it also worth testing that this work with the implicit clock and reset in Module?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All tests using a Module are testing this, in particular, AsyncResetQueueTester ensures that Queue works with async reset.

@jackkoenig
Copy link
Contributor Author

Done, obviously still fails until chipsalliance/firrtl#1068 is merged

Copy link
Contributor

@ducky64 ducky64 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm.

Consider adding summary technical documentation as release notes: adds AsyncReset type and generic Reset type that can be (inferred in FIRRTL to) Bool or AsyncReset. Largely exposes FIRRTL types in the Chisel API, most of the heavy lifting is in FIRRTL.

@timsliu
Copy link

timsliu commented Mar 25, 2020

Hi there!
I've read through this thread and am trying to add asyncReset to a LazyModule. I've tried adding:

override val reset = IO(Input(AsyncReset()))

to my LazyModuleImp definition but run into a FIRRTL error. Is there documentation somewhere on how to add an async reset?

Thanks!

@jackkoenig
Copy link
Contributor Author

There's is a PR of documentation for the website that needs a little cleanup but has useful information: freechipsproject/www.chisel-lang.org#74

@timsliu
Copy link

timsliu commented Mar 25, 2020

Great, thanks for the help!

@timsliu
Copy link

timsliu commented Mar 25, 2020

The reset documentation was really helpful! I looked around and saw that RequireAsyncReset is in the chisel3 package. But when I try to compile the example:

class MyAlwaysAsyncResetModule extends Module with RequireAsyncReset {
  val myAsyncResetReg = RegInit(false.B) // reset is of type AsyncReset
}

I get a type not found error for requireAsyncReset. I think I'm on the most recent version of Chisel - is there some other package that needs to be imported?

Thanks!

@seldridge
Copy link
Member

The RequireAsyncReset is a Chisel 3.3 features (and Chisel3 3.3 hasn't been released, yet). @ucbjrl is working super hard to get a release candidate out before the weekend.

In the meantime, if you really want this feature, this may be in a snapshot release on Sonatype (oss.sonatype.org appears to be down right now...). You can also publish FIRRTL and Chisel3 locally and use that. However, I wouldn't advise either of these approaches unless you're intending to do development of a Chisel3 library.

Sorry about that.

@timsliu
Copy link

timsliu commented Mar 25, 2020

Hey seldridge,

Thanks for the follow up! Is there an alternative way to implement an async reset that doesn't require Chisel 3.3? I'm not wedded to using the RequireAsyncReset feature - really any method that works will work.

Thanks for all the hard work, and hope you're staying safe!

Tim

@mwachs5
Copy link
Contributor

mwachs5 commented Mar 25, 2020

yes, if you have a higher level wrapping module you can do something like:

val foo = withReset(reset.asAsyncReset) {
 Module(new MyModuleIWantToBeAsyncReset())
}

{use foo as normal here}

@timsliu
Copy link

timsliu commented Mar 25, 2020

Thanks for the quick reply!

We're actually looking for a top level way of making our entire design asynchronously reset instead of just a single module. Is there a top level parameter that can make every reset asynchronous rather than changing individual modules?

Thanks!

Tim

@jackkoenig
Copy link
Contributor Author

In 3.2.x, your best bet is to make your top-level module a RawModule, eg:

@chisel3.experimental.chiselName
class MyTopModule extends RawModule {
  val clock = IO(Input(Clock()))
  val reset = IO(Input(AsyncReset()))
  // rest of ports
  withClockAndReset(clock, reset) {
    // module instantiations and logic here
    // vals in here will get named by chiselName
  }
}

@mwachs5
Copy link
Contributor

mwachs5 commented Mar 25, 2020

@timsliu i had the exact same question ;p

@timsliu
Copy link

timsliu commented Mar 26, 2020

Hi everyone,

Thanks for all the quick support!

I'm using Chipyard 1.2. I tried using a RawModule but the problem I have is the Top already extends another class, and so I can't make it also extend RawModule. I've tried wrapping where my Top is built with a RawModule:

@chisel3.experimental.chiselName
class MyTopModule extends RawModule {
  val clock = IO(Input(Clock()))
  val reset = IO(Input(AsyncReset()))
  // rest of ports
  withClockAndReset(clock, reset) {
    // module instantiations and logic here
    Module(LazyModule(new Top()(p)).suggestName("top").module)
  }
}

and then using this wrapper when I instantiate Top:

case object BuildTop extends Field[Parameters => Any]((p: Parameters) => Module(new MyTopModule(p)))

But it leads to a FIRRTL error

Type mismatch in 'top.reset <= reset'
top.reset: UInt<1>
reset: AsyncReset

I'm not sure how to change the type of top.reset to also be AsyncReset.

@mwachs5 I've also tried your suggestion and built my Top like so:

case object BuildTop extends Field[Parameters => Any] ((p: Parameters) => {
  withReset(reset.asAsyncReset){
    Module(LazyModule(new Top()(p)).suggestName("top").module)
  }
}

But here reset is not found, probably b/c I'm not doing this inside a module.

Thanks!

Tim

@timsliu
Copy link

timsliu commented Mar 30, 2020

Hey again!

Just wanted to check back in and see where Chisel 3.3 is and if RequireAsyncReset is available. If not, I'll give the snapshot release a try.

Thanks!

Tim

@seldridge
Copy link
Member

Chisel 3.3.0-RC1 was published on Friday, so this is now available (and you can verify by querying the latest API documentation):

@timsliu
Copy link

timsliu commented Mar 30, 2020

Great, I'll check it out. Thanks for all the hard work on this!!

jackkoenig added a commit that referenced this pull request Feb 28, 2023
Fixes #219

* Adds AsyncResetType (similar to ClockType)
* Registers with reset signal of type AsyncResetType are async reset
  registers
* Registers with async reset can only be reset to literal values
* Add initialization logic for async reset registers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Asynchronous Reset?
9 participants