CIRCT 24.0.0git
Loading...
Searching...
No Matches
Functions
esiaccel.components.channel_arbiter Namespace Reference

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)
 

Function Documentation

◆ _build_grant_state()

Tuple[BitsSignal, BitsSignal, BitsSignal] esiaccel.components.channel_arbiter._build_grant_state (   ports,
ClockSignal  clk,
Signal  rst,
int  num_inputs,
BitsSignal  next_grant,
BitsSignal  next_busy 
)
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().

◆ _onehot_to_index()

BitsSignal esiaccel.components.channel_arbiter._onehot_to_index ( BitsSignal  onehot)
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().

◆ _select_latency()

int esiaccel.components.channel_arbiter._select_latency ( int  num_inputs,
Optional[int]  mux_pipeline_levels 
)
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().

◆ _select_mux()

BitsSignal esiaccel.components.channel_arbiter._select_mux ( BitsSignal  sel,
List[BitsSignal]  values,
ClockSignal  clk,
Signal  rst,
Optional[int]  mux_pipeline_levels 
)
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().

◆ _select_reg_levels()

List[int] esiaccel.components.channel_arbiter._select_reg_levels ( int  num_inputs,
Optional[int]  mux_pipeline_levels 
)
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().

◆ ChannelArbiter()

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.

◆ ChannelArbiterMod()

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().

◆ GrantSchedulerMod()

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().

◆ RoundRobinArbiterMod()

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().

◆ 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().