-
Notifications
You must be signed in to change notification settings - Fork 615
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
Conversation
* } | ||
* }}} | ||
*/ | ||
trait InlineInstance { self: BaseModule => |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 :)
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 |
There was a problem hiding this 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] } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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] = { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) } |
There was a problem hiding this comment.
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))
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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()
?
There was a problem hiding this comment.
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.
bc96990
to
098758b
Compare
@jackkoenig: this should be good for another look. I added a trait and a method for accessing |
def inlineComponent(m: BaseModule): Unit = chisel3.experimental.annotate( | ||
new ChiselAnnotation with RunFirrtlTransform { | ||
def toFirrtl = InlineAnnotation(ComponentName(m.instanceName, self.toNamed)) | ||
def transformClass = classOf[InlineInstances] } |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
@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 This should be good for another look. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm!
retest this please |
ae9c203
to
55110d9
Compare
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 |
Yeah I think it would be better in |
Sounds good to me. Taking care of the rebase now. |
@ucbjrl, thanks! That clarifies things! |
@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]>
@jackkoenig: rebased and building. Thanks for the ping. It would be nice if GitHub had a "rebase and then create a merge commit" option... |
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 |
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 intoFlattenInstance
that will flatten any Module/Instance it's mixed intoThis provides Chisel-level access to existing FIRRTL annotations (
InlineAnnotation
andFlattenAnnotation
) that map to existing FIRRTL passes/transforms (InlineInstances
andFlatten
).Related issue:
Resolves #602
Type of change: other enhancement
Impact: API addition (no impact on existing code)
Development Phase: implementation
Release Notes