SimLink Troubleshooting
August 11, 2026 ยท View on GitHub
Start with the first failing boundary: source selection, native-library build, foreign-library load, socket bind, connection readiness, or protocol traffic. Avoid changing the wire format or public wrapper until the corresponding boundary has been isolated.
The getting-started guide provides the baseline commands. Backend-specific build details remain in the GHDL, VCS, and xsim guides.
Fast triage
From the same shell that starts the simulator, run:
echo "$RUCKUS_SIM_BACKEND"
pkg-config --modversion libzmq
gcc --version
make --version
Then verify the selected simulator tools:
command -v ghdl
command -v vcs vhdlan vlogan
command -v xsc xvlog xvhdl xelab xsim
Only one group needs to be present for the chosen backend. Confirm that the expected shared library exists after its build:
GHDL simlink/ghdl/build/libRogueSimLinkVhpiDirect.so
VCS simlink/vcs/libRogueSimLinkVhpi.so
xsim simlink/xsim/libRogueSimLinkDpi.so
After reset release, expect one line per live leaf/channel:
RogueTcpStream: Listening on ports N & N+1
RogueTcpMemory: Listening on ports N & N+1
RogueSideBand: Listening on ports N & N+1
No listening line means the problem precedes Rogue traffic. A listening line followed by idle traffic usually means port, direction, connection, reset, or application-graph setup.
libzmq is missing
Typical ruckus diagnostic:
libzmq package was not found
Please make sure that you have libzmq installed
or have sourced the necessary rogue setup scripts
The development package must be visible to pkg-config, not merely a runtime
library somewhere on the host:
pkg-config --exists 'libzmq >= 4.1.0'
pkg-config --cflags --libs libzmq
pkg-config --variable=libdir libzmq
Source the site's Rogue/conda environment or add the package's metadata
directory to PKG_CONFIG_PATH. Do not hard-code a library path into one
backend; all three builds use the same discovery contract.
Wrong backend or duplicate entities
Symptoms include duplicate RogueTcpStream, RogueTcpMemory, or
RogueSideBand definitions, a backend leaf compiled by the wrong simulator,
or a missing DPI/VHPI library during elaboration.
Fix the target at the manifest boundary:
export RUCKUS_SIM_BACKEND=ghdl # or vcs or xsim
Import top-level SURF and remove manually listed simlink/ghdl,
simlink/vcs, simlink/xsim, and old axi/simlink sources. The current
manifest loads one backend and removes stale sibling sources from persistent
Vivado projects.
GHDL cannot load VHPIDIRECT
If GHDL executes a VHDL stub body such as:
rogueTcpMemoryCreate: VHPIDIRECT stub body should never execute
the foreign symbol did not resolve. Rebuild the library and confirm its exact name:
make -C simlink/ghdl
ls -l simlink/ghdl/build/libRogueSimLinkVhpiDirect.so
The checked pytest helpers copy the library into the simulation build directory
and prepend the build directory to LD_LIBRARY_PATH. A downstream GHDL flow
must provide an equivalent loader path at elaboration/run time. On macOS the
Makefile records a basename-only install name so the runtime search path can
locate the staged library.
Use the focused wrapper test to distinguish environment loading from project source-list problems:
python -m pytest -q -n 0 \
tests/simlink/ghdl/test_RogueTcpMemoryWrap.py
VCS build, elaboration, or license failure
Confirm the sourced VCS environment supplies both variables and tools:
test -n "$VCS_HOME"
test -n "$VCS_VERSION"
command -v vcs vhdlan vlogan
test -f "$VCS_HOME/include/vhpi_user.h"
VCS_VERSION must be the integer expected by the adapter compatibility checks,
not an arbitrary marketing-version string. The checked X-2025.06 flow uses
VCS_VERSION=2025.
Messages such as Port ... direction mismatch or Port ... size mismatch
mean the VHDL leaf and declarative VHPI table do not agree. Remove stale
compiled libraries and ensure all VCS leaf and adapter sources come from the
same SURF revision.
The regression intentionally requires SIMLINK_RUN_VCS=1 before it checks out
a license. SIMLINK_VCS_LICENSE_FILE can override SNPSLMD_LICENSE_FILE for
test subprocesses when the site's default server ordering is unusable. These
are test controls, not runtime SimLink protocol settings.
Do not register vhpiCbEndOfSimulation to address a cocotb shutdown issue. The
current absence of that callback is deliberate; see the
VCS lifecycle notes.
xsim cannot build or load DPI
First run the standalone build and generated-prototype check:
make -C simlink/xsim all abi-check
Common failure classes are:
- missing Vivado simulator tools on
PATH; libzmqmissing frompkg-config;cannot find crti.o,-lzmq, or-lmfrom an older Vivado-bundled GCC;- a
GLIBCXX_* not founderror because the Vivado loader injected an olderlibstdc++.so.6; or - elaboration without
-sv_lib libRogueSimLinkDpi.
The checked Makefile adds the host multiarch directory for the older GCC link
case. The ruckus xsim hooks locate a libstdc++ compatible with the discovered
ZeroMQ and preload it when needed. Use the normal project xsim target so those
hooks run; a handwritten xelab command must reproduce both library binding
and runtime environment.
Invalid, changed, or overlapping port pair
Representative diagnostics are:
RogueTcpMemory: invalid SimLink base port 0
RogueTcpStream: SimLink base port changed from 9000 to 9002
RogueSideBand: SimLink port pair 9001/9002 overlaps live RogueTcpStream port pair 9000/9001
Fix the allocation rather than suppressing the check:
- base ports must be 1024 through 49151 at the public wrappers;
- every base reserves
NandN+1; - Stream
CHAN_COUNT_GandCHAN_MASK_Gderive additional pairs; and - PGP/HTSP wrappers reserve SideBand at documented offsets.
If the registry accepts the plan but ZeroMQ reports PULL bind failed or
PUSH bind failed, another process or host policy owns/blocks the port. On a
Unix host, inspect both members:
lsof -nP -iTCP:9000
lsof -nP -iTCP:9001
Terminate only the process you own, or choose a different complete pair. A
sandbox may reject all loopback binds with Operation not permitted; run the
simulation in an environment allowed to create local TCP listeners.
Listening appears, but Memory is not ready
Use the same base port in VHDL and software:
client = rogue.interfaces.memory.TcpClient("127.0.0.1", 9010, True)
if not client.waitReady(2.0, 0.05):
raise TimeoutError("Memory client is not ready")
Check that:
- the simulator clock continues after reset release;
- reset is not being reasserted continuously;
- the Rogue process uses loopback and the base port rather than
N+1; - no firewall/sandbox blocks local TCP; and
- both client sockets were created before
waitReady().
The readiness probe does not touch AXI-Lite. If it succeeds but a register
operation hangs or errors, inspect the AXI subordinate, address map, and
BRESP/RRESP behavior rather than the socket connection.
Stream is idle or reaches the wrong channel
Remember the wrapper direction names:
HDL sAxisMaster -> Rogue
Rogue -> HDL mAxisMaster
For a multi-channel wrapper, one Rogue TcpClient is required per derived base
port. HDL-to-Rogue sAxisMaster.tDest chooses the channel; Rogue-to-HDL frames
receive the route TDEST associated with the socket that delivered them.
frame.setChannel() does not replace selecting the correct client/port.
Also check:
TLASTeventually terminates every HDL-to-Rogue frame;TKEEPmarks the intended payload bytes;TVALIDis held with stable data whileTREADY=0;- the local Rogue source is connected into the
TcpClientand a sink is connected after it; and - the application accounts for asynchronous connection establishment because
Stream has no
waitReady()transaction.
An SSI metadata discrepancy usually indicates SSI_EN_G, first/last user, or
error mapping, not byte-order corruption. Compare the exact AXIS_CONFIG_G
with the DUT interface.
SideBand event or state is missing
Use pyrogue.interfaces.simulation.SideBandSim with the wrapper base port.
SideBand socket order is opposite the Stream/Memory base-port order, but the
class handles that detail internally.
Check that:
- HDL
txOpCodeEnis a one-clock pulse alongside a stabletxOpCode; - software calls
send(opCode=...)for an event andsend(remData=...)for a retained-state update; - the receive callback accepts
(op_code, remote_data)and handlesNonefor an unchanged field; and - callback work is short/thread-safe because it runs on the receive worker.
SideBand has no readiness probe. Wait for the listening diagnostic and an appropriate startup barrier before the first non-retryable event.
Transport timeout
Representative diagnostic:
RogueTcpStream: transport timeout on port 9001 during outbound send
The simulator attempted to send toward software, but no connected peer drained that direction within the configured interval. Before increasing the timeout, confirm the Rogue process is alive, connected to the correct base, and has a sink/callback consuming HDL-originated traffic.
The process-wide override must be a positive decimal number of milliseconds:
export SURF_SIMLINK_TRANSPORT_TIMEOUT_MS=60000
Zero, signs, suffixes, whitespace, non-decimal text, and overflow are rejected at model startup. A longer timeout accommodates known slow startup; it does not repair wrong directions or a peer that never drains.
Malformed or oversized input
Diagnostics such as Bad message sizes, inbound message exceeds ... parts,
inbound message exceeds ... bytes, or a Memory transaction shape error mean
the sender violated the established Rogue wire contract. Production Rogue
clients should not generate those messages. They usually identify:
- a hand-written pyzmq peer with wrong multipart boundaries;
- Stream flags/channel/error fields with incorrect sizes;
- a Memory request whose type, size, or data part is inconsistent; or
- a process connected to a pair assigned to a different model type.
Compare against the canonical wire tables in the protocol reference. Do not weaken validation to accept an incompatible peer.
Pacing assertion
The Stream pacer intentionally fails configuration errors at elaboration or simulation startup. Check these relationships:
PAYLOAD_RATE >= 0
AXIS_CLK_FREQ > 0 when PAYLOAD_RATE > 0
PAYLOAD_RATE <= 8 * TDATA_BYTES * AXIS_CLK_FREQ
Very small nonzero rates below the fixed-point resolution are also rejected.
Use zero for bypass. Runtime throughput is based on kept bytes transferred on
TVALID && TREADY, so sparse TKEEP or downstream backpressure lowers
payload progress by design.
Escalation information
When reporting a SimLink failure, include:
- SURF revision and selected backend;
- simulator and host operating-system versions;
pkg-config --modversion libzmq;- the complete port allocation, including derived Stream channels;
- wrapper generics and reset/clock behavior;
- the first SimLink diagnostic, not only the final simulator exit code; and
- whether the focused backend test passes in the same shell.
For protocol issues, also identify the Rogue version and whether the peer is a production Rogue client or the deterministic test oracle.