QCORE-C1 Quick Start Guide

QCORE-QSG-001 Rev 0.9 — January 2026

Get running on the FPGA evaluation platform — QLI link bring-up, register verification, and your first hardware-accelerated ML-KEM key exchange operation. This guide uses the Xilinx XCZU7EV evaluation board with the QCORE-C1 FPGA bitstream.

Prerequisites: Xilinx XCZU7EV evaluation board, Vivado 2023.2+, USB-JTAG cable, Linux workstation with Python 3.8+ and cocotb installed, QCORE-C1 FPGA Evaluation Bitstream (available on the Downloads tab).

Step 1: FPGA Board Setup #

Connect the XCZU7EV evaluation board to your workstation via USB-JTAG. Ensure the board is configured for JTAG boot mode (SW1 switches: OFF-ON-OFF-OFF).

ConnectionDetails
Power supply12V / 5A barrel connector (supplied with eval board)
JTAGMicro-USB to J2 debug connector
UART consoleMicro-USB to J3 (115200 baud, 8N1)
Status LEDsLD0 = Power, LD1 = FPGA Done, LD2 = QLI Link Up, LD3 = Heartbeat

Step 2: Load the Bitstream #

Program the FPGA with the QCORE-C1 evaluation bitstream using Vivado Hardware Manager or the command-line tools:

# Using Vivado command line
source /opt/Xilinx/Vivado/2023.2/settings64.sh

# Program the FPGA
vivado -mode batch -source program_qcore.tcl

# Or use the provided script
./scripts/program_fpga.sh qcore_c1_eval_xczu7ev.bit

Wait for LD1 (FPGA Done) to illuminate solid green. The heartbeat LED (LD3) should begin blinking at 1Hz within 2 seconds, confirming the QCORE-C1 design is running.

Step 3: Verify Register Access #

Use the provided Python utility to read the QCORE-C1 identification and status registers through the JTAG debug interface:

# Install the QCORE-C1 development tools
pip install libqcore

# Read chip identification
python3 -c "
from libqcore import QCore
dev = QCore.connect('jtag')
print(f'Chip ID:   0x{dev.read_reg(0x000):08X}')
print(f'Version:   0x{dev.read_reg(0x004):08X}')
print(f'Status:    0x{dev.read_reg(0x008):08X}')
print(f'NTT Ready: {bool(dev.read_reg(0x008) & 0x01)}')
print(f'QLI Up:    {bool(dev.read_reg(0x008) & 0x02)}')
"

Expected output for a correctly configured evaluation platform:

Chip ID:   0x44594231    # "DYB1" — Dyber QCORE-C1
Version:   0x00000905    # RTL v0.9.5
Status:    0x00000003    # NTT Ready + QLI Up
NTT Ready: True
QLI Up:    True

The QLI interface uses an internal loopback path on the evaluation platform. Verify link training completes and credits are exchanged:

python3 -c "
from libqcore import QCore, QLI
dev = QCore.connect('jtag')
qli = QLI(dev)

# Check link state
state = qli.link_state()
print(f'Link State:   {state}')         # Expected: ACTIVE
print(f'TX Credits:   {qli.tx_credits()}')  # Expected: 32
print(f'RX Credits:   {qli.rx_credits()}')  # Expected: 32
print(f'CRC Errors:   {qli.crc_errors()}')  # Expected: 0

# Send a loopback test packet
result = qli.loopback_test(count=100)
print(f'Loopback:     {result.passed}/{result.total} passed')
"
Note: On the FPGA evaluation platform, QLI operates in loopback mode at 50MHz (reduced from the ASIC target of 500MHz+). This validates protocol correctness but not production-speed timing.

Step 5: First ML-KEM Operation #

Execute a complete ML-KEM-768 key encapsulation and decapsulation cycle using the hardware NTT array and Keccak core:

python3 -c "
from libqcore import QCore, MLKEM
import time

dev = QCore.connect('jtag')
kem = MLKEM(dev, parameter_set=768)

# Key Generation
t0 = time.perf_counter_ns()
pk, sk = kem.keygen()
t_keygen = time.perf_counter_ns() - t0
print(f'KeyGen:     {t_keygen:,} ns')
print(f'Public key: {len(pk)} bytes')
print(f'Secret key: {len(sk)} bytes')

# Encapsulation
t0 = time.perf_counter_ns()
ct, ss_enc = kem.encaps(pk)
t_encaps = time.perf_counter_ns() - t0
print(f'Encaps:     {t_encaps:,} ns')

# Decapsulation
t0 = time.perf_counter_ns()
ss_dec = kem.decaps(ct, sk)
t_decaps = time.perf_counter_ns() - t0
print(f'Decaps:     {t_decaps:,} ns')

# Verify shared secrets match
assert ss_enc == ss_dec, 'SHARED SECRET MISMATCH!'
print(f'✓ Shared secrets match ({len(ss_enc)} bytes)')
"

Expected output on the XCZU7EV evaluation platform (100MHz clock, QLI loopback):

KeyGen:     4,200 ns
Public key: 1184 bytes
Secret key: 2400 bytes
Encaps:     5,100 ns
Decaps:     6,300 ns
✓ Shared secrets match (32 bytes)
Note: These timings include JTAG transport overhead. On the production ASIC at 500MHz (GF22FDX), projected latencies are ~400ns KeyGen, ~500ns Encaps, ~600ns Decaps. See the Datasheet for target silicon performance.

Step 6: Run NIST Test Vectors #

Validate the hardware implementation against the NIST ACVP known-answer test vectors:

# Run the full NIST KAT validation suite
python3 -m libqcore.test.kat_runner --device jtag --algo ml-kem --levels 512,768,1024

# Expected output:
# ML-KEM-512: 100/100 KAT vectors PASSED
# ML-KEM-768: 100/100 KAT vectors PASSED
# ML-KEM-1024: 100/100 KAT vectors PASSED
# ✓ All KAT validations passed

Next Steps #

Now that the evaluation platform is operational, explore these resources to deepen your integration:

ResourceDescription
Developer GuideRegister-level programming, DMA configuration, interrupt handling
QLI Interface ReferenceFull protocol specification for host-side QLI bridge implementation
Integration GuideOEM integration handbook for SoC designers
Architecture Deep DiveInternal block architecture and design rationale