Closed ducky64 closed 2 years ago
Scratchpad
ALLOCATE
, but are first aggregated by port, the ALLOCATE
replaced with a concrete index, then the connect is expanded in-place.connect_array
construct)IS_CONNECTED
is known for all ports, but LENGTH
is not.IS_CONNECTED
is mainly used for generators (pin mapper) and assertions - probably not too many implications there.set_length
construct. On all declared lengths in the generator, infrastructure calls set_length
; and for other ports, the user calls set_length
.set_length
on everything. Once the generator returns, length is consistency checked (which must be done anyways). Problem: this may lead to LENGTH
being double-declared, once in whatever was assigning to it, and again in generate set_length
.set_length
is not called? The port still allows unknown-length array operations, but does not allow indexing.set_length
can be used in a normal block to declare a static array length. (would this ever be useful? alternatively, when should LENGTH
be used - which propagates length but does not allow indexing)Implications for implementation
LENGTH
field.~LENGTH
s.~LENGTH
propagation aside (since it's not directed), LENGTH
is treated like any other parameter. IS_CONNECTED
is orthogonal from LENGTH
.
LENGTH
propagation be directioned? What would it look like in the frontend?LENGTH
and output LENGTH
port arrays?LENGTH
defined on a port gates the port's elaboration, which in turn gates connect elaboration.
LENGTH
must be propagated before connect elaborates.LENGTH
propagation is included as part of connect
and export
(in terms of IR semantics), just as IS_CONNECTED
does not require a separate constraint. Internally, this will probably expand into separate parameter propagations.LENGTH
of a port is set by the sum of all ALLOCATE
d connections.
set_length
can be used to allocate output-length ports within the generator. This can be used in non-generator hardware description (though probably isn't very useful - at least until we get semantics for width mismatch connects).set_length
is only needed to get indexable ports. In general, collective operations that don't depend on the port array length (really only parallel port connects / exports?) are preferred where possible.ALLOCATE
in the context of the protos?
ALLOCATE
is currently a ReservedParam LocalStep. It gets replaced with a named LocalStep during the compilation process. Currently, there aren't array connects, so the constraint is just rewritten, but with this proposed feature array connects will need to compile / lower into multiple single connects.
From my end, the key question is: How do I have non-directional semantics for an ALLOCATE
?
In the local-step case, it would mean making both paths and array-like elements slightly more symbolic. I'd also have to explicitly bound the max size of an array, which is going to be interesting.
Finally, is there any issue with non-contiguous indices on arrays on your end? If I generate a design where an Array has elements at [0,2,5,7]
will it break anything? Or do they have to be contiguous and minimal [0,1,2,3]
? The former would make my implementation a bit simpler, and I'll be doing it regardless for an initial pass (since we don't plan to pipe things back from my solver) but it'd be something for me to keep in mind.
What do you mean non-directional semantics for ALLOCATE
?
There shouldn't be any issues with non-contiguous indices, I believe the current compiler is set up to handle arbitrary indices, though generates contiguous and minimal indices when rewriting ALLOCATE
s.
Rough roadmap:
Part 1 - Allocate support
Part 2 - Generator Support
Part 3 - LinkArray Support - this requires propagation of length (or elts) as an parameter in the compiler, across LinkArrays
Part 4 - Maximum Fun
So it turns out that this still needs more through to deal with hierarchy.
There's good use cases for elements / length propagation in both directions of hierarchy (root-inward, and leaf-outward):
However, the current compiler structure has ports fully defined when elaborating a block. Leaf-outward elements propagation means that the port's elements can't be known until the inner block is elaborated - so the port-array can't be fully elaborated until the inner block is fully defined - possibly even several levels inward.
Possible solutions:
If taking the delayed elaboration of port arrays approach:
Potential implementation
src: DigitalSource
andsinks: Array[DigitalSink]
ports, the LinkArray would have:DigitalLink
src: Array[DigitalSource]
andsinks: Array[Array[DigitalSink]]
array.src[x] <=> array.links[x].src
andarray.sinks[0][x] <=> array.links[x].sinks[0]
LENGTH
ReservedParam, which can be used on a PortArray to get a Int type corresponding to the length.LENGTH
parameter can both be a value source (eg, in a generator, get the actual length of the block's ports) and a sink (eg, set the length from a generator)ALLOCATE
ReservedParam, which can be used on a block-side PortArray to get a fresh "slice" of ports of the array. Dynamic length (length propagates to the block), and can be connected to either an element port (single link connect) or array port (link array "parallel" connect). The PortArray's length is the sum of the length of all ALLOCATE operations. Any PortArray can either be connected as a whole, or piecewise with its ALLOCATEd components (but never both - mutually exclusive).Alternatives
connect(make_array(io0, io1), link.io_array)
. But this doesn't capture the microcontroller digital IOs use case well, and would also collapse the links from multiple connect statements into one mega link-array which could make visualization and debugging less intuitive.ALLOCATE
in IR to distinguish an element connect from a vector (parallel) connect.ALLOCATE_ARRAY
for a array-typed slice vs. an element-typed slice?Mockup example
Motivating example: an abstract analog switch generator, that has n-bit AnalogSink inputs, 1 AnalogSource output, and log(n) (parallel) DigitalSink control bits.
System-level
Overview: the AnalogSwitch is connected to n analog sources at the top level (through n independent links), and a parallel log2(n) digital control line from the microcontroller. Width inference goes n analog sources -> AnalogSwitch generator runs -> defines its log2(n)-wide control line -> defines the microcontroller digital IO array length -> microcontroller generator runs
Frontend mockup
IR mockup
Analog Switch Generator
Overview: the switch generator requires the length of the analog inputs to be known before it can run, and provides the length of the digital control line.
Overall Computation Flow Mockup
Syntax Mockup
Frontend skeleton (
__init__
) mockupFrontend
generate
mockupIR mockup (post-generate; pre-generate is not interesting)