Five concentric layers, slowest to fastest:
| Level | Tool | Catches | Time per run | Hardware needed |
|---|---|---|---|---|
| L1 Unit | g++ + CTest (plain assert()) |
logic bugs, state-machine mistakes | seconds | none |
| L2 SW emu | g++ + std::thread | topology bugs, deadlock, backpressure | seconds | none |
| L3 HLS cosim | Vitis HLS | pragma issues, II surprises, latency drift | minutes/kernel | none |
| L4 Verilator | Verilator | RTL integration, cross-kernel CDC under load | tens of minutes | none |
| L5 Real FPGA | XRT + Alveo U50 | timing closure, real Eth, real perf | minutes (after 4–8 h build) | U50 |
scripts/sim/run_unit.sh builds and runs the per-component tests:
- compiler frontend:
tests/unit/test_lexer.cpp,test_parser.cpp,test_resolver.cpp,test_analyses.cpp,test_compiler_smoke.cpp. - runtime:
runtime/tests/test_runtime_basic.cpp,test_pcap_roundtrip.cpp,test_slot_bridge.cpp.
These are pure software, deterministic, and run in CI on every push.
scripts/sim/run_emu.sh examples/<name>:
- Compiles
<name>/topology.clnpwith the SW-emu backend → producesgenerated/sw_emu/topology.cpp. - Compiles that with g++ + the runtime to a single binary
<name>_swemu. - Runs the binary; if a PCAP path is given, replays it through
tor_in/nic_in.
Each kernel runs as its own std::thread; channels are
openclicknp::SwStream SPSC FIFOs that share the same read_nb /
write_nb API as Vitis HLS streams. So the same element body
compiles unchanged in both contexts.
scripts/sim/run_cosim.sh examples/<name> [kernel]:
For each kernel:
vitis_hls -f <kernel>_cosim.tclrunscsynth_designthencosim_design -tool xsim.- The same testbench used for L1 unit tests drives the synthesized RTL.
- Pass/fail comes from cosim's exit code.
This catches the gap between "compiles with HLS" and "produces correct RTL" — pragma errors, II≠1 surprises, off-by-one latency mismatches.
scripts/sim/run_verilator.sh examples/<name>:
- Reuses the per-kernel Verilog produced by L3's
csynth_design. - Builds the auto-generated
topology.vwrapper that wires those kernels together with AXIS FIFOs. - Compiles + runs Verilator against
tb.cpp, dumpingtopology.fst.
This is the highest-fidelity test that runs without an FPGA — it catches integration-level deadlocks that show up only under realistic backpressure plus the actual synthesized RTL.
scripts/build/synth_kernels.sh examples/<name> # ~min/kernel
scripts/build/link.sh examples/<name> # 5–10 min, runs CDC #1
scripts/build/implement.sh examples/<name> # 4–8 h, runs CDC #2
scripts/run/program_fpga.sh build/<name>/<name>.xclbin
scripts/run/run_example.sh examples/<name>
scripts/run/perf_pps.sh # live counters
Cable check: scripts/run/loopback_test.sh for QSFP28-to-QSFP28
loopback validation.
scripts/sim/run_systemc.sh builds and runs the SystemC backend. It's
slower than SW-emu but cycle-accurate (each handler iteration takes one
simulated cycle), useful for FIFO-depth tuning and latency budgeting
without HLS synthesis.
Recommended GitHub Actions matrix:
- every push: L1 + L2 (no Xilinx tooling required).
- every PR: L4 (Verilator only, no Xilinx tooling required).
- nightly: L3 (Vitis HLS license required).
- release tags: full L5 with hardware-in-the-loop tests.