[DRAFT] Contextual and Contexts
This is a proposal for a new type, Contextual, which allows Chisel users to define values differently for instances of the same definition, but access them only outside the instance (thus not affecting hardware generation).
This is useful for attaching different metadata to different identical instances. In the examples, we demonstrate setting absolute indexes for instances, and setting a 'who's your neighbor' value.
I am really happy with this general form of the implementation; this feature is incredibly powerful, conceptually consistent with D/I, and has pure abstractions.
Contributor Checklist
- [ ] Did you add Scaladoc to every public function/method?
- [ ] Did you add at least one test demonstrating the PR?
- [ ] Did you delete any extraneous printlns/debugging code?
- [ ] Did you specify the type of improvement?
- [ ] Did you add appropriate documentation in
docs/src? - [ ] Did you state the API impact?
- [ ] Did you specify the code generation impact?
- [ ] Did you request a desired merge strategy?
- [ ] Did you add text to be included in the Release Notes for this change?
Type of Improvement
API Impact
Backend Code Generation Impact
Desired Merge Strategy
Release Notes
Reviewer Checklist (only modified by reviewer)
- [ ] Did you add the appropriate labels?
- [ ] Did you mark the proper milestone (Bug fix:
3.4.x, [small] API extension:3.5.x, API modification or big change:3.6.0)? - [ ] Did you review?
- [ ] Did you check whether all relevant Contributor checkboxes have been checked?
- [ ] Did you mark as
Please Merge?
Another example to think about is how does this work for parameters that should be represented in-IR. This is relatively straightforward to start with for external modules because they already are kind-of parametric definitions. Two uses cases I envision:
- Passing a generator-time parameter around
- Passing an IR parameter around
Start with an external module that has an integer parameter. (The following does not compile and uses some weird syntax that kind of matches how blackboxes work now...)
@instantiable
class Bar extends ExtModule(Map("intParam" -> IntParam)) {
@public
val in = IO(Input(Bool()))
@public
val out = IO(Output(Bool()))
}
This can then either be instantiated with the parameter bound at generator time:
class Foo(generatorParam: Int) extends RawModule {
val in = IO(Input(Bool()))
val out = IO(Output(Bool()))
val barDef = Definition(new Bar)
val bar = Instance(barDef, Seq(generatorParam))
bar.in := in
out := bar.out
}
ChiselStage.emitChirrtl(new Foo(42))
Or, the parameter is threaded through from the top module:
@instantiable
class Foo2 extends RawModule {
@public
val in = IO(Input(Bool()))
@public
val out = IO(Output(Bool()))
@public
val p = Paramater[Int]
val barDef = Definition(new Bar)
val bar = Instance(barDef, Seq(p))
bar.in := in
out := bar.out
}
But @seldridge I think you are maybe missing a use case 3? These are parameters that are specific to how a thing is used, not really a property of the thing itself. While this PR gives an abstraction to couple them, we could achieve the same thing by writing aspect-oriented code over the (sub)circuit and extracting whatever information that we need. Maybe that's what we should just do and abandon any sort of object-oriented, distribution-of-concerns into the units themselves?
To me this is really the concept to conserve, not so much anything about a parameter that the module ever needs to know...
These are parameters that are specific to how a thing is used, not really a property of the thing itself.
The "how a thing is used", in Chisel at least, is equivalent to capturing some context information (a parameter) from the parent. E.g., you can use this to communicate clock domain information or (as the PR shows) the initial [x, y] coordinates for placement of some region.
While this PR gives an abstraction to couple them, we could achieve the same thing by writing aspect-oriented code over the (sub)circuit and extracting whatever information that we need. Maybe that's what we should just do and abandon any sort of object-oriented, distribution-of-concerns into the units themselves?
😅 Don't do that...
To me this is really the concept to conserve, not so much anything about a parameter that the module ever needs to know...
My main worry is that we are actually dealing with a parameterization problem and I am concerned that this is building yet-another way to kind-of do parameterization. (We really screwed up here with FIRRTL. I thought annotations were amazing only to realize years later that they are a retrofitted solution to give parametric qualities to an un-parametric IR. Failure to recognize the need for parameterization and that we were building a solution to a parameterization problem was extremely costly.)
That said, I recognize the need for an immediate solution here (and a solution that works for diplomatic graphs) and it would be ideal if whatever is used for D/I also works for Diplomacy. I will be less nervous if we treat this as a tactical solution to fix immediate problems and we are free to strategically redesign this in the future.