Bloqade Lanes
Bloqade Lanes is a component of QuEra's Neutral Atom SDK. It compiles quantum circuits down to physical atom movement instructions for neutral atom quantum processors.
What's in this book
- Architecture Specification — the
ArchSpecJSON format that defines device topology, transport buses, zones, and AOD paths - Instruction Quick Reference — compact summary of all 24 instructions with opcodes and stack effects
- Instruction Set — the fixed-width instruction encoding, opcode layout, and per-instruction reference
- CLI Reference — the
bloqade-bytecodeCLI tool for assembling, disassembling, and validating bytecode programs
Crate documentation
The Rust API documentation is generated separately via cargo doc:
bloqade-lanes-bytecode-core— pure Rust: bytecode format, arch spec, validationbloqade-lanes-bytecode-cli— CLI tool and C library
Repository
Source code: github.com/QuEraComputing/bloqade-lanes
ArchSpec — Architecture Specification
The ArchSpec defines the physical topology and transport capabilities of a Bloqade quantum device. It is the input that the bytecode compiler and validator use to determine which instructions are legal for a given hardware configuration.
The formal JSON Schema is available at archspec-schema.json.
Top-Level Structure
{
"version": "1.0",
"geometry": { ... },
"buses": { ... },
"words_with_site_buses": [...],
"sites_with_word_buses": [...],
"zones": [...],
"entangling_zones": [...],
"measurement_mode_zones": [...],
"paths": [...], // optional
"feed_forward": false, // optional, default false
"atom_reloading": false // optional, default false
}
| Field | Type | Description |
|---|---|---|
version | string | Format version as "major.minor" (e.g. "1.0"). |
geometry | object | Physical geometry — words, grids, and site positions. |
buses | object | Transport bus definitions (site buses and word buses). |
words_with_site_buses | integer[] | Word IDs with intra-word site transport capability. |
sites_with_word_buses | integer[] | Site indices that serve as landing pads for inter-word transport. |
zones | Zone[] | Logical groupings of words for execution phases. |
entangling_zones | integer[] | Zone IDs where CZ gates can be performed. |
measurement_mode_zones | integer[] | Zone IDs that support measurement. |
paths | TransportPath[] | (optional) AOD transport paths between locations. |
feed_forward | bool | (optional, default false) Whether the device supports mid-circuit measurement with classical feedback. |
atom_reloading | bool | (optional, default false) Whether the device supports reloading atoms after initial fill. |
Geometry
The geometry describes the physical layout of the device: how many words exist, how many sites each word contains, and where those sites are in 2D space.
"geometry": {
"sites_per_word": 10,
"words": [
{
"positions": {
"x_start": 1.0,
"y_start": 2.5,
"x_spacing": [2.0, 2.0, 2.0, 2.0],
"y_spacing": [2.5]
},
"site_indices": [[0, 0], [1, 0], [2, 0], ...],
"has_cz": [[0, 5], [0, 6], ...] // optional
}
]
}
Word
A word is an independent register of atom trapping sites arranged on a 2D grid. It is the fundamental unit of the device topology.
A word's ID is its index in the geometry.words array (e.g., the first word is word 0).
| Field | Type | Description |
|---|---|---|
positions | Grid | The 2D coordinate system for this word. |
site_indices | [x_idx, y_idx][] | Site positions as index pairs into the grid's x and y coordinate arrays. |
has_cz | [word_id, site_id][] | (optional) CZ entanglement partner for each site. has_cz[i] is the site that site i entangles with. |
Grid
A grid defines the physical coordinate axes for a word using a start position and spacing values. Positions are typically in micrometers (µm).
| Field | Type | Description |
|---|---|---|
x_start | float | X-coordinate of the first grid point. |
y_start | float | Y-coordinate of the first grid point. |
x_spacing | float[] | Spacing between consecutive x-coordinates. The number of x grid points is len(x_spacing) + 1. |
y_spacing | float[] | Spacing between consecutive y-coordinates. The number of y grid points is len(y_spacing) + 1. |
The x-coordinates are computed as [x_start, x_start + x_spacing[0], x_start + x_spacing[0] + x_spacing[1], ...] (cumulative sum of spacings from the start). Same for y. Sites reference grid positions by index: site [2, 1] is located at the 3rd x-coordinate and 2nd y-coordinate.
All words must have the same grid shape — i.e., the same number of x and y grid points (same x_spacing and y_spacing lengths). The actual coordinate values may differ (words can be at different physical locations), but the grid dimensions must be consistent.
Buses
Buses are the physical transport channels that move atoms. There are two kinds:
Site Bus
A site bus moves atoms between sites within the same word. It defines a paired mapping where src[i] moves to dst[i].
{ "src": [0, 1, 2, 3, 4], "dst": [5, 6, 7, 8, 9] }
This means atom at site 0 moves to site 5, atom at site 1 moves to site 6, and so on — all in a single transport operation. The src and dst arrays must be the same length and must not overlap (no site can be both a source and a destination in the same bus). A site bus's ID is its index in the buses.site_buses array.
Word Bus
A word bus moves atoms between sites across different words. The src and dst arrays contain word IDs (not site indices). A word bus's ID is its index in the buses.word_buses array.
{ "src": [0], "dst": [1] }
The specific sites involved in inter-word transport are those listed in sites_with_word_buses — these are the "landing pad" positions within each word.
Supporting Fields
| Field | Description |
|---|---|
words_with_site_buses | Which words have site bus hardware. Only these words can execute intra-word site moves. |
sites_with_word_buses | Which site indices serve as landing pads for word-bus moves. These positions within each word are where atoms arrive and depart during inter-word transport. |
Zones
A zone groups words into logical regions for different execution phases (entangling, measurement, etc.).
"zones": [
{ "words": [0, 1, 2] }
]
A zone's ID is its index in the zones array (e.g., the first zone is zone 0).
Zone 0 is special — it must contain every word in the geometry. This ensures there is always a "global" zone that covers the entire device.
Entangling Zones
entangling_zones lists zone IDs where CZ (entangling) gates can be performed. Words in these zones must have has_cz defined to specify entanglement partners.
Measurement Mode Zones
measurement_mode_zones lists zone IDs that support measurement operations. If non-empty, the first entry must be zone 0.
Paths (Optional)
AOD (Acousto-Optic Deflector) transport paths. Each path identifies a transport lane and provides a sequence of [x, y] waypoints defining the physical trajectory atoms follow during transport.
The lane is identified by its encoded LaneAddr, serialized as a hex string. See Address Encoding for the LaneAddr bit layout.
"paths": [
{
"lane": "0xC000000000000000", // encoded LaneAddr (hex, 16-digit)
"waypoints": [[1.0, 12.5], [1.0, 7.5], [1.0, 2.5]] // physical trajectory
}
]
Each TransportPath entry has:
| Field | Type | Description |
|---|---|---|
lane | string | Encoded LaneAddr as a "0x..." hex string. |
waypoints | [x, y][] | Sequence of physical coordinate waypoints. |
To decode the lane hex string, parse it as a 64-bit unsigned integer. The low 32 bits (data0) contain [word_id:16][site_id:16] and the high 32 bits (data1) contain [dir:1][mt:1][pad:14][bus_id:16]. For example, "0xC000000000000000" decodes to direction=Backward, move_type=WordBus, word=0, site=0, bus=0. In the lane address convention, word_id always encodes the forward-direction source word for that lane; in this specific example the bus mapping is src=[0], dst=[1], so direction=Backward means the move goes from word 1 back to word 0.
This field is omitted from the JSON when not needed.
Capability Flags (Optional)
Two boolean flags describe device capabilities that affect bytecode validation:
| Field | Default | Description |
|---|---|---|
feed_forward | false | Mid-circuit measurement with classical feedback. When false, at most one measure instruction is allowed per program. |
atom_reloading | false | Atom reloading after initial fill. When false, no fill instruction is allowed (only initial_fill). |
Both fields are optional in the JSON — existing arch spec files that omit them default to false, which is the most restrictive setting.
{
"feed_forward": true,
"atom_reloading": false
}
Validation Rules
The ArchSpec::validate() method checks all structural rules in a single pass, collecting every error rather than failing fast. The following rules are enforced:
Zone Rules
| Rule | Error |
|---|---|
| Zone 0 must include every word ID in the geometry | Zone0MissingWords |
measurement_mode_zones must not be empty | MeasurementModeZonesEmpty |
measurement_mode_zones[0] must be zone 0 | MeasurementModeFirstNotZone0 |
Every ID in entangling_zones must reference a defined zone | InvalidEntanglingZone |
Every ID in measurement_mode_zones must reference a defined zone | InvalidMeasurementModeZone |
Word / Site Rules
| Rule | Error |
|---|---|
Every word must have exactly sites_per_word site_indices | WrongSiteCount |
If present, has_cz must have exactly sites_per_word entries | WrongCzPairsCount |
| All words must have the same grid shape (same number of x and y positions) | InconsistentGridShape |
| Grid coordinates must be finite (no NaN or Inf) | NonFiniteGridValue |
Site x_idx must be < number of x grid points (len(x_spacing) + 1) | SiteXIndexOutOfRange |
Site y_idx must be < number of y grid points (len(y_spacing) + 1) | SiteYIndexOutOfRange |
Site Bus Rules
| Rule | Error |
|---|---|
src.length must equal dst.length | SiteBusLengthMismatch |
src and dst must be disjoint (no shared site indices) | SiteBusSrcDstOverlap |
All site indices in src and dst must be < sites_per_word | SiteBusIndexOutOfRange |
Word Bus Rules
| Rule | Error |
|---|---|
src.length must equal dst.length | WordBusLengthMismatch |
All word IDs in src and dst must exist in geometry.words | WordBusInvalidWordId |
Cross-Reference Rules
| Rule | Error |
|---|---|
Every ID in words_with_site_buses must be a valid word ID | InvalidWordWithSiteBus |
Every index in sites_with_word_buses must be < sites_per_word | InvalidSiteWithWordBus |
Path Rules
| Rule | Error |
|---|---|
Every path's lane must decode to a valid LaneAddr (valid bus ID, word ID, site ID) | InvalidPathLane |
| Every path must have at least 2 waypoints | PathTooFewWaypoints |
| The first waypoint must match the source position of the lane, and the last waypoint must match the destination position | PathEndpointMismatch |
| Waypoint coordinates must be finite (no NaN or Inf) | NonFiniteWaypoint |
Capability Rules (Bytecode Validation)
These rules are checked during bytecode validation when an ArchSpec is provided:
| Rule | Error |
|---|---|
If feed_forward = false, at most one measure instruction is allowed | FeedForwardNotSupported |
If atom_reloading = false, no fill instruction is allowed | AtomReloadingNotSupported |
Address Encoding
At the bytecode level, locations and lanes are encoded as bit-packed integers with 16-bit address fields. Each address type is packed into instruction data words (u32):
| Type | Width | Layout | Description |
|---|---|---|---|
LocationAddr | 32 bits (1 × u32) | data0: [word_id:16][site_id:16] | Identifies a specific site within a word. |
LaneAddr | 64 bits (2 × u32) | data0: [word_id:16][site_id:16], data1: [dir:1][mt:1][pad:14][bus_id:16] | Identifies a transport lane (direction + move type + site + bus). |
ZoneAddr | 32 bits (1 × u32) | data0: [pad:16][zone_id:16] | Identifies a zone. |
These packed addresses are used in 16-byte bytecode instructions (opcode + 3 data words) and are validated against the arch spec during program validation. In JSON and Python, LaneAddr is represented as a 64-bit integer (16-digit hex string in JSON, u64 in Python).
Examples
Minimal spec with one word and one site bus:
{
"version": "1.0",
"geometry": {
"sites_per_word": 5,
"words": [
{
"positions": {
"x_start": 1.0,
"y_start": 2.0,
"x_spacing": [2.0, 2.0, 2.0, 2.0],
"y_spacing": []
},
"site_indices": [[0, 0], [1, 0], [2, 0], [3, 0], [4, 0]]
}
]
},
"buses": {
"site_buses": [
{ "src": [0, 1], "dst": [3, 4] }
],
"word_buses": []
},
"words_with_site_buses": [0],
"sites_with_word_buses": [],
"zones": [
{ "words": [0] }
],
"entangling_zones": [],
"measurement_mode_zones": [0]
}
A fuller example with multiple words, CZ pairs, and word buses is available at examples/arch/full.json.
Instruction Quick Reference
A compact summary of all 24 bytecode instructions. See the Instruction Set for full encoding details.
Cpu (0x00)
Stack manipulation, constants, and control flow (FLAIR-aligned).
| Instruction | Opcode | Stack Effect | Description |
|---|---|---|---|
const_int | 0x0200 | ( -- int) | Push 64-bit integer constant |
const_float | 0x0300 | ( -- float) | Push 64-bit float constant |
dup | 0x0400 | (a -- a a) | Duplicate top of stack |
pop | 0x0500 | (a -- ) | Discard top of stack |
swap | 0x0600 | (a b -- b a) | Swap top two elements |
return | 0x6400 | ( -- ) | Return from program |
halt | 0xFF00 | ( -- ) | Halt execution |
LaneConstants (0x0F)
Address constant instructions.
| Instruction | Opcode | Stack Effect | Description |
|---|---|---|---|
const_loc | 0x000F | ( -- loc) | Push location address |
const_lane | 0x010F | ( -- lane) | Push lane address |
const_zone | 0x020F | ( -- zone) | Push zone address |
AtomArrangement (0x10)
Atom filling and transport.
| Instruction | Opcode | Stack Effect | Description |
|---|---|---|---|
initial_fill | 0x0010 | (loc₁..locₙ -- ) | Initial atom loading |
fill | 0x0110 | (loc₁..locₙ -- ) | Atom refill |
move | 0x0210 | (lane₁..laneₙ -- ) | Atom transport along lanes |
QuantumGate (0x11)
Single- and multi-qubit gate operations.
| Instruction | Opcode | Stack Effect | Description |
|---|---|---|---|
local_r | 0x0011 | (loc₁..locₙ θ φ -- ) | Local R rotation |
local_rz | 0x0111 | (loc₁..locₙ θ -- ) | Local Rz rotation |
global_r | 0x0211 | (θ φ -- ) | Global R rotation |
global_rz | 0x0311 | (θ -- ) | Global Rz rotation |
cz | 0x0411 | (zone -- ) | Controlled-Z gate on zone |
Measurement (0x12)
Qubit measurement.
| Instruction | Opcode | Stack Effect | Description |
|---|---|---|---|
measure | 0x0012 | (zone₁..zoneₙ -- future₁..futureₙ) | Initiate measurement |
await_measure | 0x0112 | (future -- array_ref) | Wait for measurement result |
Array (0x13)
Array construction and indexing.
| Instruction | Opcode | Stack Effect | Description |
|---|---|---|---|
new_array | 0x0013 | (elem₁..elemₙ -- array_ref) | Construct array from stack |
get_item | 0x0113 | (array_ref idx₁..idxₙ -- value) | Index into array |
DetectorObservable (0x14)
Detector and observable setup.
| Instruction | Opcode | Stack Effect | Description |
|---|---|---|---|
set_detector | 0x0014 | (array_ref -- detector_ref) | Set detector |
set_observable | 0x0114 | (array_ref -- observable_ref) | Set observable |
Bloqade Lanes Bytecode Instruction Specification
This document specifies the bytecode instruction set used by Bloqade Lanes to describe atom shuttling programs for neutral atom quantum processors. A bytecode program is a sequence of fixed-width instructions that drive the full lifecycle of a computation: loading atoms into an optical lattice, shuttling them between sites using AOD (Acousto-Optic Deflector) transport, applying quantum gates, and reading out measurement results.
The instruction set is organized around the physical structure of the hardware. Atoms occupy sites within words (rows of trapping positions in the lattice). Buses define the AOD transport channels that move atoms between sites (site buses) or between words (word buses). A lane is a single atom trajectory along a bus — one source site to one destination site. A zone groups words that share a global entangling interaction (e.g. a Rydberg pulse) or define locations where atoms are measured. These concepts map directly to the address types used in the bytecode: LocationAddr (word, site), LaneAddr (word, site, bus, direction), and ZoneAddr (zone).
Programs execute on a stack machine. Address constants and numeric parameters are pushed onto the stack, then consumed by operation instructions (fills, moves, gates, measurements). The bytecode is designed to be validated offline against an architecture specification (ArchSpec) that captures the geometry, bus topology, and zone layout of a specific device.
Instruction Format
Every instruction is a fixed 16 bytes: a 32-bit opcode word followed by three 32-bit data words, all little-endian.
┌──────────────┬──────────────┬──────────────┬──────────────┐
│ opcode (u32) │ data0 (u32) │ data1 (u32) │ data2 (u32) │
├──────────────┼──────────────┼──────────────┼──────────────┤
│ bytes 0–3 │ bytes 4–7 │ bytes 8–11 │ bytes 12–15 │
└──────────────┴──────────────┴──────────────┴──────────────┘
Instructions that take no operands ignore the data words (should be zero). Instructions with operands encode them in the data words as described per-instruction below.
Opcode Packing
The opcode word is packed as a 1-byte instruction code and a 1-byte device code in the low 16 bits of the u32. The upper 16 bits are unused (must be zero). The device code occupies the least significant byte.
┌──────────────┬──────────────────┬──────────────────┐
│ unused │ instruction code │ device code │
│ (16 bits) │ (8 bits) │ (8 bits) │
└──────────────┴──────────────────┴──────────────────┘
bits 31–16 bits 15–8 bits 7–0
Full opcode = (instruction_code << 8) | device_code.
In little-endian memory layout:
byte[0] = device_code (bits 7–0)
byte[1] = instruction_code (bits 15–8)
byte[2] = 0x00 (unused)
byte[3] = 0x00 (unused)
Instruction codes can overlap across different devices — the device code byte disambiguates.
Device Codes
| Device Code | Name | Description |
|---|---|---|
0x00 | Cpu | Stack manipulation, constants, control flow (FLAIR-aligned) |
0x0F | LaneConstants | Lane-specific constant instructions |
0x10 | AtomArrangement | Atom filling and movement |
0x11 | QuantumGate | Single- and multi-qubit gate operations |
0x12 | Measurement | Qubit measurement |
0x13 | Array | Array construction and indexing |
0x14 | DetectorObservable | Detector and observable setup |
Device codes 0x01–0x0E are reserved for future FLAIR device types.
Address Encoding
All address field components are 16-bit, packed into the data words.
LocationAddr
Packed in a single data word (data0):
data0: [word_id:16][site_id:16]
bits 31–16 bits 15–0
Total: 32 bits (u32).
LaneAddr
Packed across two data words (data0 + data1):
data0: [word_id:16][site_id:16]
bits 31–16 bits 15–0
data1: [dir:1][mt:1][pad:14][bus_id:16]
bit 31 bit 30 29–16 bits 15–0
dir— direction: 0 = Forward, 1 = Backwardmt— move type: 0 = SiteBus, 1 = WordBus
Total: 64 bits across two u32 words. Note that data0 shares the same layout as LocationAddr.
Lane address convention
The word_id and site_id fields in a LaneAddr always encode the forward-direction source — the position where the atom starts in a forward move. The direction field does not change which position is encoded; it only controls which endpoint is treated as source vs destination when the lane is resolved.
Endpoint resolution always starts by resolving the forward direction:
- Look up the bus (site bus or word bus, selected by
move_typeandbus_id) - Find the index
iwherebus.src[i]matches the encodedsite_id(for site buses) orword_id(for word buses) - The forward source is
(word_id, site_id)as encoded; the forward destination is(word_id, bus.dst[i])for site buses or(bus.dst[i], site_id)for word buses - If
direction = Forward: return(fwd_source, fwd_destination) - If
direction = Backward: return(fwd_destination, fwd_source)— the endpoints are swapped
Example: Given a site bus with src=[0,1,2,3,4] dst=[5,6,7,8,9]:
| Lane | Encoded | Resolved src → dst |
|---|---|---|
site_id=0, dir=Forward | Forward source is site 0 | Site 0 → Site 5 |
site_id=0, dir=Backward | Forward source is still site 0 | Site 5 → Site 0 |
site_id=2, dir=Backward | Forward source is site 2 | Site 7 → Site 2 |
Note that a backward lane with site_id=0 means the atom moves from site 5 to site 0 — not that site 0 is the destination of a forward move.
Lane validation rules
The validator (check_lane) checks the following for each LaneAddr:
| Rule | Error condition |
|---|---|
| Bus must exist | bus_id out of range for the given move_type |
word_id in range | word_id >= num_words |
site_id in range | site_id >= sites_per_word |
| Bus membership | For site buses: word_id must be in words_with_site_buses. For word buses: site_id must be in sites_with_word_buses. |
| Valid forward source | For site buses: bus.resolve_forward(site_id) must succeed (i.e. site_id is in bus.src). For word buses: bus.resolve_forward(word_id) must succeed (i.e. word_id is in bus.src). |
Validation is always performed against the forward-direction source, regardless of the direction field.
ZoneAddr
Packed in a single data word (data0):
data0: [pad:16][zone_id:16]
bits 31–16 bits 15–0
Total: 32 bits (u32).
Instructions
Cpu (0x00) — FLAIR-aligned shared opcodes
These instruction codes are shared with the FLAIR VM/IR spec and use identical values.
const_int — Push integer constant
| Field | Value |
|---|---|
| Device Code | 0x00 |
| Instruction Code | 0x02 |
| Full Opcode | 0x0200 |
| data0 | i64 LE low 32 bits |
| data1 | i64 LE high 32 bits |
| data2 | unused |
| Stack | ( -- int) |
Pushes a signed 64-bit integer onto the stack. The value is stored as a little-endian i64 across data0 (low) and data1 (high).
const_float — Push float constant
| Field | Value |
|---|---|
| Device Code | 0x00 |
| Instruction Code | 0x03 |
| Full Opcode | 0x0300 |
| data0 | f64 LE low 32 bits |
| data1 | f64 LE high 32 bits |
| data2 | unused |
| Stack | ( -- float) |
Pushes a 64-bit float onto the stack. The value is stored as a little-endian f64 across data0 (low) and data1 (high).
dup — Duplicate top of stack
| Field | Value |
|---|---|
| Device Code | 0x00 |
| Instruction Code | 0x04 |
| Full Opcode | 0x0400 |
| data0–2 | unused |
| Stack | (a -- a a) |
pop — Discard top of stack
| Field | Value |
|---|---|
| Device Code | 0x00 |
| Instruction Code | 0x05 |
| Full Opcode | 0x0500 |
| data0–2 | unused |
| Stack | (a -- ) |
swap — Swap top two stack elements
| Field | Value |
|---|---|
| Device Code | 0x00 |
| Instruction Code | 0x06 |
| Full Opcode | 0x0600 |
| data0–2 | unused |
| Stack | (a b -- b a) |
return — Return from program
| Field | Value |
|---|---|
| Device Code | 0x00 |
| Instruction Code | 0x64 |
| Full Opcode | 0x6400 |
| data0–2 | unused |
| Stack | ( -- ) |
halt — Halt execution
| Field | Value |
|---|---|
| Device Code | 0x00 |
| Instruction Code | 0xFF |
| Full Opcode | 0xFF00 |
| data0–2 | unused |
| Stack | ( -- ) |
LaneConstants (0x0F)
const_loc — Push location address
| Field | Value |
|---|---|
| Device Code | 0x0F |
| Instruction Code | 0x00 |
| Full Opcode | 0x000F |
| data0 | LocationAddr — [word_id:16][site_id:16] |
| data1 | unused |
| data2 | unused |
| Stack | ( -- loc) |
const_lane — Push lane address
| Field | Value |
|---|---|
| Device Code | 0x0F |
| Instruction Code | 0x01 |
| Full Opcode | 0x010F |
| data0 | [word_id:16][site_id:16] |
| data1 | [dir:1][mt:1][pad:14][bus_id:16] |
| data2 | unused |
| Stack | ( -- lane) |
const_zone — Push zone address
| Field | Value |
|---|---|
| Device Code | 0x0F |
| Instruction Code | 0x02 |
| Full Opcode | 0x020F |
| data0 | ZoneAddr — [pad:16][zone_id:16] |
| data1 | unused |
| data2 | unused |
| Stack | ( -- zone) |
AtomArrangement (0x10)
initial_fill — Initial atom loading
| Field | Value |
|---|---|
| Device Code | 0x10 |
| Instruction Code | 0x00 |
| Full Opcode | 0x0010 |
| data0 | u32 LE arity |
| data1 | unused |
| data2 | unused |
| Stack | (loc₁ loc₂ … locₙ -- ) |
Pops n location addresses and performs the initial atom fill at those sites.
fill — Atom refill
| Field | Value |
|---|---|
| Device Code | 0x10 |
| Instruction Code | 0x01 |
| Full Opcode | 0x0110 |
| data0 | u32 LE arity |
| data1 | unused |
| data2 | unused |
| Stack | (loc₁ loc₂ … locₙ -- ) |
Pops n location addresses and refills atoms at those sites.
move — Atom transport
| Field | Value |
|---|---|
| Device Code | 0x10 |
| Instruction Code | 0x02 |
| Full Opcode | 0x0210 |
| data0 | u32 LE arity |
| data1 | unused |
| data2 | unused |
| Stack | (lane₁ lane₂ … laneₙ -- ) |
Pops n lane addresses and performs atom moves along those lanes. All lanes in a single move instruction are executed simultaneously as one AOD transport operation.
Lane group validation
When an ArchSpec is provided, the validator checks the group of lanes as a whole — not just each lane individually. These constraints reflect the physical limitations of a single AOD (Acousto-Optic Deflector). Each move instruction corresponds to one AOD operation:
Consistency — all lanes in the group must share the same move_type, bus_id, and direction. A single AOD operation cannot mix site-bus and word-bus moves, use different buses, or move atoms in different directions simultaneously.
Bus membership — for site-bus moves, every lane's word_id must be in words_with_site_buses. For word-bus moves, every lane's site_id must be in sites_with_word_buses.
Grid constraint — the physical positions of the lane sources must form a complete grid (Cartesian product of unique X and Y coordinates). An AOD addresses rows and columns independently, so it cannot select an arbitrary subset of positions — it must address every intersection of the selected rows and columns.
For example, if a move group contains lanes at positions (0,0), (0,1), (1,0), and (1,1), this is a valid 2x2 grid. But (0,0), (0,1), (1,0) alone is invalid — the AOD would also address (1,1), so the group must include it.
| Check | Error |
|---|---|
All lanes share move_type, bus_id, direction | Inconsistent |
Site-bus lane word_id in words_with_site_buses | WordNotInSiteBusList |
Word-bus lane site_id in sites_with_word_buses | SiteNotInWordBusList |
| Lane positions form a complete grid | AODConstraintViolation |
QuantumGate (0x11)
local_r — Local R rotation
| Field | Value |
|---|---|
| Device Code | 0x11 |
| Instruction Code | 0x00 |
| Full Opcode | 0x0011 |
| data0 | u32 LE arity |
| data1 | unused |
| data2 | unused |
| Stack | (loc₁ loc₂ … locₙ θ φ -- ) |
Pops 2 float parameters (φ = axis angle, θ = rotation angle) then n location addresses, and applies a local R rotation. The call convention matches the SSA IR: local_r(%φ, %θ, %loc₁, …) — first argument (φ) is pushed last and popped first.
local_rz — Local Rz rotation
| Field | Value |
|---|---|
| Device Code | 0x11 |
| Instruction Code | 0x01 |
| Full Opcode | 0x0111 |
| data0 | u32 LE arity |
| data1 | unused |
| data2 | unused |
| Stack | (loc₁ loc₂ … locₙ θ -- ) |
Pops 1 float parameter (θ = rotation angle) then n location addresses, and applies a local Rz rotation. The call convention matches the SSA IR: local_rz(%θ, %loc₁, …).
global_r — Global R rotation
| Field | Value |
|---|---|
| Device Code | 0x11 |
| Instruction Code | 0x02 |
| Full Opcode | 0x0211 |
| data0–2 | unused |
| Stack | (θ φ -- ) |
Pops 2 float parameters (φ = axis angle, θ = rotation angle), applies a global R rotation. The call convention matches the SSA IR: global_r(%φ, %θ).
global_rz — Global Rz rotation
| Field | Value |
|---|---|
| Device Code | 0x11 |
| Instruction Code | 0x03 |
| Full Opcode | 0x0311 |
| data0–2 | unused |
| Stack | (θ -- ) |
Pops 1 float parameter (θ = rotation angle), applies a global Rz rotation. Since there is only one parameter, it is both pushed last and popped first.
cz — Controlled-Z gate
| Field | Value |
|---|---|
| Device Code | 0x11 |
| Instruction Code | 0x04 |
| Full Opcode | 0x0411 |
| data0–2 | unused |
| Stack | (zone -- ) |
Pops a zone address and applies a CZ gate across the zone.
Measurement (0x12)
measure — Initiate measurement
| Field | Value |
|---|---|
| Device Code | 0x12 |
| Instruction Code | 0x00 |
| Full Opcode | 0x0012 |
| data0 | u32 LE arity |
| data1 | unused |
| data2 | unused |
| Stack | (zone₁ zone₂ … zoneₙ -- future₁ future₂ … futureₙ) |
Pops n zone addresses and pushes n measure futures.
await_measure — Wait for measurement result
| Field | Value |
|---|---|
| Device Code | 0x12 |
| Instruction Code | 0x01 |
| Full Opcode | 0x0112 |
| data0–2 | unused |
| Stack | (future -- array_ref) |
Pops a measure future and pushes an array reference containing the measurement results.
Array (0x13)
new_array — Construct array from stack
| Field | Value |
|---|---|
| Device Code | 0x13 |
| Instruction Code | 0x00 |
| Full Opcode | 0x0013 |
| data0 | [type_tag:8][pad:8][dim0:16] |
| data1 | [pad:16][dim1:16] |
| data2 | unused |
| Stack | (elem₁ elem₂ … elemₙ -- array_ref) |
Constructs an array of dim0 × dim1 elements with element type type_tag. If dim1 is 0, the array is 1-dimensional with dim0 elements.
get_item — Index into array
| Field | Value |
|---|---|
| Device Code | 0x13 |
| Instruction Code | 0x01 |
| Full Opcode | 0x0113 |
| data0 | u16 LE ndims (upper 16 bits unused) |
| data1 | unused |
| data2 | unused |
| Stack | (array_ref idx₁ … idxₙ -- value) |
Pops ndims index values then the array reference, and pushes the indexed element.
DetectorObservable (0x14)
set_detector — Set detector
| Field | Value |
|---|---|
| Device Code | 0x14 |
| Instruction Code | 0x00 |
| Full Opcode | 0x0014 |
| data0–2 | unused |
| Stack | (array_ref -- detector_ref) |
Pops an array reference and pushes a detector reference.
set_observable — Set observable
| Field | Value |
|---|---|
| Device Code | 0x14 |
| Instruction Code | 0x01 |
| Full Opcode | 0x0114 |
| data0–2 | unused |
| Stack | (array_ref -- observable_ref) |
Pops an array reference and pushes an observable reference.
Reserved Opcode Ranges
| Range | Owner |
|---|---|
Device 0x00, inst codes 0x00–0x8F | Reserved for FLAIR. This project uses only 0x02–0x06, 0x64, 0xFF. |
Device codes 0x01–0x0E | Reserved for future FLAIR device types |
Device codes 0x0F–0xFF | Project-specific (currently 0x0F–0x14 allocated) |
Known FLAIR allocations (device 0x00)
| Instruction Code | Purpose |
|---|---|
0x01 | const.bool |
0x10–0x17 | Arithmetic (arith.add_int, arith.add_float, etc.) |
0x20–0x23 | Comparison (cmp.gt_int, cmp.eq_float, etc.) |
0x28–0x2A | Boolean (bool.not, bool.and, bool.or) |
0x30–0x33 | Waveform (waveform.poly4, waveform.delay, etc.) |
0x40–0x43 | Channel (channel.emit, channel.play, etc.) |
0x50–0x52 | Peer messaging |
0x60–0x63 | Control flow (cf.jump, cf.branch, cf.call) |
0x80 | debug.trace |
CLI Reference
The bloqade-bytecode CLI assembles, disassembles, and validates lane-move bytecode programs.
bloqade-bytecode <COMMAND>
Installation
Via the Python package (recommended)
The CLI is bundled in the bloqade-lanes Python wheel and placed on your PATH automatically:
pip install bloqade-lanes
After installation, bloqade-bytecode is available as a command.
Note: The CLI is only included in pre-built platform wheels, not in the source distribution (sdist). If no wheel is available for your platform, use the cargo install method instead.
From source (cargo)
Install the CLI directly from the repository using cargo:
cargo install --path crates/bloqade-lanes-bytecode-cli
This compiles the binary and places it in ~/.cargo/bin/, which is typically on your PATH. No Python installation is required.
Alternatively, build without installing:
just build-cli # Output: target/release/bloqade-bytecode
The binary can be run in place (./target/release/bloqade-bytecode) or copied to a directory on your PATH.
Development install
For local development, this builds the CLI, stages it into the Python package, and installs everything:
just develop
Testing
just test-rust # Run Rust tests (core + CLI crates)
just cli-smoke-test # CLI bytecode validation tests
Commands
assemble
Assemble a text program (.sst) into binary format (.bin).
bloqade-bytecode assemble <INPUT> -o <OUTPUT>
| Argument | Description |
|---|---|
<INPUT> | Input text file (.sst) |
-o, --output <OUTPUT> | Output binary file (required) |
Example:
bloqade-bytecode assemble prog.sst -o prog.bin
# assembled 12 instructions -> prog.bin
disassemble
Disassemble a binary program (.bin) into human-readable text format.
bloqade-bytecode disassemble <INPUT> [-o <OUTPUT>]
| Argument | Description |
|---|---|
<INPUT> | Input binary file (.bin) |
-o, --output <OUTPUT> | Output text file (omit to print to stdout) |
Examples:
# Print to stdout
bloqade-bytecode disassemble prog.bin
# Write to file
bloqade-bytecode disassemble prog.bin -o prog.sst
# disassembled 12 instructions -> prog.sst
The conversion is semantically lossless: assembling and then disassembling a program preserves its instructions, and the disassembler's canonical text output round-trips through text → binary → text.
validate
Validate a program for correctness. Accepts both text (.sst) and binary formats — the format is auto-detected from the file extension.
bloqade-bytecode validate <INPUT> [--arch <ARCH>] [--simulate-stack]
| Argument | Description |
|---|---|
<INPUT> | Input file (.sst = text, otherwise binary) |
--arch <ARCH> | ArchSpec JSON file for address validation |
--simulate-stack | Run stack type simulation |
Validation levels:
| Level | When | What it checks |
|---|---|---|
| Structural | Always | Arity bounds, initial_fill ordering |
| Address | --arch provided | Location, lane, zone, and bus validity against the architecture |
| Stack simulation | --simulate-stack | Type safety, stack balance |
Examples:
# Structural validation only
bloqade-bytecode validate prog.sst
# valid (12 instructions)
# Full validation with architecture and stack simulation
bloqade-bytecode validate prog.sst --arch gemini-logical.json --simulate-stack
# valid (12 instructions)
# Validation failure
bloqade-bytecode validate bad.sst --arch gemini-logical.json
# [0] initial_fill: invalid location address ...
# error: 1 validation error(s)
arch
Pretty-print an architecture specification.
bloqade-bytecode arch <INPUT>
| Argument | Description |
|---|---|
<INPUT> | ArchSpec JSON file |
Example output:
ArchSpec v1.0
Geometry: 2 word(s), 10 sites/word
Word 0: 2x10 grid, 10 sites
x: start=0, spacing=[5.0]
y: start=0, spacing=[1.0, 1.0, ...]
site_indices: (0,0) (0,1) (0,2) ...
has_cz: (1,0) (1,1) (1,2) ...
Buses: 1 site bus(es), 1 word bus(es)
Site bus 0: src=[0, 1, 2, 3, 4] dst=[5, 6, 7, 8, 9]
Word bus 0: src=[0] dst=[1]
words_with_site_buses: [0, 1]
sites_with_word_buses: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Zones: 1 zone(s)
Zone 0: words=[0, 1]
entangling_zones: [0]
measurement_mode_zones: [0]
Paths: 1 path(s)
0xC000000000000005: 3 waypoint(s)
[1.0, 15.0]
[1.0, 10.0]
[1.0, 5.0]
The Paths section is only shown when the ArchSpec includes path data. Each path is identified by its 64-bit encoded lane address (in hex) and lists the AOD waypoints (physical coordinates) that define the transport trajectory.
arch validate
Validate an ArchSpec JSON file for internal consistency.
bloqade-bytecode arch validate <INPUT>
| Argument | Description |
|---|---|
<INPUT> | ArchSpec JSON file |
Example:
bloqade-bytecode arch validate gemini-logical.json
# arch spec is valid: gemini-logical.json
File Formats
| Extension | Format | Description |
|---|---|---|
.sst | Text | Human-readable bytecode (one instruction per line, ; comments) |
.bin | Binary | Compact binary encoding (BLQD magic header, 16 bytes per instruction) |
.json | JSON | Architecture specification |
See also the Instruction Quick Reference for a compact summary of all 24 instructions, or the full Instruction Set for encoding details.