Skip to content

Compiler API Reference

Complete API reference for the SC-NeuroCore compilation pipeline. Covers the ODE-to-Verilog equation compiler, MLIR/CIRCT emitter, weight quantiser, adaptive precision, IR type checker, static analysis, and deployment orchestrator. This is the authoritative reference for all compiler-facing Python functions.


1. Mathematical Formalism

1.1 ODE Discretisation

The compiler transforms continuous ODEs to discrete fixed-point computations using the forward Euler method:

$$ x[n+1] = x[n] + \Delta t \cdot f(x[n], I[n]) $$

In Q$m$.$f$ format with shift-based division by $\tau$:

$$ x_{\text{next}} = x + \frac{I - (x - x_{\text{rest}})}{2^{\lceil\log_2 \tau\rceil}} $$

1.2 Fixed-Point Encoding

Parameters and states are encoded in Q$m$.$f$ signed format:

$$ Q(v) = \text{round}(v \cdot 2^f) $$

The range is $[-2^{m-1}, 2^{m-1} - 2^{-f}]$ with precision $2^{-f}$.

Format Total Bits Integer Fraction Range Precision
Q8.8 16 8 8 ±127 0.0039
Q16.16 32 16 16 ±32767 0.000015
Q12.20 32 12 20 ±2047 0.00000095

1.3 Guard Bit Computation

Guard bits prevent intermediate overflow during multiply-accumulate:

$$ G = \lceil \log_2(N_{\text{terms}}) \rceil $$

where $N_{\text{terms}}$ is the maximum number of additions in the datapath. The data width is extended to $W + G$ bits for intermediates, then saturated back to $W$ bits for the final result.

1.4 Piecewise LUT Approximation

Transcendental functions ($\exp$, $\log$, $\tanh$, etc.) use 16-entry piecewise-constant lookup tables covering $[-8, +8)$:

$$ f_{\text{LUT}}(x) = \text{table}\left[\left\lfloor \frac{x + 8}{1} \right\rfloor\right] $$

Accuracy: ~1–2% over the useful range for neuron dynamics.


2. Architecture

2.1 Compilation Pipeline

flowchart TB
    subgraph Input
        A["ODE string<br/>'dv/dt = -(v-E_L)/tau + I/C'"]
    end
    subgraph Parse
        B["Python AST parser"]
        C["_VerilogExprEmitter"]
    end
    subgraph Emit
        D["Q-format parameters"]
        E["Multiply pipelines"]
        F["LUT for exp/log/tanh"]
        G["Saturating next-state"]
        H["Threshold + reset logic"]
    end
    subgraph Output
        I["Synthesizable Verilog"]
        J["Testbench"]
    end

    A --> B --> C
    C --> D & E & F & G & H
    D & E & F & G & H --> I
    I --> J

    style Input fill:#e1f5fe
    style Output fill:#e8f5e9

2.2 Module Dependency Graph

Text Only
sc_neurocore.compiler
├── equation_compiler   # ODE → Verilog
├── pipeline            # Yosys → nextpnr → bitstream
├── mlir_emitter        # MLIR/CIRCT backend
├── quantizer           # Float → Q-format
├── adaptive_precision  # Dynamic width switching
├── ir_type_checker     # Stochastic IR validation
├── static_analysis     # Guard bits, SVA, power
└── deployment          # Constraints, drivers, multi-target

3. CLI Interface

3.1 Main Compilation Command

Bash
sc-neurocore compile "dv/dt = -(v-E_L)/tau_m + I/C" \
    --threshold "v > -50" --reset "v = -65" \
    --params "E_L=-65,tau_m=10,C=1" --init "v=-65" \
    --target ice40 --testbench --synthesize -o build/

3.2 CLI Flags

Flag Default Description
--threshold None Spike condition (e.g. "v > -50")
--reset None Reset expression (e.g. "v = -65; w = 0")
--params None Comma-separated key=val pairs
--init None Initial state key=val pairs
--target ice40 FPGA target (ice40, ecp5, artix7, zynq)
--module-name sc_equation_neuron Generated Verilog module name
--testbench off Generate simulation testbench
--synthesize off Run Yosys synthesis (requires Yosys in PATH)
-o / --output build Output directory

