FPGA network topology generator built on NetworkX. Defines topologies once, emits shell commands for either hardware (--fpgalink=... strings) or emulation (mkdir/mkfifo/ln -s strings). Both backends are pure: they print to stdout and never touch the filesystem. Pipe to bash to apply.
The graph's nodes are (rank, link) tuples, the emulation filesystem layout is rank{R}/link_i{L}_{tx,rx}, and the hardware backend emits --fpgalink=... strings. Every link is bidirectional at the filesystem level (always has a _tx FIFO + an _rx symlink wired by the graph edges).
With uv:
uv venv --python 3.14
source .venv/bin/activate
uv pip install git+https://github.com/papeg/topomux
# or editable from a local checkout:
uv pip install -e /path/to/topomuxOr the repo's own make using traditional venv:
source env.sh
make venv
source .venv/bin/activateFour topologies ship:
| Name | Edge |
|---|---|
ring |
(r, 1) <-> ((r+1) % n, 0) |
reverse-ring |
(r, 0) <-> ((r+1) % n, 1) |
loopback |
(r, L) <-> (r, L) for each rank and link |
pair |
(r, 0) <-> (r, 1) within each rank |
# Hardware: emits --fpgalink=... flags on stdout, space-separated.
# Prepend the toolchain's link tool:
changeFPGAlinks $(topomux ring 2 -b hardware --fpgas-per-node 2)
changeFPGAlinksXilinx $(topomux ring 3 -b hardware --fpgas-per-node 3)
# From topology file
topomux -f topologies/xilinx_ring.topo -b hardware --fpgas-per-node 3
# Emulation: prints mkdir/mkfifo/ln-s commands. Pipe to bash to apply.
topomux ring 4 -b emulation --base-dir /tmp/pipes
topomux ring 4 -b emulation --base-dir /tmp/pipes | bash
# Emulation with a BSP overlay (adds symlinks according to BSP's id layout)
topomux reverse-ring 4 -b emulation --bsp <name> --base-dir /tmp/pipes | bash
# FPGA-Link GUI URL
topomux ring 3 -b hardware --gui-urlimport topomux
# Standard topologies - pick direction by function name
g = topomux.ring(4)
g = topomux.reverse_ring(4)
g = topomux.loopback(3, links_per_rank=2)
g = topomux.pair(3)
# From file
g = topomux.from_file("topologies/some.topo")
# Arbitrary edges
g = topomux.from_edge_list([
((0, 0), (1, 1)),
((1, 0), (2, 1)),
((2, 0), (0, 1)),
])
# Hardware output (returns list of --fpgalink=... strings)
hw = topomux.HardwareBackend(topomux.NodeMapping(fpgas_per_node=3))
print(" ".join(hw.emit(g)))
print(hw.emit_gui_url(g))
# Emulation output (returns list of mkdir/mkfifo/ln-s commands)
emu = topomux.EmulationBackend(base_dir="/tmp/pipes")
for action in emu.emit(g):
print(action)
# Emulation output (with BSP overlay)
emu = topomux.EmulationBackend(
base_dir="/tmp/pipes",
bsp=topomux.BittwareS10BSP(),
)
for action in emu.emit(g):
print(action).topo files support edge lists and shorthand commands. Comments with #.
# Shorthand
ring 4
reverse-ring 4
loopback 3 4 # 3 ranks, 4 links per rank
pair 3
# Explicit edges: rank.link - rank.link
0.0 - 1.1
2.0 - 0.1
For every (rank, link) node the graph uses, the emulation backend creates:
{base_dir}/rank{R}/link_i{L}_tx FIFO
{base_dir}/rank{R}/link_i{L}_rx symlink wired by the graph edges
Intel BSPs IO-pipes are expecting a file in the process's cwd named after the id matching the number in the board_spec.xml.
For these runtimes, topomux emits an additional fd-number symlink layer via --bsp. For every (rank, link) node in the graph:
{base_dir}/rank{R}/{bsp.output_id(L)} -> link_i{L}_tx
{base_dir}/rank{R}/{bsp.input_id (L)} -> link_i{L}_rx
A BSP describes pure board layout: input_id(link) and output_id(link) return the ids the hardware platform assigns to each link direction.
bittware_s10 (Bittware 520N, Stratix 10 p520_max_sg280l). The BSP lists 4 bidirectional channels in board_spec.xml with the id assignments that fall out of the chan_id positions:
class BittwareS10BSP(BSP):
name = "bittware_s10"
def input_id(self, link): return 2 * link # kernel_input_chL = id 2L
def output_id(self, link): return 2 * link + 1 # kernel_output_chL = id 2L+1
def num_links(self): return 4Then topomux <topology> N --bsp bittware_s10 works end-to-end.