Vivado xsim SimLink Backend
August 11, 2026 ยท View on GitHub
This backend implements the common SimLink leaves with SystemVerilog DPI-C for Vivado xsim. See the architecture reference and shared C internals. For common dependencies, backend selection, and the first transaction, start with the getting-started guide.
Call chain and ownership
VHDL entity Rogue*
-> instantiates SystemVerilog Rogue*Dpi leaf
-> SV always @(posedge clock)
-> DPI-C rogue*Update(chandle, ports...)
-> shared Rogue*Step
Each SV leaf lazily creates and retains one chandle. Its final block calls
the matching destroy function. RogueDpiInstance.c is a compatibility bridge
from that stable DPI ABI to the common RogueSimLinkInstance API, which
validates model ownership, rejects complete-pair overlap across model types,
and registers atexit cleanup.
DPI formal arguments are two-state bit values; the SV leaf presents
four-state logic ports to mixed-language elaboration and performs the
automatic narrowing at the call. RogueTcpStreamDpi parameterizes its packed
data, user, and keep vectors from 1 through 128 data bytes. DPI exposes those
vectors as little-endian svBitVecVal arrays consumed by the shared Stream
codec.
Build, ABI check, and elaboration
The Makefile uses xsc to build one combined libRogueSimLinkDpi.so from the common
instance manager and all three adapters. xelab -dpiheader generates C
prototypes from the SV imports; abi-check recompiles the adapters with that
header preincluded to catch signature drift.
The backend requires the Vivado simulator tools xsc, xvlog, xvhdl,
xelab, and xsim on PATH, plus the common libzmq development package.
make -C simlink/xsim all abi-check
Elaboration binds the combined library once:
xelab ... -sv_lib libRogueSimLinkDpi
Running an xsim co-simulation
An external target must provide a simulation top containing one or more
RogueTcpStream, RogueTcpMemory, RogueSideBand, or corresponding SURF
wrapper instances. It must drive clock and reset, assign a distinct two-port
pair to every live instance, and start a peer implementing the matching wire
protocol. An instance using portNum=N owns both N and N+1; the next
non-overlapping base port is therefore at least N+2.
From a target using the standard ruckus simulation flow, run make gui or the
target's xsim make target. simlink/ruckus.tcl selects xsim/, the build
creates the combined libRogueSimLinkDpi.so, and elaboration binds it once with
-sv_lib libRogueSimLinkDpi. After reset is released, each instance prints its
Listening on ports N & N+1 message.
Connect the peer and keep it draining before HDL produces outbound messages. Receive polling uses the worker FIFO and outbound traffic uses a bounded worker rendezvous for all three models.
Checked-in xsim examples
The repository includes two focused multi-instance regressions:
simlink/test/xsim/RogueXsimMultiInstanceTb.vhdinstantiates four Stream, two Memory, and two SideBand models concurrently.simlink/test/xsim/RogueXsimDuplicatePortTb.vhdverifies process-wide rejection when two leaves claim the same endpoint pair.tests/simlink/xsim/test_RogueXsimMulti.pybuilds the DPI library, checks the generated DPI-C prototypes, compiles the mixed-language design, runs xsim, and verifies duplicate-port rejection.simlink/test/xsim/RogueXsimTrafficTb.vhddrives the same eight-instance topology with isolated Stream, Memory, and SideBand traffic.tests/simlink/xsim/test_RogueXsimTraffic.pystarts one deterministicrogue_tcp_peer.pyprocess per instance and verifies the xsim and peer-side results. It consumes the same backend-neutral peer layout and tagged-result validator as the GHDL topology. After traffic completes, the VHDL top pulses reset and runs on before reporting success, matching the GHDL reset-survival scenario.
The traffic test uses test-only ready files to tell the parent process that
each peer configured its sockets and called ZeroMQ connect(). It retains a
short fixed settle delay because connection establishment is asynchronous.
This coordination adds no production socket, message, or handshake to the
Rogue TCP wire protocol.
Run checked-in xsim regressions in a Vivado-enabled shell:
./.venv/bin/python -m pytest -q -n 0 \
tests/simlink/xsim
They skip with an explicit reason when xsc, xvlog, xvhdl, xelab, or
xsim is unavailable.
Troubleshooting
libzmq package was not found
If the Tcl console prints:
libzmq package was not found
Please make sure that you have libzmq installed
or have sourced the necessary rogue setup scripts
install its development package or source the Rogue setup scripts that put it
on PKG_CONFIG_PATH, then rerun make gui. Confirm discovery with:
pkg-config --modversion libzmq
Listening message but idle waveform
If the console printed Listening on ports N & N+1 but the waveform never
shows activity, confirm that the peer process is running, targets loopback
127.0.0.1 and the same port pair, and uses the socket directions documented
in the
protocol reference.
Remember that ZeroMQ connect() completes asynchronously; a test-only ready
file proves
the call was issued, not that the connection handshake has finished.
Limitations
- All sockets are worker-owned; DPI calls do not call ZeroMQ.
finalis the preferred instance cleanup hook, but process-exit cleanup is still retained as a fallback.- Port ownership and model lifecycle use the same common API as GHDL and VCS.
- Sockets use zero linger and outbound rendezvous have a finite timeout.
- xsim
restart(time-zero rewind of a loaded snapshot) andrelaunch_sim(recompile/relaunch) are different lifecycle operations. The latter should preserve an external peer by socket reconnection; the former still needs an executable DPI-lifecycle regression before it is a supported SimLink claim. In particular, the test must establish whether restart restores the SVchandleto null while retaining its C allocation, which would leave the old instance live and make the replacement fail the port-overlap check.