3.3 NIR Network Compilation

Bash
sc-neurocore compile-nir model.nir --target artix7 -o build/

3.4 Multi-Target Compilation

Bash
sc-neurocore compile "dv/dt = -(v)/tau + I" \
    --target artix7,ecp5,ice40 --compare -o build/

4. Python API

4.1 Equation Compiler

Python
from sc_neurocore.neurons.equation_builder import from_equations
from sc_neurocore.compiler.equation_compiler import compile_to_verilog

neuron = from_equations(
    "dv/dt = -(v - E_L)/tau_m + I/C",
    threshold="v > -50",
    reset="v = -65",
    params=dict(E_L=-65, tau_m=10, C=1),
    init=dict(v=-65),
)

verilog = compile_to_verilog(
    neuron,
    module_name="sc_lif",
    data_width=16,
    fraction=8,
)

Supported Functions

Category Functions
Transcendental exp, log, sqrt, tanh, sigmoid, sin, cos
Arithmetic abs, clip(x, lo, hi), max(a, b), min(a, b)
Polynomial x**2 through x**8
Operators +, -, *, / (by constant), unary -
Comparison >, >=, <, <=

4.2 MLIR Emitter

Python
from sc_neurocore.compiler import MLIREmitter, generate_mlir_bundle

emitter = MLIREmitter("sc_native_top")
lhs = emitter.emit_lfsr(8, 0x5A)
rhs = emitter.emit_lfsr(8, 0xC3)
emitter.emit_and(lhs, rhs)

bundle = generate_mlir_bundle(emitter, "build/mlir/sc_native_top")
print(bundle.mlir_path)
print(bundle.manifest_path)

The manifest records operation counts and whether firtool is available.

4.3 Weight Quantizer

Python
from sc_neurocore.compiler.quantizer import quantize_weights

weights = [0.5, -0.3, 1.2, 0.0]
q_weights = quantize_weights(
    weights,
    data_width=16,
    fraction=8,
    rounding="nearest",     # or "stochastic", "floor"
)
# Returns list of Q8.8 integers

Rounding Modes

Mode Description Use Case
nearest Round to nearest representable value Default
stochastic Probabilistic rounding Training
floor Round toward zero Conservative

4.4 Adaptive Precision

Python
from sc_neurocore.compiler.adaptive_precision import AdaptivePrecisionConfig

config = AdaptivePrecisionConfig(
    low_precision=8,        # LP mode (Q4.4)
    high_precision=16,      # HP mode (Q8.8)
    switch_threshold=0.1,   # Switch to HP when gradient > 0.1
    hysteresis=0.05,        # Stay in HP until gradient < 0.05
)

4.5 IR Type Checker

Python
from sc_neurocore.compiler.ir_type_checker import check_ir_types

errors = check_ir_types(graph)
if errors:
    for e in errors:
        print(f"Type error: {e}")

Signal types: BITSTREAM, RATE, SPIKE, FIXED, ANY.

4.6 Static Analysis

Python
from sc_neurocore.compiler.static_analysis import (
    prove_overflow_free,
    generate_sva,
    estimate_power,
)

# Guard bit computation
proof = prove_overflow_free(
    equations={"v": "-(v - E_L)/tau_m + I/C"},
    params=dict(E_L=-65, tau_m=10, C=1),
    data_width=16,
    fraction=8,
)
print(f"Safe: {proof.safe}, guard bits: {proof.guard_bits}")

# SVA assertion generation
sva = generate_sva(
    state_vars=["v"],
    module_name="sc_lif",
    data_width=16,
    fraction=8,
)

# Power estimation
pe = estimate_power(
    verilog,
    data_width=16,
    freq_mhz=200.0,
    process_nm=28,
)

# Use measured VCD switching activity when available
pe_vcd = estimate_power(
    verilog,
    activity_vcd="build/sc_lif.vcd",
    vcd_time_units_per_cycle=5,
    freq_mhz=200.0,
)

4.7 Deployment

Python
from sc_neurocore.compiler.deployment import (
    generate_constraints,
    generate_cocotb_testbench,
    generate_riscv_driver,
    generate_sby_script,
    compile_multi_target,
    format_comparison_table,
    estimate_resources,
)

