Symbolic Resource Allocation during Sketch Generation #137
Replies: 2 comments 1 reply
-
I've added an initial implementation to PR #133 (see source code here) |
Beta Was this translation helpful? Give feedback.
-
from my slack message:
|
Beta Was this translation helpful? Give feedback.
-
Setup
In #129 I began chatting about basic outlines of sketch generation. The idea is that an architecture-agnostic sketch template is specialized to a concrete architecture-specific sketch by querying an architecture configuration to replace abstract primitive interfaces (e.g., a generic
LUT4
) with architecture-specific primitives (e.g., a Lattice ECP5 LUT4).When we specialize (or should we say implement?) a primitive interface with an arch-specific primitive we might not know all the details of the replacing value. For instance, we don't know what the lut memory should be when we provide a Lattice LUT4. Rather than specify this during sketch generation we want to make these values symbolic and use a program hole.
The problem is, when sketches get large (say we have 64 lut memories), the search space can get way too big and synthesis times can blow up. One way around this is to use the same symbolic values over and over again. It is often (usually, in fact!) the case: if we are making a 64 bit binary and function each of the 64 bits will be identical (modulo some unused bits being different).
When we wrote templates explicitly we could specify how many symbolic values we wanted:
This also provides a layer of flexibility: for instance, multiplication programs have two layers, an 'ANDing' layer, and a 'summing' layer. The 'anding' layer ands a bunch of bits and then passes these to be summed up:
At least in Lattice, bot the CCU2Cs used for summing in the second layer and the LUT4s used for ANDing in the first layer are backed by 16-bit bitvectors. Even though they have the same 'type' from a symbolic allocation standpoint, we the sketch writers know that they will be used differently and thus should be allocated differently: we don't want to use the same symbolic variables between layers.
The Problem with Sketch Generation
The Sketch Generation design decouples sketch structure and symbolic allocation, and this decoupling makes it harder to easily allocate symbolic resources depending on a program point. To be explicit I'll spell it out:
The Symbolic Resource Allocator
My solution is to have a third component which I'm calling the Symbolic Resource Allocator (SRA). The SRA should have the following properties:
I propose something like the following:
The basic idea is that
hashed
is a mutable association list mapping keys to previously allocated symbolic values, andallocator
is a method/function that querieshashed
for an already allocated symbolic resource, returning it if it exists and allocating/storing/returning the resource if it does not exist.The
allocator
function will take thesymbolic-allocator
struct, the bitwidth it is to allocate, and optionally some form of hint (this can be anything):The
bw
andhint
arguments will be used to form a key into thehashed
assoc list stored insra
(via(cons bw hint)
). By default only the bitwidth will be used to determine if a symbolic resource has been allocated yet (e.g., a bitvec16). But if the sketch designer would like to specify that different program points should have their own allocation they can pass different values to hint via(allocator sra 16 'AND-LUTS)
,(allocator sra 16 'SUM-CCU2Cs)
, etc.The above design ticks off the first three of the four requirements I mentioned above:
I haven't tackled 4 yet, but in the worst case scenario we can provide raw access to the
symbolic-allocator
or a field to manually override theallocator
function:Beta Was this translation helpful? Give feedback.
All reactions