Stream switch – openenoc_axis_switch
Connects multiple frame streams using unicast or multicast routing and arbitration.
Source: openenoc_axis_switch.sv.
Parameters
Name |
Default value |
Allowed values |
Description |
|---|---|---|---|
|
|
Integer >= 1; see scan-range limitation |
Number of upstream input streams. |
|
|
Integer >= 1 |
Number of downstream output streams and routing-bitmap bits. |
|
|
M_COUNT by S_COUNT array of 0/1 |
Entry [m][s] permits source s to reach output m. |
|
|
0, 1, or 2 |
0 bypasses input registration; 1 uses a simple one-entry register; 2 uses a two-entry AXI-Stream register slice (skid buffer). |
|
|
0, 1, or 2 |
Selects the same register modes independently for every output; mode 1 inserts bubbles, while mode 2 can sustain one beat per cycle. |
Signals
Interface ports contain channels in both directions. The table identifies the selected modport or stream role; member directions follow that interface.
Name |
Type |
Direction / role |
Description |
|---|---|---|---|
|
|
input |
Common clock for all streams, frame admission, and output ownership. |
|
|
input |
Active-high synchronous reset of arbitration, frame ownership, pending copies, and register-slice VALID state. |
|
|
input |
Routing mode sampled when a frame is admitted: low M_COUNT USER bits when one, or the most significant DEST index bits when zero. |
|
|
sink |
Incoming frame bytes and routing metadata on each input. |
|
|
source |
Routed or replicated frames on each independently stalled output. |
Architecture and operation
The switch connects multiple AXI-Stream inputs to multiple outputs. It routes frame bytes using metadata already prepared by upstream processing, such as Forwarding engine – openenoc_axis_forwarding_engine. Lookup and source learning belong to those upstream blocks. Once admitted, frames with disjoint output sets can progress concurrently, with one beat per cycle on each ready path.
For tuser_bitmap_route = 0, the most significant
$clog2(M_COUNT) input TDEST bits select one output. A single-output
configuration selects output zero. For tuser_bitmap_route = 1, the low
M_COUNT TUSER bits form the requested output bitmap. Bit n selects
output n. In either mode, M_CONNECT[m][s] filters the requested route
for source s: an unset permission removes output m from its destination set.
A partially permitted multicast reaches the surviving outputs. An empty
effective route consumes the frame through TLAST without emitting a copy
or reserving an output.
Each input passes through its configured register slice before frame admission. A rotating scanner examines one input per cycle, starting at input zero after reset. It advances every cycle, including when that input is idle, already active, or waiting for an occupied output. It does not skip to another requesting input within the same cycle. A candidate frame is admitted when all outputs in its effective route are free; a frame selected for discard can be admitted without output ownership. At most one new frame is admitted per cycle, while all previously active routes can transfer data in parallel.
The route and routing mode are evaluated at admission, after any input register buffering. An active frame keeps its captured route until its final beat has completed on every selected path. Later metadata or mode changes do not redirect that active frame. A queued frame that has not yet been admitted uses the mode present when its scan succeeds.
Admission reserves all selected outputs together. A waiting multicast does not reserve an idle subset while another requested output is occupied. An active source keeps its output ownership during input gaps and output stalls, preventing another frame from interleaving on those outputs. Ownership is released after the final beat completes; a competing admission uses the ownership state from the beginning of its cycle and cannot reuse an output on that same completion edge. Its next opportunity also depends on the rotating scan position.
Each output independently accepts its copy of the current beat. VALID stays asserted with stable payload while that output is unready. Once one output accepts the beat, its VALID is suppressed for that beat while the remaining copies wait. The input beat advances after every selected output has either accepted its copy earlier or accepts it in the current cycle. A fast output therefore receives each beat exactly once, even when another copy stalls. Different multicast copies can transfer in different cycles. Input register storage can accept later beats until its available space fills.
S_REG_TYPE and M_REG_TYPE control independent register slices on
all inputs and outputs. Type 0 bypasses the slice. Type 1 provides one stored
beat and inserts bubbles between transfers. Type 2 provides two stored beats
and supports uninterrupted one-beat-per-cycle traffic. These slices buffer
beats rather than complete frames. The default input slices can accept a
prefix while a source waits for admission. Added output slices can accept
the final copies and release switch ownership before those copies reach
the external consumers; each output slice preserves frame order.
DATA, KEEP, LAST, and enabled STRB retain their frame-byte meaning. TID and TDEST are cast to their output widths, preserving the low output-width bits. TUSER on a surviving copy retains the original input metadata, including requested destinations removed by the connectivity filter. The switch does not rewrite TUSER to the filtered route or remove an ingress port from it.
Use consistent stream settings within each input/output array and matching
DATA widths between the arrays. Enabled KEEP requires matching KEEP widths.
Both DEST and USER must be enabled on both sides, even when only one is
used for routing. Input TDEST needs at least
max(1, $clog2(M_COUNT)) bits and input USER needs at least M_COUNT bits.
Use unicast indices in 0 .. M_COUNT - 1. TLAST must identify frame
boundaries. Reset clears the scan position,
ownership, active-frame state, pending copies, and buffered VALID state;
partial frames are abandoned.
The current scan counter wraps at its encoded width,
max(1, $clog2(S_COUNT)), without an explicit wrap or bounds skip at
S_COUNT. Four-input configurations use all encoded positions. Input counts
that leave unused encodings require scanner range handling before use.
Functional scenarios
All examples use four inputs and four outputs, 64-bit DATA, KEEP_W = 8,
enabled KEEP and LAST, S_REG_TYPE = 2, and M_REG_TYPE = 0.
Input TDEST is six bits wide; output TDEST is four bits wide. USER is
enabled and four bits wide on both sides. All streams share a 10 ns clock,
and reset has already been released. The scanner examines input 0 in tick 0,
followed by inputs 1, 2, and 3 in ticks 1, 2, and 3.
All frames are 66 bytes, including the Ethernet header and excluding
preamble, SFD, and FCS. Eight full beats use KEEP 0xFF; the final
two-byte beat uses KEEP 0x03 and TLAST. Frame FA uses DA0 through
DA8, FB uses DB0 through DB8, and subsequent frames follow
the same naming pattern. Every copy retains its frame’s data labels.
Each source continuously presents its queued frames while they are pending.
TUSER bitmaps use the 0b prefix and port order [3:0].
These metadata fields belong to the internal processing streams and do not
add Ethernet frame bytes.
A numbered tick marks a clock interval. A beat transfers at its ending
rising edge when VALID and READY are high. x marks irrelevant payload
values while VALID is low. Consecutive diagrams within a scenario show
adjacent clock intervals.
Independent unicast routes progress together
tuser_bitmap_route = 0 and all M_CONNECT entries are one. Every output
is ready. The high two input TDEST bits select the output; the low four
bits remain on output TDEST.
Frame |
Input |
Data beats |
Input TDEST |
Output / TDEST |
|---|---|---|---|---|
|
0 |
|
|
2 / |
|
1 |
|
|
0 / |
|
2 |
|
|
3 / |
|
3 |
|
|
1 / |
Each input first fills its available register storage while waiting for
admission. FC and FD start leaving outputs 3 and 1 in ticks 3 and 4;
FA and FB follow on outputs 2 and 0 in ticks 5 and 6. These start times
reflect the scanner and input buffering, rather than a shared output-data
bus. From tick 6 through tick 11, all four outputs transfer a beat every
cycle. The final beats complete in ticks 11 through 14 on their respective
outputs, each retaining KEEP 0x03 and TLAST.
Four disjoint unicast routes progress concurrently after separate admissions
Overlapping multicast routes wait for the complete output set
tuser_bitmap_route = 1 and all M_CONNECT entries are one. All outputs
remain ready; inputs 2 and 3 have no queued frames.
Frame |
Input |
Data beats |
Input TUSER |
Outputs |
|---|---|---|---|---|
|
0 |
|
|
0 and 1 |
|
1 |
|
|
1 and 2 |
FA acquires outputs 0 and 1 and sends both copies in ticks 5 through 13. FB’s prefix enters its input register slice, but its remaining input data waits because output 1 belongs to FA. Output 2 stays idle even though it is free and ready: FB cannot begin using only the available part of its requested route. Neither FA copy interleaves with FB.
A multicast waits for its whole route while FA owns the shared output
FA’s final copies transfer in tick 13, releasing both outputs at that interval’s ending edge. The input-1 scan in tick 13 still sees the existing ownership. Its next visit in tick 17 can admit FB, and FB’s two copies start in tick 18. They transfer consecutively through tick 26 on outputs 1 and 2. Output 0 remains free after FA completes, and output 3 is unused.
The next input scan admits FB after the shared output is released
Independent multicast copies and connectivity filtering
tuser_bitmap_route = 1. Only M_CONNECT[0][3] is zero; all other
paths are permitted. Input 3 queues FD followed immediately by FE.
Output 0 remains unready throughout the example.
Frame |
Input |
Data beats |
Input TUSER |
Effective route |
Result |
|---|---|---|---|---|---|
|
0 |
|
|
|
Outputs 1 and 3 |
|
1 |
|
|
|
Output 2 |
|
2 |
|
|
|
Discard |
|
3 |
|
|
|
Discard |
|
3 |
|
|
|
Output 3 |
In tick 5, output 1 accepts DA0 while output 3 is unready. Output 3
holds that same beat with VALID high through tick 8 and accepts it in
tick 9. Output 1 remains invalid in ticks 6 through 9, so it does not
accept DA0 again. Both copies resume with DA1 in tick 10 and
finish together with DA8 in tick 17. Meanwhile FB transfers on output
2 in ticks 6 through 14.
FC’s empty requested bitmap and FD’s fully filtered route are both drained without an output copy. FE’s prefix can enter input 3’s register slice, but its surviving destination, output 3, remains owned by FA until FA finishes. Output 0’s low READY does not block any of these permitted paths.
Each multicast output accepts its own copy once while other paths advance
After FA releases output 3, the next eligible input-3 scan admits FE.
Its beats leave output 3 in ticks 20 through 28, with no copy on output 0.
Every FE output beat still carries TUSER 0b1001, while its effective
route selects only output 3. Its final DE8 transfers once in tick 28
with KEEP 0x03 and TLAST.
Connectivity removes a destination while preserving the original TUSER
Verification
The test suite is in dv/core/openenoc_axis_switch/.
Testbench
This test suite validates the openenoc_axis_switch module in isolation. The module routes
AXI Stream traffic from multiple inputs to multiple outputs.
Routing Modes
The switch verification currently targets two routing modes:
1. Unicast (TDEST-based Routing)
Routes each frame to one output port based on TDEST MSBs
Useful for classical single-destination forwarding
Activated when
PARAM_TUSER_BITMAP_ROUTE := 0
2. Multicast (TUSER Bitmap Routing)
Routes each frame based on TUSER bitmap bits
Supports one-to-many delivery from one input to multiple outputs
Includes empty-bitmap drop behavior
Includes scheduler/contention scenarios between multiple inputs
Activated when
PARAM_TUSER_BITMAP_ROUTE := 1
Coverage
Unicast (TDEST) tests:
Single-destination forwarding checks
Multiple frame sizes
Idle insertion and backpressure scenarios
Multicast (TUSER bitmap) tests:
Multiple bitmap combinations (including all-target and subset delivery)
Empty bitmap drop behavior
Scheduler/contention scenario with overlapping output targets
Idle insertion and backpressure scenarios