14 - XCOM Network Synchronization
Objective: Learn how to synchronize multiple QICK boards and exchange low-latency data using the XCOM network. This notebook covers:
XCOM architecture overview (full mesh network, deterministic latency)
Hardware setup (FMC transceiver board, external hub)
Initializing the XCOM network (AUTO_ID, master/slave configuration)
Sending and receiving data between boards (8-bit, 16-bit, 32-bit)
Loopback testing and latency measurement
tProc integration for real-time communication
Debugging XCOM (status registers, debug registers)
Synchronizing tProc absolute clocks across multiple boards
Prerequisites:
Two or more QICK boards with XCOM FMC transceiver boards
External XCOM hub connecting all boards
Stable external reference clock (e.g., rubidium clock)
Completed basic tutorials (00-05)
References:
XCOM Paper: arXiv:2603.18977v1
QICK XCOM driver:
qick.ip.QICK_Xcom
1. XCOM Overview
XCOM (X - Communication) is a full mesh network that enables:
Synchronization of multiple QICK boards with <100 ps skew
Low-latency communication (186 ns per 32-bit word, can be reduced to 62 ns)
Deterministic message exchange without central control
All-to-all simultaneous communication on different channels
tProc integration for real-time data exchange
Hardware Requirements
┌─────────────────────────────────────────────────────────────────────────────┐
│ XCOM Network Topology │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ QICK #1 │ │ QICK #2 │ │ QICK #3 │ │ QICK #4 │ │
│ │ (Master) │ │ (Slave) │ │ (Slave) │ │ (Slave) │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ XCOM Hub (Fanout Buffers) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ • Each board has a dedicated Tx channel (LVDS @ up to 312.9 MHz) │
│ • All boards listen on all Rx channels (full mesh) │
│ • External reference clock (e.g., 100 MHz rubidium) for frequency sync │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Supported boards: Up to 15 boards (prototype supports 5)
Key capabilities:
Sub-picosecond timing jitter with nested zero-delay PLL mode
Multi-tile sync (MTS) for DAC/ADC alignment across boards
48-bit absolute clock counter synchronized across all boards
Deterministic communication latency (independent of network traffic)
2. Setup and Initialization
[ ]:
# Standard imports
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import time
import logging
from qick import *
from qick.asm_v2 import AveragerProgramV2
from qick.tprocv2_assembler import Assembler
# Setup logging
logging.basicConfig(level=logging.INFO, format='%(levelname)-8s [%(filename)s:%(lineno)d] %(message)s')
# Connect to the board (adjust path to your firmware with XCOM support)
# For multi-board setup, create separate Soc instances for each board
BITSTREAM_PATH = '/path/to/your/xcom_firmware.bit' # <--- CHANGE THIS
soc = QickSoc(BITSTREAM_PATH, external_clk=True, force_init_clks=True)
soccfg = soc
print(f"Connected to QICK board")
print(f"Firmware version: {soccfg.get('fw_version', 'Unknown')}")
print(f"XCOM available: {hasattr(soc, 'xcom_0')}")
# For multi-board experiments, you would connect to each board separately:
# soc_master = QickSoc('master_firmware.bit', external_clk=True)
# soc_slave1 = QickSoc('slave1_firmware.bit', external_clk=True)
# soc_slave2 = QickSoc('slave2_firmware.bit', external_clk=True)
2.1 Initialize Clock and Tile Synchronization
Before using XCOM, the board clocks and DAC/ADC tiles must be synchronized.
[ ]:
# Initialize tile synchronization (required for multi-board alignment)
print("Initializing tile synchronization...")
soc.init_tile_sync()
soc.verify_clock_tree()
soc.sync_tiles()
print("Tile synchronization complete.")
# Verify external clock is locked
print(f"External clock locked: {soc.check_ext_clk()}")
3. XCOM Driver Overview
The QICK_Xcom class provides the Python interface to the XCOM hardware. It inherits from SocIP and uses AXI-lite for register access.
[ ]:
# Display XCOM registers and opcodes
print("=== XCOM Registers ===")
for reg_name, reg_addr in soc.xcom_0.REGISTERS.items():
print(f" {reg_name:15} : address {reg_addr}")
print("\n=== XCOM Opcodes ===")
for op_name, op_code in soc.xcom_0.opcodes.items():
print(f" {op_name:20} : {op_code}")
3.1 Key XCOM Operations
Method |
Opcode |
Description |
|---|---|---|
|
XCOM_AUTO_ID |
Auto-assign network IDs to all boards |
|
XCOM_SET_ID |
Manually set this board’s ID |
|
XCOM_SEND_8BIT_1/2 |
Send 8 bits to board |
|
XCOM_SEND_16BIT_1/2 |
Send 16 bits to board |
|
XCOM_SEND_32BIT_1/2 |
Send 32 bits to board |
|
XCOM_SET_FLAG |
Set flag on destination board |
|
XCOM_CLEAR_FLAG |
Clear flag on destination board |
|
(any) |
Execute any opcode with data |
Note: reg parameter (1 or 2) selects which destination register to write to.
4. Network Configuration
4.1 Reset and Auto-ID
The first step is to reset the XCOM network and assign IDs to all connected boards.
[ ]:
# Reset the XCOM network
print("Resetting XCOM network...")
soc.xcom_0.run_cmd(soc.xcom_0.opcodes['XCOM_RST'], 0, 0)
time.sleep(0.1)
# Run AUTO_ID to discover and assign IDs to all boards
print("Running AUTO_ID...")
soc.xcom_0.run_cmd(soc.xcom_0.opcodes['XCOM_AUTO_ID'], 0, 0)
time.sleep(0.5)
# Set local board ID (master board in this example)
BOARD_ID_MASTER = 2
soc.xcom_0.set_local_id(BOARD_ID_MASTER)
print(f"Local board ID set to: {BOARD_ID_MASTER}")
# Re-run AUTO_ID to confirm
soc.xcom_0.auto_id()
time.sleep(0.1)
# Configure XCOM (enable certain features)
# 0x400 typically enables the XCOM interface
soc.xcom_0.xcom_cfg = 0 + 0x400
print(f"XCOM config: 0x{soc.xcom_0.xcom_cfg:04X}")
4.2 Verify Configuration
Check the status and register values to ensure proper configuration.
[ ]:
# Print XCOM status
soc.xcom_0.print_status()
# Print all AXI registers
print("\n")
soc.xcom_0.print_axi_regs()
# Print debug information
print("\n")
soc.xcom_0.print_debug()
5. Sending Data Between Boards
5.1 Sending 8-bit Data
Use send_byte() to transmit 8-bit values to another board.
[ ]:
# Send 8-bit data to another board (e.g., board ID 5)
DEST_BOARD = 5
test_byte = 0xAA
print(f"Sending 8-bit data (0x{test_byte:02X}) to board {DEST_BOARD}...")
soc.xcom_0.send_byte(test_byte, DEST_BOARD, reg=2)
print("Data sent.")
# For multi-board setup, you would read the data on the destination board:
# received_data = soc_slave.xcom_0.mem # Read from XCOM memory
5.2 Sending 16-bit Data
Use send_half_word() to transmit 16-bit values.
[ ]:
# Send 16-bit data
test_half_word = 0xABCD
print(f"Sending 16-bit data (0x{test_half_word:04X}) to board {DEST_BOARD}...")
soc.xcom_0.send_half_word(test_half_word, DEST_BOARD, reg=2)
print("Data sent.")
5.3 Sending 32-bit Data
Use send_word() to transmit 32-bit values.
[ ]:
# Send 32-bit data
test_word = 0xDEADBEEF
print(f"Sending 32-bit data (0x{test_word:08X}) to board {DEST_BOARD}...")
soc.xcom_0.send_word(test_word, DEST_BOARD, reg=2)
print("Data sent.")
5.4 Writing to XCOM Memory
XCOM has internal memory (16x32-bit) that can be written to and read from.
[ ]:
# Write to XCOM memory on destination board
dest_addr = 5
data_to_write = 350
print(f"Writing {data_to_write} to memory address {dest_addr} on board {DEST_BOARD}")
soc.xcom_0.run_cmd(soc.xcom_0.opcodes['XCOM_WRITE_MEM'], dest_addr, data_to_write)
# Read back the memory
print("\nReading XCOM memory (local board):")
print("addr\tvalue")
for i in range(16):
soc.xcom_0.axi_addr = i
print(f"{i:3d}\t{soc.xcom_0.mem}")
6. tProc Integration for XCOM
XCOM can be controlled directly from the tProc using assembly instructions. The PA (Peripheral Access) instruction is the primary mechanism for tProc-XCOM interaction.
[ ]:
# Example assembly program for XCOM communication from tProc
xcom_tproc_asm = """
// XCOM Communication Test Program (tProc side)
// Sends data to another board via XCOM
// Initialize registers
REG_WR r1 imm #1 // Configuration register 1
REG_WR r2 imm #2 // Configuration register 2
REG_WR r3 imm #3 // Configuration register 3
REG_WR r4 imm #4 // Configuration register 4
REG_WR r5 imm #5 // Destination board ID
REG_WR r6 imm #424 // Data to send
// Write to XCOM control/configuration
// ctrl_csf_qpa = clear flag in cfg/cntl
// cfg_src_qpa = configure source
// Trigger output pin for scope synchronization
TRIG p0 set
// Write to XCOM memory (WRITE_MEM operation)
REG_WR s_ctrl imm ctrl_csf_qpa // Clear flag in cfg/cntl
WAIT qpa_rdy // Wait for peripheral ready
PA 7 r2 r6 // Peripheral access: write r6 using config in r2
WAIT qpa_dt // Wait for data transfer complete
// Clear trigger
TRIG p0 clr
// Set flag on destination board
WAIT qpa_rdy
PA 1 r5 // Set flag on board in r5
WAIT qpa_dt
.END
"""
# Compile the assembly
p_txt, p_bin = Assembler.str_asm2bin(xcom_tproc_asm)
print(f"Assembly compiled: {len(p_bin)} instructions")
print("\nAssembly code:")
print(p_txt)
# Load the program into tProc program memory
# Note: The method name may vary based on your firmware version
# soc.qick_processor_0.load_mem(mem_sel='pmem', buff_in=p_bin, check=False)
print("\nProgram ready to load to tProc.")
print("Uncomment the load_mem line and adjust the method name as needed for your firmware.")
6.1 tProc Peripheral Access (PA) Instruction
The PA instruction is used for peripheral access. The syntax is:
PA <cfg_reg> <addr_reg> <data_reg>
Where:
cfg_reg: Configuration register (controls the operation)addr_reg: Address/target register (e.g., destination board ID)data_reg: Data register (value to send)
Common PA operations:
PA cfg |
Operation |
Description |
|---|---|---|
0 |
CLEAR_FLAG |
Clear flag on destination board |
1 |
SET_FLAG |
Set flag on destination board |
6 |
XCOM QCTRL |
XCOM control operation |
7 |
WRITE_MEM |
Write to XCOM memory |
7. Master-Slave Communication Example
In a multi-board setup, one board acts as the master and others as slaves. The master can:
Broadcast reset/start commands to synchronize absolute clocks
Send data to specific slaves
Receive data from slaves
Coordinate multi-board experiments
7.1 Master Board Configuration
[ ]:
# Master board setup (run on the board designated as master)
def setup_master_board(soc, master_id=2):
"""Configure a board as XCOM master."""
print("Setting up master board...")
# Reset XCOM
soc.xcom_0.run_cmd(soc.xcom_0.opcodes['XCOM_RST'], 0, 0)
time.sleep(0.1)
# Auto-assign IDs
soc.xcom_0.run_cmd(soc.xcom_0.opcodes['XCOM_AUTO_ID'], 0, 0)
time.sleep(0.5)
# Set local ID
soc.xcom_0.set_local_id(master_id)
# Configure XCOM
soc.xcom_0.xcom_cfg = 0 + 0x400 + 0x100 # Additional config for master
print(f"Master board configured with ID: {master_id}")
return True
# Slave board configuration (run on each slave board)
def setup_slave_board(soc, slave_id):
"""Configure a board as XCOM slave."""
print(f"Setting up slave board with ID: {slave_id}...")
# Reset XCOM
soc.xcom_0.run_cmd(soc.xcom_0.opcodes['XCOM_RST'], 0, 0)
time.sleep(0.1)
# Auto-assign IDs
soc.xcom_0.run_cmd(soc.xcom_0.opcodes['XCOM_AUTO_ID'], 0, 0)
time.sleep(0.5)
# Set local ID
soc.xcom_0.set_local_id(slave_id)
# Configure XCOM
soc.xcom_0.xcom_cfg = 0 + 0x400
print(f"Slave board configured with ID: {slave_id}")
return True
print("Master/slave configuration functions defined.")
print("\nFor multi-board experiments:")
print(" master = setup_master_board(soc_master, master_id=2)")
print(" slave1 = setup_slave_board(soc_slave1, slave_id=5)")
print(" slave2 = setup_slave_board(soc_slave2, slave_id=7)")
7.2 Sending Data from Master to Slave
Once configured, the master can send data to any slave.
[ ]:
# Example: Master sending a ramp pattern to a slave
def master_send_ramp(soc_master, dest_slave_id, num_values=100):
"""Send a ramp of values from master to slave."""
print(f"Master sending {num_values} values to slave {dest_slave_id}...")
start_time = time.time()
for i in range(num_values):
# Send 16-bit value
soc_master.xcom_0.send_half_word(i, dest_slave_id, reg=2)
# Optional: wait for acknowledgment
# while soc_master.xcom_0.mem == 0:
# pass
elapsed = time.time() - start_time
print(f"Sent {num_values} values in {elapsed:.3f}s ({num_values/elapsed:.0f} values/sec)")
return elapsed
# On the slave side, you would read incoming data:
# def slave_receive_data(soc_slave, num_values=100):
# data = []
# for i in range(num_values):
# data.append(soc_slave.xcom_0.mem)
# return data
print("Master-to-slave communication functions defined.")
8. Loopback Testing
Loopback testing validates XCOM functionality by sending data to the same board (self-communication). This is useful for debugging without multiple boards.
[ ]:
# Loopback test: write to self
def xcom_loopback_test(soc, num_transfers=1000):
"""
Perform XCOM loopback test by writing to self.
Returns list of received data and measured latency.
"""
dest = 2 # Send to self (using local board ID)
# Configure XCOM for loopback
soc.xcom_0.xcom_cfg = 2 + 0x100 # Loopback mode
# Clear destination memory
soc.xcom_0.run_cmd(soc.xcom_0.opcodes['XCOM_WRITE_MEM'], dest, 0)
data_in = []
start_time = time.time()
for i in range(1, num_transfers):
# Send 16-bit value
soc.xcom_0.run_cmd(soc.xcom_0.opcodes['XCOM_SEND_16BIT_2'], dest, i)
# Wait for data to be received
while soc.xcom_0.mem == 0:
pass
data_in.append(soc.xcom_0.mem)
# Verify data integrity
if soc.xcom_0.mem != i:
print(f"Data mismatch at i={i}: sent {i}, received {soc.xcom_0.mem}")
# Clear for next transfer
soc.xcom_0.run_cmd(soc.xcom_0.opcodes['XCOM_WRITE_MEM'], dest, 0)
elapsed = time.time() - start_time
return data_in, elapsed
# Run loopback test
print("Running XCOM loopback test...")
try:
data_received, elapsed_time = xcom_loopback_test(soc, num_transfers=500)
print(f"\nLoopback test complete.")
print(f"Elapsed time: {elapsed_time:.3f}s")
print(f"Transfers: {len(data_received)}")
if len(data_received) > 0:
print(f"Average latency per transfer: {elapsed_time/len(data_received)*1000:.3f} ms")
# Plot received data to verify ramp pattern
plt.figure(figsize=(12, 6))
if len(data_received) > 100:
plt.plot(data_received[0:100], '.-')
else:
plt.plot(data_received, '.-')
plt.xlabel('Transfer Number')
plt.ylabel('Received Value')
plt.title('XCOM Loopback Test - Received Data')
plt.grid(True)
plt.show()
except Exception as e:
print(f"Loopback test failed: {e}")
9. Latency Measurement
XCOM provides deterministic, low-latency communication. Precise latency can be measured using the tProc’s absolute time counter.
[ ]:
# Assembly program to measure XCOM round-trip latency
latency_measure_asm = """
// XCOM Latency Measurement Program
// Measures round-trip latency by timing a send-receive cycle
// Initialize registers
REG_WR r1 imm #1
REG_WR r2 imm #2 // Destination board ID
REG_WR r3 imm #3
REG_WR r4 imm #4
REG_WR r5 imm #5
REG_WR r10 imm #0 // DMEM pointer for data storage
// Write some zeros to DMEM
DMEM_WR [r10] imm #0
DMEM_WR [r10 + &1] imm #0
DMEM_WR [r10 + &2] imm #0
DMEM_WR [r10 + &3] imm #0
DMEM_WR [r10 + &4] imm #0
DMEM_WR [r10 + &5] imm #0
// Clear XCOM flag
REG_WR s_ctrl imm ctrl_csf_qpa
LOOP:
// Record start time
REG_WR r12 op -op(s_usr_time)
// Send data via XCOM
WAIT qpa_rdy
PA 6 r2 r6 // XCOM QCTRL operation
WAIT qpa_dt
// Wait for response (flag from destination)
WAIT qpa_rdy
PA 0 r2 // Clear flag
WAIT qpa_dt
// Record end time
REG_WR r13 op -op(s_usr_time)
// Store delta time in DMEM
DMEM_WR [r10] op -op(r13 - r12)
// Increment pointer and data
REG_WR r6 op -op(r6 + #1)
REG_WR r10 op -op(r10 + #1)
// Loop counter
REG_WR r11 op -op(r11 - #1) -uf
JUMP LOOP -if(NZ)
.END
"""
print("Latency measurement program ready.")
print("Run this on the master board to measure XCOM round-trip latency.")
9.1 Analyzing Latency Data
After running the latency measurement program, analyze the collected timing data.
[ ]:
# Example: Reading latency data from DMEM after tProc execution
def read_latency_data(soc, num_samples=100):
"""Read latency measurement data from tProc DMEM."""
delta_times = []
for i in range(num_samples):
# Read from DMEM (addresses 0, 1, 2, ...)
# Method name may vary based on firmware
# delta = soc.qick_processor_0.single_read(2, i)
delta_times.append(0) # Placeholder
return delta_times
print("Latency data analysis functions defined.")
print("\nAfter running the tProc program, you would analyze data with:")
print(" delta_times = read_latency_data(soc, num_samples=100)")
print(" plt.hist(delta_times, bins=50)")
print(" print(f'Average latency: {np.mean(delta_times)} cycles')")
10. Using Flags for Synchronization
XCOM flags provide a fast, single-bit signaling mechanism between boards. They are useful for:
Triggering actions on slave boards
Synchronizing state machines across boards
Implementing token ring or handshake protocols
[ ]:
# Set flag on another board
def set_remote_flag(soc, dest_board_id):
"""Set the flag on a remote board."""
print(f"Setting flag on board {dest_board_id}...")
soc.xcom_0.set_flag(dest_board_id)
print("Flag set.")
# Clear flag on another board
def clear_remote_flag(soc, dest_board_id):
"""Clear the flag on a remote board."""
print(f"Clearing flag on board {dest_board_id}...")
soc.xcom_0.clear_flag(dest_board_id)
print("Flag cleared.")
# Check local flag status
def check_local_flag(soc):
"""Check the status of the local flag."""
flag_status = soc.xcom_0.flag
print(f"Local flag: {flag_status}")
return flag_status
# From tProc, you can wait for a flag using WAIT instruction
# WAIT qpa_rdy
# PA 0 r2 # Check/clear flag
# WAIT qpa_dt
print("Flag operations defined.")
print("\nExample usage:")
print(" set_remote_flag(soc_master, slave_id)")
print(" check_local_flag(soc_slave)")
11. Absolute Clock Synchronization
One of XCOM’s most powerful features is absolute clock synchronization across all boards. The master can broadcast reset and start commands to align all tProc absolute time counters.
11.1 Synchronization Protocol
Frequency synchronization: All boards share the same external reference clock (e.g., 100 MHz rubidium)
Phase synchronization: PLLs configured in nested zero-delay mode
DAC/ADC alignment: Multi-tile sync (MTS) calibration
Absolute time alignment: Master broadcasts RESET and START commands
[ ]:
# Master broadcasts reset to all boards
def broadcast_reset(soc_master):
"""Broadcast absolute clock reset to all boards."""
print("Broadcasting RESET command to all boards...")
# Use XCOM_QRST_SYNC opcode to broadcast reset
soc_master.xcom_0.run_cmd(soc_master.xcom_0.opcodes['XCOM_QRST_SYNC'], 0, 0)
time.sleep(0.1)
print("RESET broadcast complete.")
# Master broadcasts start to all boards
def broadcast_start(soc_master):
"""Broadcast absolute clock start to all boards."""
print("Broadcasting START command to all boards...")
# Use XCOM_QCTRL with appropriate parameters to start
soc_master.xcom_0.run_cmd(soc_master.xcom_0.opcodes['XCOM_QCTRL'], 4, 6) # proc_start
time.sleep(0.1)
print("START broadcast complete.")
# Verify synchronization
def verify_sync(soc_board, expected_time=None):
"""Read absolute time from tProc to verify sync."""
# Read absolute time from tProc (address depends on firmware)
# This is a placeholder - actual register address may vary
# abs_time = soc_board.qick_processor_0.single_read(2, 0)
abs_time = 0 # Placeholder
print(f"Board absolute time: {abs_time}")
return abs_time
print("Synchronization functions defined.")
print("\nTypical synchronization sequence:")
print(" 1. broadcast_reset(soc_master) # Reset all absolute clocks")
print(" 2. time.sleep(0.1)")
print(" 3. broadcast_start(soc_master) # Start all absolute clocks")
print(" 4. verify_sync(soc_master)")
print(" 5. verify_sync(soc_slave1) # Should show same value")
12. Debugging XCOM
XCOM provides several debugging registers to help diagnose issues.
[ ]:
def diagnose_xcom(soc):
"""Run comprehensive XCOM diagnostics."""
print("=" * 60)
print("XCOM DIAGNOSTICS")
print("=" * 60)
# Check XCOM presence
if not hasattr(soc, 'xcom_0'):
print("❌ XCOM not found! Check firmware bitstream.")
return False
print("✅ XCOM found")
# Print status register
print("\n--- Status Register ---")
soc.xcom_0.print_status()
# Print debug register
print("\n--- Debug Register ---")
soc.xcom_0.print_debug()
# Print all registers
print("\n--- All Registers ---")
soc.xcom_0.print_axi_regs()
# Test basic read/write
print("\n--- Basic Read/Write Test ---")
test_addr = 5
test_value = 0x12345678
try:
soc.xcom_0.axi_addr = test_addr
original = soc.xcom_0.mem
soc.xcom_0.mem = test_value
readback = soc.xcom_0.mem
soc.xcom_0.mem = original # Restore
if readback == test_value:
print(f"✅ Memory write/read: wrote 0x{test_value:08X}, read 0x{readback:08X}")
else:
print(f"❌ Memory mismatch: wrote 0x{test_value:08X}, read 0x{readback:08X}")
except Exception as e:
print(f"❌ Memory test failed: {e}")
# Test loopback (if configured)
print("\n--- Loopback Test ---")
try:
data, elapsed = xcom_loopback_test(soc, num_transfers=10)
if len(data) == 9 and all(data[i] == i+1 for i in range(9)):
print(f"✅ Loopback test passed ({len(data)} transfers)")
else:
print(f"⚠️ Loopback test had {len(data)} transfers, expected 9")
except Exception as e:
print(f"❌ Loopback test failed: {e}")
print("\n" + "=" * 60)
return True
# Run diagnostics
diagnose_xcom(soc)
13. Common Issues and Troubleshooting
[ ]:
print("""
╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║ XCOM TROUBLESHOOTING GUIDE ║
╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ ║
║ Issue │ Likely Cause │ Solution ║
║ ──────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────║
║ XCOM not detected │ Wrong firmware │ Use bitstream with XCOM IP ║
║ │ Missing FMC board │ Install XCOM transceiver board ║
║ ──────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────║
║ AUTO_ID fails │ No external hub connected │ Connect all boards to XCOM hub ║
║ │ Cable mismatch │ Use matched-length LVDS cables ║
║ ──────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────║
║ High latency (>200 ns) │ XCOM clock too low │ Increase LVDS clock (up to 312.9 MHz) │
║ │ Software overhead │ Use tProc instead of Python for timing │
║ ──────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────║
║ Data corruption │ LVDS signal integrity │ Check cable quality and length │
║ │ Clock skew │ Run MTS calibration │
║ ──────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────║
║ Flag not received │ Wrong destination ID │ Verify board IDs with auto_id() │
║ │ Flag not cleared │ Always clear flag after receipt │
║ ──────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────║
║ tProc XCOM commands fail │ Peripheral not ready │ Add WAIT qpa_rdy before PA │
║ │ Incorrect PA configuration │ Verify cfg register values │
║ ──────────────────────────────┼─────────────────────────────────┼───────────────────────────────────────────║
║ Synchronization drift │ External clock not locked │ Verify PLL lock status │
║ │ Missing MTS calibration │ Run init_tile_sync() and sync_tiles() │
║ ║
╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ QUICK DIAGNOSTIC COMMANDS ║
╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ ║
║ # Check if XCOM is present ║
║ hasattr(soc, 'xcom_0') ║
║ ║
║ # Print all XCOM registers ║
║ soc.xcom_0.print_axi_regs() ║
║ ║
║ # Check XCOM status ║
║ soc.xcom_0.print_status() ║
║ ║
║ # Run full diagnostics ║
║ diagnose_xcom(soc) ║
║ ║
╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
""")
14. Summary
You have learned:
XCOM Architecture: Full mesh network for multi-board synchronization and communication
Hardware Requirements: FMC transceiver boards and external hub for 5-15 boards
Network Configuration: AUTO_ID, set_local_id, master/slave roles
Data Transfer: Sending 8/16/32-bit data between boards with deterministic latency
tProc Integration: Using PA instruction for real-time XCOM access
Flags: Fast single-bit signaling for synchronization
Absolute Clock Sync: Broadcasting RESET/START to align all tProcs
Debugging: Status registers, diagnostics, troubleshooting
Key Takeaways
XCOM enables <100 ps skew and 186 ns deterministic latency between boards
Each board has a dedicated Tx channel; all boards listen on all Rx channels
The master board can synchronize absolute clocks across the entire network
tProc integration allows real-time XCOM communication without software overhead
Always run MTS calibration and verify external clock before using XCOM
Next Steps
For simpler multi-board synchronization (external clock + external start), see
`10_Multi_Board_Synchronization.ipynb<./10_Multi_Board_Synchronization.ipynb>`__Proceed to explore other advanced topics
For more information, refer to the XCOM paper (arXiv:2603.18977v1)
Check the QICK firmware documentation for XCOM IP details
References
For detailed XCOM documentation, see XCOM Overview
XCOM Command Reference: Command Reference
XCOM Paper: arXiv:2603.18977v1
QICK XCOM driver:
qick.ip.QICK_Xcom