SimLink Alignment and Hardening
August 14, 2026 ยท View on GitHub
Goal and status
Align the GHDL/VHPIDIRECT, VCS/VHPI, and Vivado xsim/DPI SimLink backends
around shared transport, lifecycle, and protocol-core code while preserving
the public Rogue*Wrap interfaces and established Rogue wire protocols.
Status: implementation and review are complete on simlink-alignment. SimLink
is now a top-level SURF subsystem under simlink/, with executable tests under
tests/simlink/. User and maintainer documentation starts at
simlink/README.md; test architecture and commands
are in tests/simlink/README.md.
Delivered architecture
simlink/shared/owns the common ZeroMQ transport, complete adjacent-port reservation, instance lifecycle, multipart codecs, and deterministic Stream, Memory, and SideBand model behavior.simlink/ghdl/,simlink/vcs/, andsimlink/xsim/contain thin simulator-specific adapters using VHPIDIRECT, VHPI, and DPI-C respectively.- Each backend builds one descriptively named combined shared library and uses one process-wide instance registry across all three model types.
simlink/sim/contains the stable record-based SURF wrappers and the deterministic aggregate Stream payload pacer.simlink/test/contains test-only HDL/SystemVerilog harnesses; Python orchestration and protocol peers live undertests/simlink/.- GHDL and VCS reuse the same active eight-instance VHDL topology and cocotb traffic scenario. xsim uses the same peer allocation and result contract through a self-driving mixed-language top.
- Stream supports payload widths from 1 through 128 bytes per beat. The public wrappers retain existing interface names and wire semantics.
Compatibility and design decisions
- The public compatibility surface is
RogueTcpStreamWrap,RogueTcpMemoryWrap, andRogueSideBandWrap; backend leaves are integration details. - Existing multipart order, field sizes, host byte order, socket directions, transaction identifiers, and SSI/AXI sideband behavior remain unchanged.
- Memory ordinary responses remain four-byte numeric AXI responses. Only the
TcpBridgeProbereadiness response uses ASCIIOK, as required byTcpClient.waitReady(). - One worker exclusively owns each model's ZeroMQ context and sockets. Complete inbound messages cross a bounded 16-entry FIFO; the worker stops draining rather than dropping or overwriting an entry.
- Each instance reserves its complete adjacent TCP port pair before either bind and releases it only after both sockets close.
- Transport waits and shutdown are bounded. The positive-decimal
SURF_SIMLINK_TRANSPORT_TIMEOUT_MSoverride controls model deadlines. - Stream pacing is based on simulated payload-byte credit and completed AXI Stream handshakes. It is independent of host scheduling and transport queue depth and intentionally does not model encoding or frame overhead.
- VCS teardown remains passive at
$finish: registering a VHPI end-of- simulation callback conflicts with cocotb's VPI shutdown in VCS. Process exit andatexit(rogueSimLinkDestroyAll)own cleanup; direct cleanup remains available to the native lifecycle harness.
Review closeout
The final review and hardening passes resolved the following material defects:
- Bounded
RogueTcpStreamWrapchannel mapping for non-power-of-two channel counts and rejectedCHAN_COUNT_G = 0at elaboration. - Corrected VCS VHPI port-direction validation where standard
vhpiInis zero. - Removed the VCS mixed-VHPI/VPI end-of-simulation callback that caused teardown segfaults, and updated the native lifecycle harness accordingly.
- Added NULL guards and removed signed-shift undefined behavior in the VCS adapter.
- Centralized fixed test-port allocation and added overlap checks for xdist.
- Replaced timing-sensitive peer startup assumptions with explicit readiness and bounded wall-clock completion.
- Closed the ZeroMQ PUSH slow-joiner window by waiting for
ZMQ_EVENT_HANDSHAKE_SUCCEEDEDbefore first send and using bounded linger so queued fire-and-forget frames are flushed during peer teardown. - Made shared-library staging atomic so parallel tests never rewrite a mapped library in place.
- Kept simulator relaunch peers alive for the full compile/elaboration/run budget and kept clocking until every expected inbound observation arrives.
- Consolidated socket helpers, malformed-input coverage, lifecycle checks, and native transport bounds/overload tests.
No unresolved product-code defect remains in the completed alignment scope.
Verification
Verification on the final synchronized PR head included:
- GitHub CI: lint, regression, documentation, and the required production Rogue Memory contract all passed.
- Local open-source suite on macOS: 94 passed and 13 expected skips. The skips were five Linux-only abort/Valgrind cases, three separately provisioned Rogue contracts, two licensed VCS cases, and three Vivado xsim cases.
- Flake8 passed for the changed Python test code.
- VSG reported zero violations across all 22 SimLink VHDL files.
make MODULES="$PWD" importcompleted and loaded the top-levelsimlinkruckus manifest.- All SimLink runner scripts passed
bash -n. - Licensed VCS X-2025.06 evidence covers both the active multi-instance test and
the two-
simvpersistent-peer relaunch test. - Licensed Vivado 2025.2 evidence covers mixed-language elaboration, multi-instance isolation, duplicate-port rejection, and repeated active traffic after the slow-joiner fix.
Generated simulator output remains under tests/sim_build/simlink/ and is not
part of this handoff.
Remaining risks and follow-ups
These are deliberate follow-ups, not merge blockers:
- Hosted CI does not provide licensed VCS or Vivado tools. Add a self-hosted
licensed job for
run-vcs.shandrun-xsim.shwhen suitable infrastructure is available. - The required real-Rogue CI job currently exercises Memory. Promote the checked-in Stream and SideBand Rogue contracts into that required job.
- VCS proves shared active traffic and relaunch but does not yet mirror every GHDL wrapper boundary/error case. Extend the existing VPI bridge pattern for deeper wrapper coverage.
- Exact in-place VCS GUI/UCLI re-exec and xsim
restart/relaunch_simlifecycle commands remain distinct from the covered process-relaunch contract and need tool-specific tests. - Stream and SideBand have no production readiness transaction equivalent to
Memory's
TcpBridgeProbe; callers must account for asynchronous connection establishment. - Stream currently allocates fixed 20,000,000-byte inbound and outbound buffers per instance. A future allocation redesign could reduce idle memory without changing the wire protocol.
- The identical non-power-of-two channel-map defect on the legacy
masterbranch is outside this PR and remains a maintainer decision.