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 instance inline API #838

Merged
merged 2 commits into from
Aug 23, 2018
Merged

Conversation

seldridge
Copy link
Member

@seldridge seldridge commented Jun 22, 2018

This adds a circuit/module/instance inlining and flattening API. This adds three new traits:

  • InlineInstance that will inline any Module/Instance it's mixed into
  • FlattenInstance that will flatten any Module/Instance it's mixed into

This provides Chisel-level access to existing FIRRTL annotations (InlineAnnotation and FlattenAnnotation) that map to existing FIRRTL passes/transforms (InlineInstances and Flatten).

Related issue:
Resolves #602

Type of change: other enhancement

Impact: API addition (no impact on existing code)

Development Phase: implementation

Release Notes

  • Add instance inlining and flattening API

@jackkoenig jackkoenig self-requested a review June 29, 2018 20:39
* }
* }}}
*/
trait InlineInstance { self: BaseModule =>
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the trait is useful to inline all instances of a given Module, do you think we could also have a way to inline a single instance?

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I see you do it in the example! Nice :)

@aswaterman
Copy link
Member

I was just about to add something similar to rocket-chip before discovering this PR. (In fact, I was going to propose syntax identical to Module(new Bar with InlineInstance).) So, LGTM.

@aswaterman aswaterman added the Feature New feature, will be included in release notes label Jul 4, 2018
@jackkoenig jackkoenig added this to the 3.2.0 milestone Jul 6, 2018
Copy link
Contributor

@jackkoenig jackkoenig left a comment

Choose a reason for hiding this comment

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

Looks really good overall, a couple of questions/comments