# Timing constraints
xdc = generate_constraints("sc_lif", freq_mhz=200)

# Cocotb testbench
tb = generate_cocotb_testbench("sc_lif", data_width=16, fraction=8)

# RISC-V driver
driver = generate_riscv_driver(
    "sc_lif",
    params={"E_L": 16, "tau_m": 16},
    rtos="freertos",
)

# SymbiYosys formal
sby = generate_sby_script("sc_lif", mode="bmc", depth=20)

# Resource estimation
res = estimate_resources("sc_lif", verilog)
print(f"LUTs: {res.estimated_luts}, DSPs: {res.estimated_dsps}")

5. Pipeline Orchestration

5.1 Full Synthesis Flow

Python
from sc_neurocore.compiler.pipeline import run_synthesis_pipeline

result = run_synthesis_pipeline(
    verilog_path="build/sc_lif.v",
    target="ice40",
    freq_mhz=100,
    output_dir="build/",
)
print(f"LUTs: {result.lut_count}")
print(f"FFs: {result.ff_count}")
print(f"Fmax: {result.fmax_mhz:.1f} MHz")

5.2 Pipeline Stages

Stage Tool Input Output
Parse Python AST ODE string IR graph
Emit _VerilogExprEmitter IR graph Verilog RTL
Synthesis Yosys Verilog BLIF/JSON
P&R nextpnr BLIF Bitstream

5.3 MLIR/CIRCT Path

Python
from sc_neurocore.compiler import MLIREmitter, generate_mlir_bundle

emitter = MLIREmitter("sc_native_top")
# ... emit operations ...
bundle = generate_mlir_bundle(emitter, "build/mlir/sc_native_top")

The MLIR backend generates .mlir files and mlir_bundle_manifest.json. The manifest records operation counts and does not claim CIRCT lowering unless a downstream tool execution record is attached.


6. Data Types and Structures

6.1 CompilationResult

Python
@dataclass
class CompilationResult:
    target: str
    verilog: str
    verilog_lines: int
    data_width: int
    fraction: int
    overflow: str           # "saturate" or "wrap"
    rounding: str           # "nearest", "stochastic", "floor"
    estimated_luts: int
    estimated_dsps: int
    estimated_ffs: int
    guard_bits: int
    max_freq_mhz: float

6.2 OverflowProofResult

Python
@dataclass
class OverflowProofResult:
    safe: bool
    guard_bits: int
    max_intermediate_bits: int
    overflow_possible_vars: list[str]

6.3 PowerEstimate

Python
@dataclass
class PowerEstimate:
    dynamic_mw: float
    static_mw: float
    total_mw: float
    energy_per_spike_nj: float
    toggle_rate: float

6.4 ResourceEstimate

Python
@dataclass
class ResourceEstimate:
    estimated_luts: int
    estimated_dsps: int
    estimated_ffs: int
    estimated_bram_18k: int
    mul_count: int
    add_count: int
    register_bits: int

7. Performance Characteristics

7.1 Compilation Speed

Neuron Type State Vars Compile Time Lines
LIF 1 ~5 ms ~80
Izhikevich 2 ~8 ms ~120
AdEx 2 ~10 ms ~140
HH 4 ~20 ms ~250
Custom (10 vars) 10 ~50 ms ~600

7.2 Generated Verilog Quality

Metric LIF Q8.8 Izh Q16.16 HH Q16.16
Lines 80 120 250
LUTs (Artix-7) ~80 ~200 ~500
DSPs 1 3 8
Fmax 450 MHz 400 MHz 350 MHz
Power (28nm) 0.003 mW 0.008 mW 0.06 mW

7.3 LUT Accuracy

Function LUT Entries Range Max Error
exp 16 [-8, 8) 1.5%
log 16 (0, 8) 2.0%
tanh 16 [-8, 8) 1.0%
sigmoid 16 [-8, 8) 1.2%
sqrt 16 [0, 8) 1.8%

8. Test Suite and Verification

8.1 Equation Compiler Test

Bash
python -c "
from sc_neurocore.neurons.equation_builder import from_equations
from sc_neurocore.compiler.equation_compiler import compile_to_verilog

