NXLang Documentation
NXLang is a Datalog-style language that compiles to NXPU hardware register writes. Write facts and rules in a human-readable syntax; the compiler handles symbol resolution, variable encoding, and hardware targeting.
Installation
git clone https://github.com/dyber-pqc/NXPU.git cd NXPU pip install -r requirements.txt # Verify installation python nxc.py version # NXLang Compiler (nxc) v0.3.0
Quick Start
Create a file hello.nx:
fact: parent(tom, bob). fact: parent(bob, ann). rule: grandparent(X, Z) :- parent(X, Y, _), parent(Y, Z, _). # query: grandparent.
Compile and run:
# Compile to Tcl (for FPGA) python nxc.py hw-compile hello.nx --format tcl # Compile to binary python nxc.py hw-compile hello.nx --format nxb # Run in simulation python nxc.py repl --transport sim nxpu> fact parent(tom, bob) nxpu> fact parent(bob, ann) nxpu> rule grandparent(X, Z) :- parent(X, Y, _), parent(Y, Z, _) nxpu> derive DeriveResult(count=1, cycles=19, time_us=0.19) nxpu> query grandparent QueryResult(pred=grandparent, matches=1)
Facts
Facts are ground truth assertions stored in the NXPU's Content-Addressable Memory. Each fact has a predicate name and up to 3 arguments.
fact: vulnerable(CVE_2024_1234, openssl, v3_0_1). fact: patient_takes(john, warfarin). fact: internet_facing(web_01).
Internally, each fact is encoded as a 56-bit CAM entry:
| Field | Bits | Description |
|---|---|---|
| Predicate ID | [55:48] | 8-bit, auto-assigned by compiler |
| Argument 2 | [47:32] | 16-bit constant ID |
| Argument 1 | [31:16] | 16-bit constant ID |
| Argument 0 | [15:0] | 16-bit constant ID |
Rules
Rules express logical inference. The head is derived when all body atoms are satisfied simultaneously with consistent variable bindings.
rule: at_risk(Server) :-
runs_service(Server, Software, Version),
vulnerable(_, Software, Version),
internet_facing(Server, _, _).
Rules support up to 4 body atoms and 8 simultaneous variables. The hardware performs depth-first search with backtracking when partial bindings fail.
Software must bind to the same value in both runs_service and vulnerable.
Queries
query: at_risk. # Wildcard: all at_risk facts query: patient_takes(john). # Filtered: john's medications
Variables & Constants
| Syntax | Type | Example |
|---|---|---|
| Uppercase | Variable (binds during unification) | Server, Drug, X |
| Lowercase / quoted | Constant (exact match) | warfarin, web_01 |
Underscore _ | Don't-care (matches anything) | vulnerable(_, S, V) |
Type System
The compiler performs type checking including arity consistency (same predicate always has same number of args), head-variable-in-body verification (head variables must appear in at least one body atom), and hardware constraint validation (max 4 body atoms, max 8 variables, max 255 predicates, max 65535 constants).
Compiler Pipeline
NXLang Source (.nx)
|
v
Lexer (40+ token types, line/col errors)
|
v
Parser (recursive descent, 15 AST node types)
|
v
Type Checker (arity, variable, constraint validation)
|
v
NXIR (SSA-form IR with engine tags)
|
v
Optimizer (dead code elimination, parallel regions)
|
v
FPGA Backend
|-- Symbol Table (names -> 8-bit/16-bit IDs)
|-- Variable Encoder (rules -> packed register format)
|-- Hardware Encoder (register write sequences)
|
v
Output: .tcl | .nxb | .json | .h
CLI Reference
| Command | Description |
|---|---|
nxc hw-compile <file> -f tcl | Compile to Vivado Tcl script |
nxc hw-compile <file> -f nxb | Compile to NXB binary |
nxc hw-compile <file> -f json | Compile to JSON |
nxc hw-compile <file> -f c | Compile to C header |
nxc deploy <file> -t jtag | Upload and run on FPGA |
nxc repl -t sim | Interactive REPL (simulation) |
nxc repl -t jtag | Interactive REPL (hardware) |
nxc query <pred> | Query running hardware |
nxc status | Show hardware status |
nxc compile <file> | Compile to simulation code |
nxc check <file> | Type-check only |
nxc ir <file> | Show NXIR intermediate representation |
Output Formats
NXB Binary
Compiled, portable binary. 16-byte header + register writes + symbol table + CRC32.
Offset Size Field 0x00 4 Magic: "NXB\0" (0x4E584200) 0x04 2 Version: 0x0001 0x06 2 Flags 0x08 4 Write count 0x0C 4 Symbol table offset 0x10 N*8 Register writes (addr + data) ... var Symbol table (JSON) ... 4 CRC32
Python SDK
import nxpu # Connect to hardware or simulation session = nxpu.connect("sim") # or "jtag" # Add facts session.add_fact("parent", "tom", "bob") session.add_fact("parent", "bob", "ann") # Add and run a rule session.add_rule("grandparent(X,Z) :- parent(X,Y,_), parent(Y,Z,_)") result = session.derive() # DeriveResult(count=1, cycles=19, time_us=0.19) # Query q = session.query("grandparent") # QueryResult(pred=grandparent, matches=1) # Load a .nx file session.load_file("pharma_safety.nx") # Compile to binary nxb = session.compile_to_nxb(facts, rules)
Session API
| Method | Description |
|---|---|
add_fact(pred, *args) | Add a fact to hardware CAM |
add_fact_text(text) | Add fact from string: "parent(tom, bob)" |
add_rule(text) | Register a rule for derivation |
derive(rule_text=None) | Run derivation, returns DeriveResult |
derive_all() | Run all registered rules |
query(pred, *args) | Query CAM, returns QueryResult |
load_file(path) | Compile and upload .nx file |
load_nxb(path) | Upload compiled .nxb binary |
status() | Returns HWStatus (entries, busy, etc.) |
scratch_test() | Verify hardware connectivity |
Transports
| Transport | Description | Usage |
|---|---|---|
sim | Software simulation (no hardware) | Development, testing |
jtag | SSH + Vivado JTAG-to-AXI | ZCU106 FPGA |
pcie | PCIe (future) | M.2 / ASIC |
uart | UART serial (future) | Embedded |
Register Map
42 registers accessed via AXI4-Lite (8-bit address space):
| Address | Name | Access | Description |
|---|---|---|---|
| 0x00 | CMD | W | 1=ADD_FACT, 3=QUERY, 4=DERIVE |
| 0x04 | STATUS | R | busy, entry_count, match_count |
| 0x08-0x0C | DATA | R/W | Entry bits [55:0] |
| 0x10-0x14 | MASK | R/W | Search mask |
| 0x24 | SCRATCH | R/W | Test register (init: 0x4E585055) |
| 0x28-0x44 | RULE_* | R/W | Rule configuration (8 registers) |
| 0x48 | DERIVED_COUNT | R | Facts derived by last DERIVE |
| 0x4C | CYCLE_COUNT | R | Clock cycles for last DERIVE |
| 0x50-0x5C | NM_* | R/W | Neural Mesh I/O |
| 0x60-0x64 | CF_* | R/W | Concept Formation |
| 0x68-0x7C | NLU_* | R/W | NLU Pipeline |
| 0x80-0xA0 | FB/MR/PC | R/W | Feedback, Meta-Rule, Pattern Comp. |
Variable Encoding
The most complex part of compilation. Each Datalog variable gets a 3-bit index (0-7). Indices are packed 9 bits per atom (3 args x 3 bits) into a 36-bit field split across VAR_IDX_LO (bits 31:0) and VAR_IDX_HI (bits 35:32).
var_idx_lo=0x05811688 for the security benchmark).