def inlineComponent(m: BaseModule): Unit = chisel3.experimental.annotate(
new ChiselAnnotation with RunFirrtlTransform {
def toFirrtl = InlineAnnotation(ComponentName(m.instanceName, self.toNamed))
def transformClass = classOf[InlineInstances] }
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the utility of this alternative way of specifying that an instance should be inlined? I guess you could make a submodule in code you don't control or don't want to modify inline an instance.

Copy link
Member Author

Choose a reason for hiding this comment

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

Had to think about this as I didn't have good justification originally. If a library writer chooses to only expose a wrapped Module via a factory then this would be the only way to inline that instance. Consider:

object BarFactory {
  def apply(): Bar = Module(new Bar)
}
class Top extends Module with InlineHelpers {
  val io = IO(new Bundle{ val a = Input(Bool()) })
  val Seq(x, y, z) = Seq.fill(3)(BarFactory())
  Seq(x, y, z).map(_.io.a := io.a)
  inlineComponent(x)
}

There's nothing that I'm aware of where this type of API is the only one that is exposed (something like chisel3.util.Queue is generally used only via factory, but the underlying class is public). Nonetheless, there may be some motivation for only exposing a factory and causing that.

Do you think this is worth including or is the case here too pathological?

Copy link
Contributor

Choose a reason for hiding this comment

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

As a user I think it would be good to be able to inline a module created in a 3rd-party library without having to modify that library (promotes re-use instead of fork/copy-paste).

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry I missed this 10 days ago.

I agree that we would like to be able to inline modules constructed in factories methods, but in practice this won't work for that either. As in the case of Queue, factory methods for constructing modules typically don't return the actual module reference itself, rather they return an interface (eg. see object Queue).

I think we need to come up with another way to support that in the general case. One idea I (or @aswaterman, I can't remember who proposed it) had was to provide a mechanism similar to withClock where you could scope things below to be inlined. There could be something like inlineInstances { } or the like. Unfortunately, this would require some form of support from Chisel, either natively or some API to get references to the Modules instantiated within a block.

In any case, I think we should remove this API and just have the mixin.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, Queue was only intending to be illustrative of how a user might write a library (and hopefully pass back the module as opposed to the IO). I like the idea of inlineInstances { }, but yes, that's likely out of scope here.

As API deprecation is such a hot topic, I think it's best to err with new APIs on the side of caution. I'll remove this.

class Foo extends Module with Internals with InlineHelpers with InlineInstance
class Bar extends Module with Internals with InlineHelpers

def collectInstances(c: fir.Circuit): Array[WDefInstance] = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why Array? It's not all that important but usually we just use Seq

Copy link
Member Author

Choose a reason for hiding this comment

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

Was due to the internal use of a mutable ArrayBuffer, but that gets converted automatically. I'll switch the internal to a ListBuffer as that's all that needed and return a Seq. Consistency is good...

m
}
c map onModule
insts.toArray.map{ case WDefInstance(_, n, m, _) => WDefInstance(fir.NoInfo, n, m, fir.UnknownType) }
Copy link
Contributor

Choose a reason for hiding this comment

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

Alternatively:

insts.toArray.map(inst => inst.copy(info = fir.NoInfo, tpe = fir.UnknownType))

Copy link
Member Author

Choose a reason for hiding this comment

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

Much cleaner, thanks! I can even shave off the inst => inst.copy --> _.copy ...

* }
* }}}
*/
def inlineCircuit(): Unit = chisel3.experimental.annotate(
Copy link
Contributor

Choose a reason for hiding this comment

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

This one feels kind of dangerous. I dunno how I feel about an arbitrary point in the Module hierarchy being able to instigate inlining all modules. I would maybe make InlineCircuit as trait and have it error unless it's mixed in to the top module. Alternatively, I think Flatten in FIRRTL inlines everything below a given module? Maybe make a FlattenHierarchy trait that can be mixed in to flatten everything below and thus Inlining the whole circuit can be accomplished by mixing that in at the top?

Copy link
Member Author

Choose a reason for hiding this comment

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

I personally don't like it either... This PR only includes that because the FIRRTL inlining annotations support a CircuitName that will flatten the whole thing. I was struggling with a good API for that at the Chisel level (and will usually aim to expose whatever FIRRTL API exists at the Chisel level). I like the approach of requiring that this be mixed into the top module if that is the desired effect. Or: that's much better than "spooky inlining at a distance..."

This does bring up a point, which did not occur to me, that a Chisel flattening API should be a part of the inlining API (since they're both frontends for InlineInstances). Are you in agreement that that would round out this PR?

Copy link
Contributor

Choose a reason for hiding this comment

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

Let me check that I'm understanding correctly but I think I agree.

By Chisel flattening API do you mean some API in Chisel (eg. trait FlattenHierarchy) to flatten everything below the given module? And in adding that, we would remove def inlineCircuit()?

Copy link
Member Author

Choose a reason for hiding this comment

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

(Sorry, dropped off on this...)

Yes, I mean an API for flattening everything below a given module (an annotator that emits a firrtl.transforms.FlattenAnnotation which will run firrtl.transforms.Flatten should be all that's needed). I'm fine with removing the inlineCircuit API as it seems at worst dangerous (somebody uses this in their library) and at best confusing (SO posts asking, "Why is my circuit inlined?"). The initial motivation for inlineCircuit was only to fully expose the entire FIRRTL API of firrtl.passes.Inline, but it's far better to expose a usable sane Chisel API.

@seldridge seldridge force-pushed the issue-602 branch 2 times, most recently from bc96990 to 098758b Compare July 18, 2018 03:43
@seldridge
Copy link
Member Author

@jackkoenig: this should be good for another look. I added a trait and a method for accessing firrtl.transforms.Flatten as it's natural to include this here (since flattening is a wrapper around firrtl.passes.InlineInstances) and it seems to have been implicitly needed from your comments about circuit inlining. Related to circuit inlining, I removed the spooky inlineCircuit method.

def inlineComponent(m: BaseModule): Unit = chisel3.experimental.annotate(
new ChiselAnnotation with RunFirrtlTransform {
def toFirrtl = InlineAnnotation(ComponentName(m.instanceName, self.toNamed))
def transformClass = classOf[InlineInstances] }
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry I missed this 10 days ago.

I agree that we would like to be able to inline modules constructed in factories methods, but in practice this won't work for that either. As in the case of Queue, factory methods for constructing modules typically don't return the actual module reference itself, rather they return an interface (eg. see object Queue).

I think we need to come up with another way to support that in the general case. One idea I (or @aswaterman, I can't remember who proposed it) had was to provide a mechanism similar to withClock where you could scope things below to be inlined. There could be something like inlineInstances { } or the like. Unfortunately, this would require some form of support from Chisel, either natively or some API to get references to the Modules instantiated within a block.

In any case, I think we should remove this API and just have the mixin.

* }
* }}}
*/
def flattenComponent(m: BaseModule): Unit = chisel3.experimental.annotate(
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't love this API. It's not the worst thing in the world (it's similar to dontTouch) so I don't think it's worth blocking over but similarly to prefering some sort of inlineInstances { }-like API maybe there could be a flattenSubModules { } or something, of course it would require support in Chisel core

Copy link
Contributor

Choose a reason for hiding this comment

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

If we keep it, I think we should make it return the argument itself and probably rename it to flattenHierarchy or something which IMO is more clear about what it's doing

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll remove this as well. If a user wants this behavior, they can write their own annotator. Otherwise, we should be absolutely certain that the API we expose publicly is the one we want to support. Keeping this back in favor of a future better API makes more sense than pushing this out and then deprecating or supporting both.

@seldridge
Copy link
Member Author

@jackkoenig: After your comments and the discussion today about the need to not deprecate APIs willy-nilly, I opted to remove the questionable APIs and only provide the mixin APIs that everyone (you, @aswaterman, me) seems to like. I additionally, updated the documentation to be clear about what's going on and to do the testing more vigorously. For testing, the actual instance names are checked.

We can move forward with the inlineInstances { } API at some point. I'm in agreement that this is better than the dontTouch method style annotators.

This should be good for another look.

Copy link
Contributor

@jackkoenig jackkoenig left a comment

Choose a reason for hiding this comment

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

lgtm!

@jackkoenig
Copy link
Contributor

retest this please

@seldridge seldridge force-pushed the issue-602 branch 2 times, most recently from ae9c203 to 55110d9 Compare August 21, 2018 03:39
@seldridge
Copy link
Member Author

No clue what's going on with Jenkins. Is it applying more stringent scalastyle checks than are in the public XML? Since I was taking a look at this anyway to make sure it was passing tests/scalastyle, I went ahead and rebased it into two logical commits that both build.

One question: should we move this into chisel3.util.experimental like BoringUtils?

@jackkoenig
Copy link
Contributor

Yeah I think it would be better in chisel3.util.experimental, if you want to rebase it again, I can then do a merge commit after?

@seldridge
Copy link
Member Author

Sounds good to me. Taking care of the rebase now.

@ucbjrl
Copy link
Contributor

ucbjrl commented Aug 21, 2018

Jenkins checkstyle is configured to complain with 40 or more style violations.

image

image

@seldridge
Copy link
Member Author

@ucbjrl, thanks! That clarifies things!

@jackkoenig
Copy link
Contributor

@seldridge Do you want to rebase this (again)? Looks like another PR sneaked in ahead

This adds a new trait, InlineInstance, to chisel3.util.experimental. This
trait, when mixed into a specific module or instance, will "inline" that
module, i.e., "collapse a module while preserving it's submodules."

This includes testing (InlineSpec) and ScalaDoc documentation.

Signed-off-by: Schuyler Eldridge <[email protected]>
This adds a new trait, FlattenInstance, to chisel3.util.experimental. When
mixed into a module or a specific instance this trait will "flatten",
i.e., "inline that module and all of its submodules".

This includes testing (additions to InlineSpec) and ScalaDoc
documentation.

Signed-off-by: Schuyler Eldridge <[email protected]>
@seldridge
Copy link
Member Author

@jackkoenig: rebased and building. Thanks for the ping. It would be nice if GitHub had a "rebase and then create a merge commit" option...

@jackkoenig
Copy link
Contributor

Yeah for real, or if CI could rerun a PR against the new master automatically so that it would be fully safe to just do a merge

@jackkoenig jackkoenig merged commit 52daa37 into chipsalliance:master Aug 23, 2018
@seldridge seldridge deleted the issue-602 branch August 23, 2018 18:45
@ucbjrl ucbjrl modified the milestones: 3.2.0, 3.1.4 Dec 4, 2018
@ucbjrl ucbjrl modified the milestones: 3.1.4, 3.2.0 Dec 11, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature New feature, will be included in release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants