Stream switch – openenoc_axis_switch

Connects multiple frame streams using unicast or multicast routing and arbitration.

Source: openenoc_axis_switch.sv.

Parameters

Parameters

Name

Default value

Allowed values

Description

S_COUNT

4

Integer >= 1; see scan-range limitation

Number of upstream input streams.

M_COUNT

4

Integer >= 1

Number of downstream output streams and routing-bitmap bits.

M_CONNECT

'{M_COUNT{ '{S_COUNT{1'b1}}}}

M_COUNT by S_COUNT array of 0/1

Entry [m][s] permits source s to reach output m.

S_REG_TYPE

2

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

M_REG_TYPE

0

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.

Public signals and interfaces

Name

Type

Direction / role

Description

clk

logic

input

Common clock for all streams, frame admission, and output ownership.

rst

logic

input

Active-high synchronous reset of arbitration, frame ownership, pending copies, and register-slice VALID state.

tuser_bitmap_route

logic

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.

s_axis[S_COUNT]

taxi_axis_if.snk

sink

Incoming frame bytes and routing metadata on each input.

m_axis[M_COUNT]

taxi_axis_if.src

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.

Frames offered together in tick 1

Frame

Input

Data beats

Input TDEST

Output / TDEST

FA

0

DA0 through DA8

0b100001

2 / 0b0001

FB

1

DB0 through DB8

0b000010

0 / 0b0010

FC

2

DC0 through DC8

0b110011

3 / 0b0011

FD

3

DD0 through DD8

0b010100

1 / 0b0100

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.

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.

Competing multicast frames

Frame

Input

Data beats

Input TUSER

Outputs

FA

0

DA0 through DA8

0b0011

0 and 1

FB

1

DB0 through DB8

0b0110

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.

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.

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.

Requested routes and surviving frame copies

Frame

Input

Data beats

Input TUSER

Effective route

Result

FA

0

DA0 through DA8

0b1010

0b1010

Outputs 1 and 3

FB

1

DB0 through DB8

0b0100

0b0100

Output 2

FC

2

DC0 through DC8

0b0000

0b0000

Discard

FD

3

DD0 through DD8

0b0001

0b0000

Discard

FE

3

DE0 through DE8

0b1001

0b1000

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.

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.

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