Open rachitnigam opened 5 years ago
Great writeup! Thanks for crafting a ticket for this frequent discussion point. Here are a few quick notes:
def
s behave macro-like) for now. This strategy seems like the most useful one; separate hardware modules strike me as necessary in larger designs but less useful in small- to medium-scale accelerators.def
s and macros is not quite perfect: unlike a typical macro, our def
s have dynamic arguments. Imagining typical macro system, you might write defmacro add(x, y) => x + y
and call it like add(50 - 8, 2)
to generate the syntax (50 - 8) + 2
. The arguments to such a macro are static, i.e., syntax fragments, and the macro builds up larger syntax out of the smaller syntax pieces. In our language, however, you don't pass syntax into our "functions"—you pass in memory resources and possibly wires (scalar values). These don't quite resemble argument passing in traditional languages, but it's also not the same as passing in static syntax. That, I think, represents the difference between macros and inlined functions—where we are currently somewhat closer to the latter.def
s fully inlined and invent a new, separate concept for defining separate hardware modules that communicate. This new thing would not contain any code of its own; it would just wrap a def
to provide shareable, synchronized access to the underlying hardware. Seems like a fun topic for deep thinking!Spatial inlines functions. See under "Using Functions".
Real reusable functions which specify how many instances of RTL blocks implement them. For example (made up syntax):
def foo{N}(...)
creates N
instances of the function foo
(using the allocation pragma). Next, calls to foo
consume
one instance of foo
:
foo(a,b); foo(a, b); // two copies consumed
And the reasoning extends to unroll
ed loops:
for (...) unroll 4 {
foo(a, b); // 4 copies consumed
}
---
regenerates copies of foo
. This claim is slightly sketchy because function calls may take multiple cycles.
foo(a, b); // N copies available
---
foo(a, b); // N copies available
The type theoretic ideas might be related to graded modalities (I might be wrong about this, but the idea of affine resources consumable a finite number of things exists).
Bonus implementation points: Multi ported memories will already need a similar style of reasoning to work.
Allowing source level reasoning for these resources will help with area-efficiency tradeoffs and maybe we can eventually infer the N
in the polymorphism extension.
Neat! I do think time steps should indeed replenish function resources, as they do memory banks—the reason being that function calls (unless we do something drastic) are synchronous, i.e., a function call waits for the entire function to finish. If we allowed async calls, this would get more complicated—the function resource would probably need to stay consumed past the time step and only get released when synchronizing the result (like forcing a future).
Macros
In the current semantics of Fuse, there are no true "functions". All
def
s in the semantics should be thought of simple macros that just get expanded at call locations. This notion corresponds to havingk
copies of RTL if there arek
calls to a functions. Furthermore, anunroll
context also increases the number of RTL blocks for a functions.For example, in this code:
there should be exactly
k + 2
copies offoo
in the hardware design.Functions
On the other hand, a true function in Fuse would represent exactly one RTL block regardless of the number of syntactic calls. This has a few implication for the semantics.
foo(...); foo(...)
since the same RTL block is being sent two signals in parallel.foo(...)
inside an unrolled loop is also incorrect, because the same RTL block is being invokedk
times in a single cycle.Obviously, this notion of functions is very restrictive.
Vivado's default
According to this SDAccel page, syntactic functions have the following defaults:
The notion of a hierarchy here is fuzzy to me (maybe @sa2257 can clarify). However, this seems to imply that all functions in emitted C++ code are true functions according to our definition.
Things to consider
We have to consider a few things before we add true functions to the language:
inline
pragma for all functions.All of these considerations will inform the design of fuse-to-rtl in the future. I suggest that for the first paper, we only think about
def
s as macros and not have any support for actual functions till Fuse 2.0.