n = from_equations('dv/dt = -(v-E_L)/tau_m + I/C',
    threshold='v > -50', reset='v = -65',
    params=dict(E_L=-65, tau_m=10, C=1), init=dict(v=-65))
v = compile_to_verilog(n, module_name='sc_lif')
assert 'module sc_lif' in v
assert 'spike' in v
assert len(v.splitlines()) > 50
print(f'Equation compiler: PASS ({len(v.splitlines())} lines)')
"

8.2 Multi-Target Compilation Test

Bash
python -c "
from sc_neurocore.neurons.equation_builder import from_equations
from sc_neurocore.compiler.deployment import compile_multi_target

n = from_equations('dv/dt = -(v)/tau + I',
    threshold='v > 0', reset='v = 0',
    params=dict(tau=10), init=dict(v=0))
results = compile_multi_target(n, ['artix7', 'ice40'], 'test')
assert len(results) == 2
print('Multi-target: PASS')
"

8.3 Quantizer Test

Bash
python -c "
from sc_neurocore.compiler.quantizer import quantize_weights
q = quantize_weights([1.0, -0.5, 0.25], data_width=16, fraction=8)
assert q[0] == 256   # 1.0 * 256
assert q[1] == -128   # -0.5 * 256
assert q[2] == 64     # 0.25 * 256
print('Quantizer: PASS')
"

8.4 Static Analysis Test

Bash
python -c "
from sc_neurocore.compiler.static_analysis import prove_overflow_free
r = prove_overflow_free(
    {'v': '-(v - E_L)/tau_m + I/C'},
    dict(E_L=-65, tau_m=10, C=1),
    data_width=16, fraction=8,
)
assert r.safe
print(f'Overflow proof: PASS (guard_bits={r.guard_bits})')
"

8.5 SVA Generation Test

Bash
python -c "
from sc_neurocore.compiler.static_analysis import generate_sva
sva = generate_sva(['v'], module_name='sc_lif')
assert 'a_no_overflow_v' in sva
assert 'c_spike_reachable' in sva
print('SVA generation: PASS')
"

8.6 Power Estimation Test

Bash
python -c "
from sc_neurocore.compiler.static_analysis import estimate_power
v = 'wire signed [15:0] _mul0 = a * b; reg signed [15:0] v_reg;'
pe = estimate_power(v, data_width=16, freq_mhz=200, process_nm=28)
assert pe.total_mw > 0
print(f'Power: {pe.total_mw:.6f} mW — PASS')
"

8.7 Deployment Functions Test

Bash
python -c "
from sc_neurocore.compiler.deployment import (
    generate_constraints,
    generate_cocotb_testbench,
    generate_riscv_driver,
    generate_sby_script,
)

xdc = generate_constraints('test', freq_mhz=200)
assert 'create_clock' in xdc

tb = generate_cocotb_testbench('test', data_width=16, fraction=8)
assert 'cocotb' in tb

d = generate_riscv_driver('test', {'v': 16}, rtos='freertos')
assert 'xTaskCreate' in d

sby = generate_sby_script('test', mode='bmc', depth=10)
assert 'mode bmc' in sby

print('All deployment: PASS')
"

8.8 E2E Pipeline Test

Bash
python -m pytest tests/e2e/test_e2e_pipeline.py -v

8.9 Troubleshooting

Symptom Cause Fix
compile_to_verilog fails Invalid ODE syntax Check equation string format
Overflow in simulation Guard bits insufficient Increase data width
Yosys synthesis fails Unsupported Verilog construct Check target compatibility
Power estimate zero Empty Verilog source Verify compilation output
MLIR bundle missing firtool firtool not installed Install CIRCT toolchain

References

  1. Fixed-point arithmetic: Yates, R.B. "Fixed-Point Arithmetic: An Introduction." Digital Signal Labs, Technical Report, 2013.

  2. Yosys synthesis framework: Wolf, C. "Yosys Open SYnthesis Suite." https://yosyshq.net/yosys/, 2024.

  3. CIRCT project: LLVM Foundation. "Circuit IR Compilers and Tools." https://circt.llvm.org/, 2024.


Further Reading