|
CIRCT 24.0.0git
|
Functions | |
| List[int] | _select_reg_levels (int num_inputs, Optional[int] mux_pipeline_levels) |
| int | _select_latency (int num_inputs, Optional[int] mux_pipeline_levels) |
| BitsSignal | _select_mux (BitsSignal sel, List[BitsSignal] values, ClockSignal clk, Signal rst, Optional[int] mux_pipeline_levels) |
| BitsSignal | _onehot_to_index (BitsSignal onehot) |
| Tuple[BitsSignal, BitsSignal, BitsSignal] | _build_grant_state (ports, ClockSignal clk, Signal rst, int num_inputs, BitsSignal next_grant, BitsSignal next_busy) |
| GrantSchedulerMod (int num_inputs, int queue_depth) | |
| RoundRobinArbiterMod (int num_inputs) | |
| RoundRobinControlMod (int num_inputs) | |
| ChannelArbiterMod (Channel channel_type, int num_inputs, int output_fifo_depth, bool buffer_inputs, bool telemetry, Optional[int] mux_pipeline_levels, bool pipelined_scheduler, int grant_queue_depth) | |
| ChannelSignal | ChannelArbiter (List[ChannelSignal] input_channels, ClockSignal clk, Signal rst, *Optional[AppID] appid=None, Optional[int] output_fifo_depth=None, bool buffer_inputs=True, Optional[int] mux_pipeline_levels=None, bool pipelined_scheduler=False, int grant_queue_depth=4, bool telemetry=True) |
|
protected |
Register `next_grant`/`next_busy` into the grant FSM state every control module has, drive the `grant`/`grant_oh`/`busy` ports with it, and return `(grant, grant_oh, busy)` for the strategy to compute its next state from (typically via `Wire`s, since next state depends on current). `grant_oh` is decoded *ahead* of its registers -- one flop per input -- so each high-fanout per-input grant is driven straight from a flop rather than a shared combinational decode of `grant`. Decoding at the instantiation site would necessarily land after the register, hence it lives here. Both are fed from the same next-state, so `grant_oh[i]` is high exactly when `grant == i`.
Definition at line 110 of file channel_arbiter.py.
Referenced by esiaccel.components.channel_arbiter.GrantSchedulerMod(), and esiaccel.components.channel_arbiter.RoundRobinControlMod().
|
protected |
Encode a one-hot bit-vector to its binary index. Bit `b` of the result is the OR of the one-hot bits whose index has bit `b` set.
Definition at line 84 of file channel_arbiter.py.
Referenced by esiaccel.components.channel_arbiter.GrantSchedulerMod().
|
protected |
Pipeline-register latency (cycles) that `_select_mux` inserts.
Definition at line 44 of file channel_arbiter.py.
References esiaccel.components.channel_arbiter._select_reg_levels().
Referenced by esiaccel.components.channel_arbiter.ChannelArbiterMod().
|
protected |
Return `values[sel]`. With `mux_pipeline_levels` falsy this is a flat combinational mux (a single `hw.array_get`, which CIRCT lowers to an unpipelined mux tree). Otherwise it is built as an explicit balanced binary mux tree -- 2:1 nodes consuming one `sel` bit per level -- with a pipeline register inserted after every `mux_pipeline_levels` levels. This lets a large/wide selection mux (the Fmax bottleneck of a big fan-in mux) be retimed across registers. The remaining `sel` bits are pipelined alongside the partial results so each level selects with the correctly-delayed index. The added latency is `_select_latency(len(values), mux_pipeline_levels)` cycles.
Definition at line 49 of file channel_arbiter.py.
References esiaccel.components.channel_arbiter._select_reg_levels().
Referenced by esiaccel.components.channel_arbiter.ChannelArbiterMod().
|
protected |
Tree levels after which `_select_mux` inserts a pipeline register. A register is placed after every `mux_pipeline_levels` levels, except after the final (root) level -- its result is registered downstream. This is the single source of truth for the mux-tree pipelining: `_select_mux` builds the registers at these levels and `_select_latency` just counts them.
Definition at line 27 of file channel_arbiter.py.
Referenced by esiaccel.components.channel_arbiter._select_latency(), and esiaccel.components.channel_arbiter._select_mux().
| ChannelSignal esiaccel.components.channel_arbiter.ChannelArbiter | ( | List[ChannelSignal] | input_channels, |
| ClockSignal | clk, | ||
| Signal | rst, | ||
| *Optional[AppID] | appid = None, |
||
| Optional[int] | output_fifo_depth = None, |
||
| bool | buffer_inputs = True, |
||
| Optional[int] | mux_pipeline_levels = None, |
||
| bool | pipelined_scheduler = False, |
||
| int | grant_queue_depth = 4, |
||
| bool | telemetry = True |
||
| ) |
Build a pipelined, list-aware N:1 channel multiplexer.
Unlike the combinational `pycde.esi.ChannelMux`, this is a flat registered
round-robin arbiter with a feed-forward output stage (output register + FIFO
+ credit counter), so it closes timing at high fan-in. It also keeps
multi-flit list messages contiguous: once an input is granted, it holds the
output until a flit whose 'last' field is set has been transferred. List
framing is auto-detected from the channel type (window payloads with a 'last'
field); all other payloads are treated as single-flit messages.
Arguments:
input_channels: the channels to multiplex. All must share the same
(ValidReady) type.
clk, rst: clock and reset.
appid: optional `AppID` for the arbiter instance (e.g. to address it or to
disambiguate its telemetry in the appid hierarchy).
output_fifo_depth: depth of the output FIFO; must be greater than the
pipeline latency (one output register plus any selection-mux pipeline
latency). Defaults to that plus a small internal slack.
buffer_inputs: insert a per-input skid buffer to localize backpressure.
mux_pipeline_levels: if set, build the N:1 data-selection mux as an explicit
binary tree and insert a pipeline register after every this-many tree
levels (1 = register every level). This retimes the wide selection mux
for very large fan-in; the added latency is absorbed by the output FIFO /
credit counter. `None` (default) uses a flat combinational mux.
pipelined_scheduler: decouple grant selection from the datapath using a
grant queue fed by a sweep scheduler, instead of re-arbitrating
combinationally at each message end. This takes the round-robin tree out
of the single-cycle `grant -> grant` loop, which is the Fmax limiter at
high fan-in. Changes the service order (see `GrantSchedulerMod`).
grant_queue_depth: depth of that grant queue -- how many grant decisions
may be committed ahead of the datapath. Must be >= 2: a single entry
cannot keep the datapath fed back to back, so every message would cost a
refill bubble. This is not a fairness knob; a newly-valid input's wait
also scales with the number of concurrently active inputs (see
`GrantSchedulerMod`).
telemetry: emit telemetry (selected channel, list-length stats, etc.).
See `docs/components/ChannelArbiter.md`.
Definition at line 617 of file channel_arbiter.py.
| esiaccel.components.channel_arbiter.ChannelArbiterMod | ( | Channel | channel_type, |
| int | num_inputs, | ||
| int | output_fifo_depth, | ||
| bool | buffer_inputs, | ||
| bool | telemetry, | ||
| Optional[int] | mux_pipeline_levels, | ||
| bool | pipelined_scheduler, | ||
| int | grant_queue_depth | ||
| ) |
Build a pipelined, list-aware N:1 channel multiplexer module. See the `ChannelArbiter` convenience function for the user-facing entry point and `docs/components/ChannelArbiter.md` for the design.
Definition at line 385 of file channel_arbiter.py.
References esiaccel.components.channel_arbiter._select_latency(), esiaccel.components.channel_arbiter._select_mux(), esiaccel.components.channel_arbiter.GrantSchedulerMod(), and esiaccel.components.channel_arbiter.RoundRobinControlMod().
| esiaccel.components.channel_arbiter.GrantSchedulerMod | ( | int | num_inputs, |
| int | queue_depth | ||
| ) |
Decoupled, pipelinable grant scheduler (`pipelined_scheduler=True`). A **grant queue** holds upcoming winners for the datapath to pop, and a **sweep scheduler** refills it off the critical path. That breaks the flat arbiter's single-cycle `grant -> grant` loop, its dominant timing limiter at high fan-in. A queued entry is a hint about who to serve next, not a promise that a particular message is waiting: an entry whose input has since gone idle is skipped in one cycle (`stale` below) rather than stalling the output. Consequently service order is best-effort, and `queue_depth` bounds only how far ahead of the datapath decisions are committed -- it is not a fairness knob. See section 7.1 of `docs/components/ChannelArbiter.md` for why committing early is safe and for the full ordering/latency caveats.
Definition at line 142 of file channel_arbiter.py.
References esiaccel.components.channel_arbiter._build_grant_state(), and esiaccel.components.channel_arbiter._onehot_to_index().
Referenced by esiaccel.components.channel_arbiter.ChannelArbiterMod().
| esiaccel.components.channel_arbiter.RoundRobinArbiterMod | ( | int | num_inputs | ) |
Combinational round-robin winner selection, factored into its own module for waveform visibility. Given a per-input `valids` bitmask (bit `i` is input `i`) and a `start` index, `winner` is the lowest-index input that is valid and at index `>= start` (cyclically), falling back to the lowest-index valid input overall; `any_valid` is high when any input is valid. Purely combinational -- the owning state (`rr_ptr`, `grant`/`busy`) lives in `RoundRobinControlMod`.
Definition at line 249 of file channel_arbiter.py.
Referenced by esiaccel.components.channel_arbiter.RoundRobinControlMod().
| esiaccel.components.channel_arbiter.RoundRobinControlMod | ( | int | num_inputs | ) |
Flat round-robin grant control (the default strategy). Answers "who is granted next?" combinationally in the cycle the current message ends, using two `RoundRobinArbiter` instances -- one for picking up from idle, one for the message-end turnaround -- plus the `rr_ptr` fairness pointer, which is private to this strategy. See section 7 of `docs/components/ChannelArbiter.md`. `launch` is unused; it exists only to match `GrantSchedulerMod`'s signature.
Definition at line 311 of file channel_arbiter.py.
References esiaccel.components.channel_arbiter._build_grant_state(), and esiaccel.components.channel_arbiter.RoundRobinArbiterMod().
Referenced by esiaccel.components.channel_arbiter.ChannelArbiterMod().