Open nathanielnrn opened 5 months ago
What specific use case requires this capability?
I believe the idea came from wanting to pass in a seq_mem
to an axi_seq_mem
wrapper as a ref
cell, then pass in the axi_seq_mem
to an axi_wrapper
as a ref
cell. I moved away from that approach, partially because it is not currently possible.
I believe there was also an idea this could be useful with respect to some arbitration logic? @anshumanmohan would have to expand on that though.
Another example I just encountered which might be a bit more contained:
An axi_seq_mem
consists of read_controller
and write_controller
which consists of a number of read channels and a number of write channels respectively. I'd like to pass in a std_reg
that lives in an axi_seq_mem
through the read_controller
to a read_channel
but this is not possible currently.
Following up based on a discussion we had at the weekly Calyx meeting.
Another, somewhat more generalized solution to this issue that @sampsyo proposed would be "scoped withs
" that I understand have come up before (couldn't find an issue for this). The proposal as I understand it is to allow generalized with
to exist within a control
block, that describes the wiring of signals/ref
cells`. So in the multiplier example we can imagine
comp main () -> () {
cells{
my_impl = implementation();
my_multiplier = multiplier(); // back to the original version
my_op = my_op();
}
control{
with my_multiplier::impl = my_impl { // within here `impl` is wired up to `my_impl`
invoke my_op[op = my_multiplier]()();
}
}
}
Another benefit is that this decouples the wiring of components from their invocations, which feels like it might be a bit constraining in the first place.
It might also make sense to allow for the assignment of single signals within with
blocks, probably all on the same level, say something like
with [my_multiplier::impl = my_impl, my_multiplier.go = controller.out, ...] {}
I believe @EclecticGriffin mentioned that it seems feasible to compile these with
blocks into the existing with
s that enable combinational groups.
It was discussed that unfortunately this new with
construct sort of couples a bunch of changes together: changes to invoke
, new with
, allowing for nested ref
cells. So this proposal might deserve its own issue and further discussion about the details of these changes.
It feels like it could be useful to allow for the passing in of cells that contain
ref
s to other components that also take inref
cells. Currently, there isn't a way to express this in the language. I imagine this will be even more powerful once subtyping #2015 is introduced.As a motivating example we can imagine a
binary_op
component that takes in a reference to some op, which itself takes in a reference to an implementation of an op. (To actually be useful this would require subtyping to allow to pass in all kinds of ops, for now we can assume this is a multiply.I can imagine a case where during one invocation of
op
we want to use a fast multiplier, and during another invocation we want to use an efficient multiplier. This could be expressed as differentimplementation
s.Note the last line passes in
my_multiplier
into an invocation ofmy_op
, butmy_multiplier
doesn't have anyimplementation
cell hooked up to it.A work around could be to create high-level components with all the cells needed by lower-level components. In the above case this would mean that the high-level
bin_op
would need a reference to animplementation
:We could then thread the concrete
implementation
found inmain
through a single invocation ofmy_op
But this seems equivalent to manually hoisting up all lower level
ref
cells and isn't very scalable.An idea that came up for how to address this via @anshumanmohan
It may be useful to hook up
ref
cells in thecells
block of a component. This would allow us to refer to "partially applied" cell instantiation within control blocks. So going back to our initial example, we could change thecells
section of main as follows.I think this would allow for the threading of concrete cells through component hierarchies by only worrying about them in the single component they are referenced in? So the only place we'd have to keep track of/instantiate an
implementation
cell would be inmultiplier
, which is exactly where aref
ofimplementation
is expected, and we wouldn't have to "hoist up" animplementation
cell intobin_op
.This might ruin some assumptions about invocations/passing in cells? Right now the only way to pass in cells to references is via
invokes
, and this would introduce such passing to cell declaration? It might be problematic w.r.t to the IR or the current passes, but hopefully this wouldn't be too big of a change. Maybe @calebmkim @sampsyo or @rachitnigam has thoughts about this aspect.Perhaps this is worth talking about in the next Calyx meeting as well?