dan-fritchman / Hdl21

Hardware Description Library
BSD 3-Clause "New" or "Revised" License
69 stars 16 forks source link

Maybe just... run the `Generator`s right away #194

Closed dan-fritchman closed 1 year ago

dan-fritchman commented 1 year ago

The general philosophy of how Hdl21 works has always included something like "defer all execution that we can".
This generally manifests as having two "eras" of execution:

  1. "Generation Time", or, like, "regular time". User code runs, generally "constructing" hardware content. This informally describes essentially all Hdl21-user-code.
  2. "Elaboration Time". Hdl21 stuff behind the scenes takes over, does most of its heavy lifting.

Most downstream consumers of Hdl21 (via VLSIR) can only use the Hdl21 stuff post-elaboration. After we have computed out things like generators, bundles, port-usages, arrays, etc. (Basically all the niceties that Hdl21 is for.) But we like those niceties! So they're all available at generation-time.

Examples of stuff deferred from generation-time to elaboration-time include:

Elaboration is essentially Hdl21's compiler. Inspired by popular compilers and by Chisel elaboration, it is organized in a series of ElabPass classes which traverse the hierarchy, generally doing a targeted single-thing at a time, e.g. "flatten bundles", "resolve port refs", or "just check that everything looks OK".

The one that's kinda special, and weird, and always goes first, is GeneratorElaborator, whose purpose is "run all the generator functions". That action has always been a part of elaboration time. There are a few reasons I can remember as to why, which may no longer be relevant. And there may be a few more that I no longer remember. The liabilities of this approach tend to come up more than the other elaborators, particularly the need to keep track of which GeneratorCalls have been executed, and which are "future" promises to do so.

The issue here: should we just run the Generators "right away" in user-code-time instead?
At minimum I wanna try it. And see if there's some similar-sized liability going the other way.

dan-fritchman commented 1 year ago

Draft of this is in #196

dan-fritchman commented 1 year ago

196 was faster than expected to implement.

And has been quite helpful thus far, especially in contexts mixing generator-calls with "other stuff". E.g. referring to result-Module attributes for simulation or for layout.

Downsides to this that I can think of:

@h.generator
def sloooooooow(p: Params) -> h.Module:
  ... 

@h.module
class GetsSlowed:
  i = sloooooooow()() # <= this runs at import-time
@h.generator
def uses_context(p: Params, ctx: Context) -> h.Module:
  # Maybe get, like, VDD and VSS signals from that `ctx` object 

@h.module 
class HasThat:
  i = uses_context(Params()) # <= note the idea is, this does not need to provide the `Context`