BerkeleyHCI / PolymorphicBlocks

A work-in-progress board-level hardware description language (HDL) providing design automation through generators and block polymorphism.
BSD 3-Clause "New" or "Revised" License
72 stars 11 forks source link

Block-side Port Arrays #20

Closed ducky64 closed 2 years ago

ducky64 commented 3 years ago

Potential implementation

Alternatives

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

self.mcu = self.Block(Microcontroller())
self.analog_switch = self.Block(AnalogSwitch())
self.connect(self.analog_switch.ins.allocate(), self.analog_in0)  # single element link connect
self.connect(self.analog_switch.ins.allocate(), self.analog_in1)  # single element link connect
...
self.connect(self.mcu.digital_ios.allocate(), self.analog_switch.control)  # link-array connect

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

array_prop

  1. The three export connections define all the allocations to analog_switch.in
  2. Aggregating them, the port-array is of length 3. Solver knows analog_switch.in.length=3.
  3. With the analog_switch.in.length defined, analog_sw is ready to generate. Inside, it also defines analog_switch.ctl.length = log2Ceil(3) = 2 and instantiates those internal connections.
  4. With analog_switch.ctl.length defined, the link array is ready to elaborate. All the ports on the link array are constrained to have the same length.
  5. Elaboration runs, the effects aren't visible externally (since the length equivalence on all ports is defined in the link array, not a product of elaboration).
  6. All allocations to mcu.digital_ios are knows, and the solver knows mcu.digital_ios.length = 2.
  7. mcu can elaborate.
Syntax Mockup

Frontend skeleton (__init__) mockup

self.in = self.Port(Vector(AnalogIn()))
self.out = self.Port(AnalogOut())
self.ctl = self.Port(Vector(DigitalIn()))
self.register_generator(  # mockup API for generators that are dependent on directed constraints (eg, can only run after some parameter is resolved)
    prereqs=self.in.length,
    outputs=self.ctl.length,  # optional?
    self.generate
)

Frontend generate mockup

length = self.get(self.in.length)
self.ctl.set_length(log2(length))  # instantiates (and allows indexing into) array elements

# and in a concrete implementation, which connects the abstract interface to an internal chip
self.ic = self.Block(SomeAnalogSwitchChip())
self.connect(self.in[0], self.ic.in0)
self.connect(self.in[1], self.ic.in1)
# there would probably be logic to handle array length mismatches
...  # to n inputs

self.connect(self.out, self.ic.out)

self.connect(self.ctl[0], self.ic.ctl0)
self.connect(self.ctl[1], self.ic.ctl1)
...  # to log2(n) control lines

IR mockup (post-generate; pre-generate is not interesting)

ducky64 commented 3 years ago

Scratchpad

Implications for implementation

rohit507 commented 3 years ago
ducky64 commented 3 years ago

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.

rohit507 commented 3 years ago

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.

ducky64 commented 3 years ago

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 ALLOCATEs.

ducky64 commented 2 years ago

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

ducky64 commented 2 years ago

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: