Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecating @static and what to replace it with #1896

Closed
calebmkim opened this issue Feb 5, 2024 · 6 comments
Closed

Deprecating @static and what to replace it with #1896

calebmkim opened this issue Feb 5, 2024 · 6 comments

Comments

@calebmkim
Copy link
Contributor

calebmkim commented Feb 5, 2024

(Please let me know if any of this is unclear)

Continuing on #1429, I have a PR that is close to being ready to deprecate @static. If we merge the PR, this is what the state of static will/should be:

static<n> and promotable

  1. static<n> can go on groups, control, and components to provide a guarantee for static timing (this is unchanged from before). When placed on a component, it provides no done signal. You assert the go signal on the 0th cycle, and get the result on the nth cycle.
  2. @promotable(n) can go on groups, control, and go ports of components to provide a hint for static timing. This provides no timing guarantees. When placed on components, the interface is still dynamic: you assert the go signal until the done signal is high, at which point the result is ready. Because @promotable is just a hint, you can not, for example, static invoke a component even if it has a @promotable attribute.

@interval for double-duty

However, we have some components (like registers) which we would like to be able to serve double-duty (can be invoked in both static and dynamic contexts). We also (I think?) want Calyx components, not just primitives, to be able to serve double-duty as well. Therefore, we introduce a new attribute, @interval(n), which can be placed on the @go ports of registers, multipliers, and any other components we wish to be capable of double duty. The interface for @interval(n) is the following:

  1. Assert go for cycles [0,n). On the nth cycle, the done signal will be high and the output will be ready.

This has a few consequences:

  • For any Calyx component that has @interval(n), the control must be static<n> (it can't just be @promotable, since @promotable could be ignored), or it must contain continuous assignments (in which case they operate kind of like Verilog primitives).
  • For any Calyx component that has @interval(n), you can static invoke it. This allows us to static invoke registers, for example. 1.

loose ends

The way static promotion currently works is that, it upgrades dynamic components to static<n> components, and adds an attribute @promoted to it, which tells the component to provide a done signal, since the promoted component still may be used in dynamic contexts. Here is the interface that these components obey:

  1. You assert go on the 0th cycle, and then on the nth cycle, the done signal will be high and the output will be ready.

A few notes:

  • This interface is purely internal, in the sense that the compiler is the only one who can create this interface. Frontends cannot use this interface.
  • We like this interface, because it avoids a large fanout for the go signal (i.e., the go signal doesn't have to guard every assignment in the component anymore).
  • Right now, this is how we compile @interval(n) components "under the hood". In other words, we treat @interval components like a component we just promoted to static (i.e. we treat it like a static<n> that still must produce a done signal).
  • (4) is a subset of (3) (as well as (1) and (2)): if a component obeys (4), then it will also obey (3). This is why this strategy even works. All of the existing dynamic or static invocations of a component compiled using (4) will still work even if they are invoked thinking about rule (3).
  • It seems that, long term, we may(?) want to get rid of (3) entirely and only use (4) as our interface for double-duty. But right now, all of the primitives (std_mult, std_reg, etc.) use (3) as their interface. For now, we are only using (4) as an "under the hood" optimization to prevent large fanouts of go signals in certain components.

Footnotes

  1. When compiling these static invoke comp, we guard comp.go = %0 ? 1 if we have a static<n> comp and we guard comp.go =1 if we have an @interval comp.

@calebmkim calebmkim changed the title Deprecating @static and What to replace it with Deprecating @static and what to replace it with Feb 5, 2024
@rachitnigam
Copy link
Contributor

We also (I think?) want Calyx components, not just primitives, to be able to serve double-duty as well

One tricky part with this will be supporting early-resetting in such components. You'd basically end up needing to generate shift-register-based FSMs instead of the normal single-register FSMs we generate.

@calebmkim
Copy link
Contributor Author

calebmkim commented Feb 5, 2024

One tricky part with this will be supporting early-resetting in such components. You'd basically end up needing to generate shift-register-based FSMs instead of the normal single-register FSMs we generate.

Maybe this warrants a synchronous discussion, but could you elaborate on why this is: in particular, in what situations we need shift-register-based FSMs?

If it's pipelining that you're thinking about, I I thought we'd be fine since the FSMs only need to count up to the II of the component, not the latency. So during a given invocation of the component, we would have just a single FSM that is able to coordinate the execution of all the pipeline stages. But you may be thinking of something different.

Also, for components with an @interval of 1, we compile those differently than normal components, and I think it's correctly done (I may be wrong though).

Also, it's possible I'm just misinterpreting what you mean by "early-resettings" and my comments just don't make sense.

@rachitnigam
Copy link
Contributor

Well, one of the problems with early resetting is that we cant support both early resets and done signals right? If this is not true the. i'm wrong.

If this is true, then we need some other mechanism to generate both early resets and done signals which shift registers would do.

@calebmkim
Copy link
Contributor Author

Well, one of the problems with early resetting is that we cant support both early resets and done signals right?

We currently support this similar to how we compile the wrapper for static control, using a signal_reg to distinguish between different "FSM=0" states (i.e., use a signal_reg to distinguish whether FSM=0 bc we just reset, or FSM=0 bc we are not executing).

The exact logic for how we set the signal_reg is a little bit different than with a static control wrapper, since we need to be able to support immediate re-triggering of components (i.e., we want to be able to re-trigger the component during the cycle that the component's done signal is high), but I'm pretty sure we have a way that works.

@rachitnigam
Copy link
Contributor

@calebmkim is this considered resolved with #1897

@calebmkim
Copy link
Contributor Author

Closed with #1897

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants