Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ ruckus
ruckus/
build/
sim_build*
xsc.pb
.coverage*

# Local editor/agent state
Expand Down
2 changes: 1 addition & 1 deletion axi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ This tree contains reusable AXI-family RTL and wrappers. Top-level `axi/ruckus.t
- `axi4/`: full AXI4 support blocks and adapters.
- `bridge/`: bridges between AXI-family buses and SURF protocol records.
- `dma/`: DMA register, descriptor, FIFO, and stream integration cores.
- `simlink/`: simulator-link support and C/C++/VHDL pieces used by simulation flows.
- `simlink/`: simulator-link support and C/C++/VHDL pieces used by simulation flows (GHDL, VCS, and Vivado xsim backends). See [simlink/README.md](simlink/README.md) for the Rogue TCP/SideBand co-simulation usage guide.

Use existing package record types before adding flattened ports. Put durable adapter entities in `ip_integrator/` or `wrappers/`, and keep executable cocotb tests under `tests/axi/`.
39 changes: 39 additions & 0 deletions axi/simlink/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Simlink

This directory holds three interchangeable Rogue co-simulation backends -- GHDL/VHPIDIRECT (`ghdl/`), Synopsys VCS/VHPI (`vcs/`), and Vivado xsim/DPI (`xsim/`) -- selected automatically by `ruckus.tcl` based on the active simulator. All three backends share the same `shared/` C cores and speak the same ZeroMQ wire protocol to a Rogue-side Python peer, so a testbench built against one backend behaves the same on any of the others.

## Prerequisites

- `libzmq >= 4.1.0` -- check with `pkg-config --modversion libzmq`.
- A Rogue-environment shell for the peer scripts (they `import pyrogue` / `import rogue`).

## Running the Vivado xsim Co-Simulation

1. Run `make gui` from the target directory. Ruckus auto-detects whichever of `RogueTcpStream.vhd`, `RogueTcpMemory.vhd`, or `RogueSideBand.vhd` is present in the sim sources, builds the combined `RogueTcpDpi.so` via `xsc`, and binds it with a single `-sv_lib RogueTcpDpi` -- no manual build step is required.
2. Watch the Tcl/GUI console for the `xsc` build completing and, after `launch_simulation` / `run`, the module's `Listening on ports N & N+1` message -- that print is the ready-for-peer signal.
3. In a separate terminal, start the matching Rogue-side driver script against that module's port.
4. Watch the waveform -- data movement between the DPI adapter and the peer confirms the round trip.

| Module | Demo TB | Ports | Peer script |
|--------|---------|-------|-------------|
| RogueTcpStream | `RogueTcpStreamXsimDemoTb` | 9000 (push on 9001) | `prbsLoopbackDemo.py` |
Comment thread
bengineerd marked this conversation as resolved.
Outdated
| RogueTcpMemory | `RogueTcpMemoryXsimDemoTb` | 9100 (push on 9101) | `axiVersionMemoryDemo.py` |
| RogueSideBand | `RogueSideBandXsimDemoTb` | 9200 (push on 9201) | `sideBandDemo.py` |

## Selecting a Module

Vivado elaborates one top per simulation fileset, so switching which module's demo runs is a single manual step: in the target's `ruckus.tcl`, comment/uncomment the desired `set_property top {...XsimDemoTb}` line, then run `make gui` again. This is the only manual step when switching between modules -- the DPI build and `-sv_lib` binding stay identical regardless of which module is selected.

## 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 libzmq (or source the Rogue setup scripts that put it on `PKG_CONFIG_PATH`) and re-run `make gui`.

**Idle waveform, no data movement**
If the console printed `Listening on ports N & N+1` but the waveform never shows activity, the peer has not connected. Confirm the driver script is running, targeting the same host (loopback `127.0.0.1`) and port pair as the module under test, and that push/pull directions are not swapped.
37 changes: 31 additions & 6 deletions axi/simlink/ruckus.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,36 @@ source $::env(RUCKUS_PROC_TCL)
# Load Simulation
loadSource -lib surf -sim_only -dir "$::DIR_PATH/tb"

# Check if not GHDL
if {![info exists ::env(GHDLFLAGS)]} {
# Include VHPI
loadSource -lib surf -sim_only -dir "$::DIR_PATH/vcs"
# Select the simulator backend. Prefer the backend explicitly requested by the
# ruckus make target (make xsim/gui -> xsim, make vcs -> vcs). Fall back to
# environment sniffing for older ruckus. Note: sniffing VCS_VERSION alone is
# unreliable because users commonly source both Vivado and VCS in one setup
# script, so VCS_VERSION is set even during a Vivado xsim run.
if {[info exists ::env(RUCKUS_SIM_BACKEND)]} {
set simBackend $::env(RUCKUS_SIM_BACKEND)
} elseif {[info exists ::env(GHDLFLAGS)]} {
set simBackend "ghdl"
} elseif {[info exists ::env(VCS_VERSION)]} {
set simBackend "vcs"
} else {
# Exclude VHPI
loadSource -lib surf -sim_only -dir "$::DIR_PATH/ghdl"
set simBackend "xsim"
}

# When re-generating an existing Vivado project, purge sibling-backend sources
# so switching backends doesn't leave duplicate RogueTcp* entities in sim_1.
# Only remove when present, to avoid needless add/remove churn in the project.
# Guard on VIVADO_VERSION because get_files/remove_files are Vivado-only; GHDL
# re-analyzes from scratch and has no persistent project to clean.
if {$::env(VIVADO_VERSION) > 0.0} {
foreach other {ghdl vcs xsim} {
if {${other} ne ${simBackend}} {
set staleFiles [get_files -quiet [file normalize "$::DIR_PATH/${other}/*"]]
if {[llength ${staleFiles}] > 0} {
remove_files -quiet ${staleFiles}
}
}
}
}

# Load the selected backend
loadSource -lib surf -sim_only -dir "$::DIR_PATH/${simBackend}"
2 changes: 1 addition & 1 deletion axi/simlink/tb/RogueTcpStreamWrap.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ begin
end generate;

-- Channels
U_ChanGen : for i in 0 to CHAN_COUNT_C-1 generate
GEN_CHAN : for i in 0 to CHAN_COUNT_C-1 generate
------------------
-- Inbound Resizer
------------------
Expand Down
52 changes: 52 additions & 0 deletions axi/simlink/xsim/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
##############################################################################
## This file is part of 'SLAC Firmware Standard Library'.
## It is subject to the license terms in the LICENSE.txt file found in the
## top-level directory of this distribution and at:
## https://confluence.slac.stanford.edu/display/ppareg/LICENSE.html.
## No part of 'SLAC Firmware Standard Library', including this file,
## may be copied, modified, propagated, or distributed except according to
## the terms contained in the LICENSE.txt file.
##############################################################################
# xsc-based build (Vivado xsim DPI-C), re-targeted from ghdl/Makefile's
# gcc -shared -fPIC + pkg-config libzmq pattern to the xsc two-step compile/
# link flow. Builds the combined RogueTcpDpi.so from every module's DPI-C
# adapter (RogueTcpStream.c, RogueTcpMemory.c, RogueSideBand.c); a single
# "-sv_lib RogueTcpDpi" binds all of them. Each adapter keeps its own
# file-scope static data and name-prefixed
# functions, so linking them into one .so has no symbol collisions. The
# file is named RogueTcpDpi.so (no "lib" prefix): xelab's "-sv_lib
# RogueTcpDpi" resolves the DPI library as <name>.so, not lib<name>.so
# (per UG900). xsc stages its compiled object under its own implicit
# ./xsim.dir/work/xsc/ directory (no BUILD_DIR indirection like GHDL/VCS use).
##############################################################################

CC := xsc
# -I../shared resolves the shared RogueTcp*Core.h headers that every backend
# includes; xsc's underlying gcc searches the including file's own dir
# first, so the xsim/ RogueTcp*.h still take precedence. svdpi.h is
# auto-found by xsc (adds $XILINX_VIVADO/data/xsim/include automatically).
CFLAGS := -I../shared $(shell pkg-config --cflags libzmq)
# Vivado <= 2024.1 bundles a gcc-9.3.0 whose link driver invokes its own
# binutils via -B and does not search the host multiarch lib dir, so the
# RogueTcpDpi.so link fails with "cannot find crti.o / -lzmq / -lm". Point both
# the startfile search (-B, for crt*.o) and the library search (-L) at the
# active gcc's lib dir, derived portably from `gcc -print-file-name=crti.o`
# (resolves to /usr/lib/x86_64-linux-gnu on Debian/Ubuntu, /usr/lib64 on RHEL).
# Harmless on Vivado >= 2024.2, whose xsc already resolves these.
MULTIARCH_DIR := $(dir $(shell gcc -print-file-name=crti.o))
LFLAGS := $(shell pkg-config --libs libzmq) -B$(MULTIARCH_DIR) -L$(MULTIARCH_DIR)

LIB := RogueTcpDpi.so

all: $(LIB)

$(LIB): RogueTcpStream.c RogueTcpMemory.c RogueSideBand.c
$(CC) -c RogueTcpStream.c -gcc_compile_options "$(CFLAGS)"
$(CC) -c RogueTcpMemory.c -gcc_compile_options "$(CFLAGS)"
$(CC) -c RogueSideBand.c -gcc_compile_options "$(CFLAGS)"
$(CC) --shared -o $(LIB) -gcc_link_options "$(LFLAGS)"

clean:
rm -rf xsim.dir xsc.pb xsc.log $(LIB)

.PHONY: all clean
72 changes: 72 additions & 0 deletions axi/simlink/xsim/RogueSideBand.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//////////////////////////////////////////////////////////////////////////////
// This file is part of 'SLAC Firmware Standard Library'.
// It is subject to the license terms in the LICENSE.txt file found in the
// top-level directory of this distribution and at:
// https://confluence.slac.stanford.edu/display/ppareg/LICENSE.html.
// No part of 'SLAC Firmware Standard Library', including this file,
// may be copied, modified, propagated, or distributed except according to
// the terms contained in the LICENSE.txt file.
//////////////////////////////////////////////////////////////////////////////
//
// Vivado xsim DPI-C backend for the Rogue side-band model. The ZMQ transport
// (RogueSideBandRestart/Send/Recv) and the opcode/remData FSM
// (RogueSideBandStep) live in axi/simlink/shared/RogueSideBandCore.h,
// included by every backend. This file provides only the DPI-specific
// plumbing: a per-edge update function (rogueSideBandUpdate) that copies the
// DPI svBit/svBitVecVal parameters into the input snapshot, runs one FSM
// step, and writes the outputs back through the DPI output pointers. Unlike
// the GHDL VHPIDIRECT backend, every port here is <=32 bits, so each vector
// argument is a single svBitVecVal word -- no encode/decode loop is needed.
//////////////////////////////////////////////////////////////////////////////

#include <zmq.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include "svdpi.h"
#include "RogueSideBand.h"
#include "RogueSideBandCore.h"

// Single instance for this simulation; file-scope statics are zero-
// initialized by C, replacing the src Init's malloc+memset.
static RogueSideBandData sideBandData;

// Per-edge update function, called from the RogueSideBandDpi SV leaf every
// rising_edge(clock) via import "DPI-C". Copies each DPI parameter straight
// into the input snapshot (the getInt seam), runs one shared FSM step, then
// writes the outputs back through the DPI output pointers.
void rogueSideBandUpdate(unsigned char reset, const svBitVecVal *portNum,
const svBitVecVal *txOpCode, unsigned char txOpCodeEn, const svBitVecVal *txRemData,
svBitVecVal *rxOpCode, unsigned char *rxOpCodeEn, svBitVecVal *rxRemData) {
RogueSideBandData *data = &sideBandData;
unsigned int reqPort = portNum[0] & 0xFFFF;

// DPI-C imports carry no per-instance context, so this backend hosts a
// single global sideBandData and supports only one RogueSideBand per
// simulation. A second instance would otherwise silently share this
// state and bind only the first port. Fail fast once a different,
// already-latched port is observed rather than corrupt state.
if ( data->port != 0 && reqPort != 0 && reqPort != data->port ) {
vhpi_printf("RogueSideBand: Vivado xsim DPI-C backend supports only one instance per simulation; observed ports %u and %u\n", data->port, reqPort);
vhpi_assert("RogueSideBand: multiple instances unsupported under Vivado xsim DPI-C", vhpiFatal);
return;
}

data->inSnap[s_reset] = reset ? 1 : 0;
data->inSnap[s_port] = reqPort;
data->inSnap[s_txOpCode] = txOpCode[0];
data->inSnap[s_txOpCodeEn] = txOpCodeEn ? 1 : 0;
data->inSnap[s_txRemData] = txRemData[0];

// RogueSideBandStep (shared FSM) calls RogueSideBandRecv, which uses
// ZMQ_DONTWAIT -- never make this a blocking call, or it freezes the
// whole Vivado xsim process, not just a background thread.
RogueSideBandStep(data);

rxOpCode[0] = data->outState[s_rxOpCode];
*rxOpCodeEn = data->outState[s_rxOpCodeEn] ? 1 : 0;
rxRemData[0] = data->outState[s_rxRemData];
}
81 changes: 81 additions & 0 deletions axi/simlink/xsim/RogueSideBand.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//////////////////////////////////////////////////////////////////////////////
// This file is part of 'SLAC Firmware Standard Library'.
// It is subject to the license terms in the LICENSE.txt file found in the
// top-level directory of this distribution and at:
// https://confluence.slac.stanford.edu/display/ppareg/LICENSE.html.
// No part of 'SLAC Firmware Standard Library', including this file,
// may be copied, modified, propagated, or distributed except according to
// the terms contained in the LICENSE.txt file.
//////////////////////////////////////////////////////////////////////////////

#ifndef __ROGUE_SIDE_BAND_H__
#define __ROGUE_SIDE_BAND_H__

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

// Signals
#define s_clock 0
#define s_reset 1
#define s_port 2

#define s_txOpCode 3
#define s_txOpCodeEn 4
#define s_txRemData 5

#define s_rxOpCode 6
#define s_rxOpCodeEn 7
#define s_rxRemData 8

#define PORT_COUNT 9

// Vivado xsim's DPI-C boundary has no VHPI (no vhpi_register_cb / value-change
// callbacks), so the VHPI print/assert calls in the transplanted FSM body are
// shimmed straight to printf/abort. The severity argument is accepted but
// never referenced, so no VHPI severity token (e.g. vhpiFatal) needs defining.
// This shim is backend-agnostic and is shared verbatim with the GHDL backend.
#define vhpi_printf(...) printf(__VA_ARGS__)
#define vhpi_assert(msg, sev) \
do { \
fprintf(stderr, "%s\n", (msg)); \
abort(); \
} while (0)

// Macros for get/set ints, redefined for the per-edge update-procedure model:
// getInt reads the input snapshot populated at update-procedure entry;
// setInt writes the output state read back through the DPI output pointers.
#define getInt(idx) (data->inSnap[idx])
#define setInt(idx, val) (data->outState[idx] = (val))

// Structure to track state
typedef struct {
uint16_t port;

uint8_t rxRemData;
uint8_t rxOpCode;
uint8_t rxOpCodeEn;

uint8_t txRemData;
uint8_t txRemDataChanged;
uint8_t txOpCode;
uint8_t txOpCodeEn;

unsigned int inSnap[PORT_COUNT];
unsigned int outState[PORT_COUNT];

void * zmqCtx;
void * zmqPull;
void * zmqPush;
} RogueSideBandData;

// Start/restart zeromq server
void RogueSideBandRestart(RogueSideBandData *data);

// Send a message
void RogueSideBandSend(RogueSideBandData *data);

// Receive data if it is available
int RogueSideBandRecv(RogueSideBandData *data);

#endif
70 changes: 70 additions & 0 deletions axi/simlink/xsim/RogueSideBand.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
-------------------------------------------------------------------------------
-- Company : SLAC National Accelerator Laboratory
-------------------------------------------------------------------------------
-- Description: Rogue Side Band Simulation Module
-------------------------------------------------------------------------------
-- This file is part of 'SLAC Firmware Standard Library'.
-- It is subject to the license terms in the LICENSE.txt file found in the
-- top-level directory of this distribution and at:
-- https://confluence.slac.stanford.edu/display/ppareg/LICENSE.html.
-- No part of 'SLAC Firmware Standard Library', including this file,
-- may be copied, modified, propagated, or distributed except according to
-- the terms contained in the LICENSE.txt file.
-------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;

entity RogueSideBand is
port (
clock : in std_logic;
reset : in std_logic;
portNum : in std_logic_vector(15 downto 0);

txOpCode : in std_logic_vector(7 downto 0);
txOpCodeEn : in std_logic;
txRemData : in std_logic_vector(7 downto 0);
rxOpCode : out std_logic_vector(7 downto 0);
rxOpCodeEn : out std_logic;
rxRemData : out std_logic_vector(7 downto 0));
end RogueSideBand;

-- Define architecture
architecture RogueSideBand of RogueSideBand is

------------------------------------------------------------------------
-- Vivado xsim has no VHPI, so this fork forwards to the DPI-C model via a
-- native mixed-language component instantiation of the SV leaf
-- RogueSideBandDpi (below), instead of GHDL's attribute foreign
-- VHPIDIRECT binding. VHPI original: axi/simlink/vcs/RogueSideBand.vhd
------------------------------------------------------------------------

component RogueSideBandDpi
port (
clock : in std_logic;
reset : in std_logic;
portNum : in std_logic_vector(15 downto 0);

txOpCode : in std_logic_vector(7 downto 0);
txOpCodeEn : in std_logic;
txRemData : in std_logic_vector(7 downto 0);
rxOpCode : out std_logic_vector(7 downto 0);
rxOpCodeEn : out std_logic;
rxRemData : out std_logic_vector(7 downto 0));
end component;

begin

U_Dpi : component RogueSideBandDpi
port map (
clock => clock,
reset => reset,
portNum => portNum,
txOpCode => txOpCode,
txOpCodeEn => txOpCodeEn,
txRemData => txRemData,
rxOpCode => rxOpCode,
rxOpCodeEn => rxOpCodeEn,
rxRemData => rxRemData);

end RogueSideBand;
Loading