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

Refactor Annotations #767

Merged
merged 12 commits into from
Mar 1, 2018
Merged

Refactor Annotations #767

merged 12 commits into from
Mar 1, 2018

Conversation

jackkoenig
Copy link
Contributor

This makes it much easier to write annotations. As an example, check out identity annotation from the tests:

// Annotation used in Firrtl
case class IdentityAnnotation(target: Named, value: String) extends SingleTargetAnnotation[Named] {
  def duplicate(n: Named) = this.copy(target = n)
}
object identify {
  // Convenient constructor for this annotation in Chisel code on Chisel InstanceIds
  def apply(component: InstanceId, value: String): Unit = {
    val anno = LazyAnnotation(() => IdentityAnnotation(component.toNamed, value))
    annotate(anno)
    // We have a transform we want to run whenever something is identified
    annotate(RunTransformAnnotation(classOf[IdentityTransform]))
   }
 }
  • Related issue

This is a companion to chipsalliance/firrtl#721

  • Type of change

    • Bug report
    • Feature request
    • Other enhancement
  • Impact

    • no functional change
    • API addition (no impact on existing code)
    • API modification
  • Development Phase

    • proposal
    • implementation
  • Release Notes

    • Updates Chisel to new JSON-based Annotation system in Firrtl (see Refactor Annotations firrtl#721).
    • Deprecates ChiselAnnotation in favor of LazyAnnotation.
    • Deprecates Module.annotate in favor of object annotate
    • Adds .toNamed to InstanceId so that we can easily generate Firrtl references to Chisel objects.

This still uses LegacyAnnotations and so also serves to test the
LegacyAnnotation support in Firrtl
This allows us to delay creation of Annotations till elaboration is
complete. Also update all annotation-related code.
@jackkoenig jackkoenig added this to the 3.1.0 milestone Jan 27, 2018
@jackkoenig
Copy link
Contributor Author

TODO

  • Add ScalaDoc

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.

Looks sane, I had some thoughts about naming APIs.

What's the rationale for LazyAnnotation? Is lazy simply an implementation detail that users don't need to care about, or does it affect the behavior of the API?

@@ -129,6 +133,11 @@ private[chisel3] trait HasId extends InstanceId {
case Some(p) => p.name
case None => throwException(s"$instanceName doesn't have a parent")
}
// TODO Should this be public?
Copy link
Contributor

Choose a reason for hiding this comment

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

For Testers, we do need an API to get the FIRRTL name of a Chisel object. How that should be done (through DataMirror maybe?) and specifics (name qualified up to which level? Top? User-defined root?) are questions though.

/** Holds the implementation of toNamed for Data and MemBase */
private[chisel3] trait NamedComponent extends HasId {
/** Returns a FIRRTL ComponentName that references this object
* @note Should not be called until circuit elaboration is complete
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps not a now thing, but can we relax when this can be called? For example, it should be possible to get the name of any bound wire, assuming it's properly named (instead of T_, which is kind of meaningless anyways) - though we might need to ensure that these names can't be mangled in the future. Going up the module chain also presents difficulties in this case, you never know when an enclosing module might not have a proper name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think making it possible to eagerly access the names of things is desirable but it's a non-trivial undertaking. We would need to ensure that querying the name of something does not impact the result. For example:

class NamingTop extends RawModule {
  val w = Wire(UInt(8.W)).suggestName("foo")
  w.getName // ???
  val foo = IO(Input(UInt(8.W)))
  w := foo
}

Currently, w gets the name foo_1 because ports always win. We would need to be sure that calling getName did not accidentally change this.

Doing this with reflection also sounds... dangerous. Perhaps we could make @chiselName disable the reflection-based naming and make eager-naming a feature of using @chiselName?

Copy link
Contributor

Choose a reason for hiding this comment

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

I still can't say I'm a fan of an API that depends so much on mutable state where access can be unsafe. Are there other possible APIs, like ChiselAnnotation.toFirrtl taking in a object that can remap a component to a name?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What is the difference between putting the code here in NamedComponent vs. in ChiselAnnotation? In either case, getting a Firrtl Named from a Chisel object relies on mutable state in that object that isn't set until the end of elaboration. This PR is not changing anything about that aspect of Chisel's implementation.

@ducky64
Copy link
Contributor

ducky64 commented Feb 10, 2018

Lazy seems like "we're abusing this Scala language feature because we're too lazy [heh] to write the plumbing properly" and not balancing the usability / learnability concerns with the implementation effort, probably partially because we're Scala power users and kind of feel that users are also at a similar level.

I think the general convention for lazy evaluation is not that it gives you a different result because of execution order, but it gives you the same result with different performance characteristics. In other words, with few exceptions (for example, infinite lists), lazy is supposed to give the same result as the non-lazy version, which is not the case here.

For this PR in particular, I think Lazy is meant to be a way to get the target name, right? Can we not leave the serialization of the annotation (which is where the name is needed) to the FIRRTL emit stage, passing in a Chisel Data/Module/... reference at the annotation stage? My experience is that the () => ... notation is unnecessarily confusing, and those that don't understand Chisel execution order (which in my opinion is implementation rather than interface) won't understand the motivation and might instead think it's just a poorly designed API. This notation is also not foolproof, as you can pass in a function to an eagerly-evaluated value, and would likely create a very unintuitive error message.

@jackkoenig
Copy link
Contributor Author

jackkoenig commented Feb 13, 2018

I agree this is kind of an abuse of the term "lazy". It would probably be better to be more explicit and call it something like DelayedAnnotation.

I designed this to require the minimum amount of boiler plate for annotation writers. Using a reference to the Chisel object is possible, but in my opinion it leads to a worse overall API. I'll lay out the two ways I have thought of to do it that way:

  1. Duplicate the Annotation
// Consider some Annotation with associated information
case class MyAnnotation(target: ComponentName, info: Map[String, Int]) extends SingleTargetAnno[ComponentName] {
  def duplicate(n: ComponentName) = this.copy(target = n)
}
// I could duplicate the datastructure to also support a Chisel Data
case class MyAnnotation2(target: Data, info: Map[String, Int]) {
  // I would also have to call out what other annotation it should correspond to, 
  // and AFAIK it's impossible to have static type safety that this class corresponds to the other one
  def companionAnno = classOf[MyAnnotation]
}
  1. Extend Firrtl's Named to allow "Lazy" or "Delayed" Names

This one is probably better but has some potential issues. I could open up the Named in Firrtl (it's currently sealed) to allow Chisel types to extend it. If we want any type safety, we'd have BaseModule extend ModuleName and Data extends ComponentName and have some notion of a Lazy or Delayed Name. This then means that whenever the user uses that annotation, they have to check that the name is actually known. This would add boiler plate to use. Alternatively, I could overwrite the unapply methods on ComponentName and ModuleName and put the dynamic check there.

My main issue with 2. is that there's a bit of magic going on behind the scenes. I suspect that people could run into subtle issues trying to match on Chisel types in Firrtl transformations (which would actually work so long as you didn't serialize between Chisel and Firrtl!) The proposal on this PR is the least magical and most explicit way to do it. Sure, () => is confusing to new users, but if someone is writing custom annotations, they're most certainly also writing custom transformations which is a whole larger bag of worms then learning what a function as an argument is.

Use a Chisel-specific RunFirrtlTransform API to preserve behavior of old
ChiselAnnotation (now called ChiselLegacyAnnotation)
@jackkoenig
Copy link
Contributor Author

Updated this with a more explicit ChiselAnnotation trait that should be extended in Chisel libraries that use Annotations. You can still create anonymous ones if you don't want the boiler plate, but they do provide a reasonable API for corresponding Annotations in Chisel-land and Firrtl-land.

I also add RunFirrtlTransform to preserve behavior of Driver.execute instantiating Firrtl transforms associated with a given Annotation.

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.

Two comments

object identify {
def apply(component: InstanceId, value: String): Unit = {
val anno = IdentityChiselAnnotation(component, value)
annotate(anno)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is annotate called twice?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mistakenly left an old API, fixed

/** Holds the implementation of toNamed for Data and MemBase */
private[chisel3] trait NamedComponent extends HasId {
/** Returns a FIRRTL ComponentName that references this object
* @note Should not be called until circuit elaboration is complete
Copy link
Contributor

Choose a reason for hiding this comment

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

I still can't say I'm a fan of an API that depends so much on mutable state where access can be unsafe. Are there other possible APIs, like ChiselAnnotation.toFirrtl taking in a object that can remap a component to a name?

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.

looks good, current understanding is that order-dependent stuff will fail noisily if invoked improperly.

@jackkoenig
Copy link
Contributor Author

retest this please

@jackkoenig
Copy link
Contributor Author

Can any one reproduce the failure I'm seeing on Jenkins (DontTouchSpec failing)? I can't seem to reproduce it.

@ucbjrl
Copy link
Contributor

ucbjrl commented Feb 28, 2018 via email

@jackkoenig
Copy link
Contributor Author

Well, ensuring the tests each had their own test directory fixed the problem 🤷‍♀️. Anyway for rocket-chip bumping reasons, I'm going to merge #788 first. Then I'll merge this.

@jackkoenig jackkoenig merged commit 4655343 into master Mar 1, 2018
ucbjrl added a commit to freechipsproject/firrtl-interpreter that referenced this pull request Mar 1, 2018
ucbjrl added a commit to freechipsproject/chisel-testers that referenced this pull request Mar 1, 2018
ucbjrl added a commit to freechipsproject/chisel-testers that referenced this pull request Mar 1, 2018
* Update in light of annotations refactor.
Fix for chipsalliance/firrtl#721 and chipsalliance/chisel#767.

* Respond to review comments - simplify annotations.
ucbjrl added a commit to freechipsproject/firrtl-interpreter that referenced this pull request Mar 2, 2018
ucbjrl added a commit that referenced this pull request Mar 9, 2018
* Update in light of annotations refactor.
Fix for chipsalliance/firrtl#721 and #767.

* Respond to review comments - simplify annotations.
@jackkoenig jackkoenig deleted the refactor-annos branch May 15, 2018 22:26
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.

3 participants