Skip to main content

Setnex ISA balanced-ternary processor simulator

Project description

setnex-sim

Python simulator for the Setnex ISA — an open balanced ternary instruction set architecture. Implements the full fetch/decode/execute cycle for a 27-trit processor: 27 general-purpose registers, 5 configurable ternary logic modes (LMODE), and fixed-length R/I/J/U/B instructions. Built on tritlib.

Status

v0.5 — fully functional simulator with assembler, CLI toolchain, and architectural exception handling. 52 opcodes implemented (adds 6 TFP), 9 CSRs, 98 %+ test coverage.

Implemented:

  • Decoder (R, I, J, U, B formats)
  • ALU (ADD, SUB, MUL, DIV, MOD, NEG, TAND, TOR, TNOT, TIMPL, CONS, ACONS, TSHIFT, TCMP, TGET, TSET, TSIGN, TABS, TMIN, TMAX)
  • Ternary floating-point unit (FADD, FSUB, FMUL, FDIV, FCMP, FCVT) — T26F format (§7)
  • Configurable ternary logic via LMODE (Kleene, Łukasiewicz, Heyting, RM3, Bochvar)
  • Registers (27 GPR + CSR: PC, LMODE, FLAGS, EPC, ECAUSE, EVEC, STATUS, ESAVE, ETVAL)
  • Architectural exception dispatch: EXC_DIV0, EXC_FAULT, EXC_ILLEGAL, EXC_ECALL (§8), including machine-check reset on kernel-mode synchronous fault
  • Sparse word-addressed memory
  • CPU fetch/decode/execute loop with ternary FLAGS (sign, carry)
  • Branches and jumps (BEQ, BNE, BLT, BGT, BLE, BGE, BF, BRT3, JMP, JMPA, CALL)
  • System instructions (CSRR, CSRW, CSRX, ECALL, IRET, TSEL)
  • Text assembler (setnex-asm): .sasm.tern, label resolution, pseudo-instructions
  • Runner (setnex-run): execute .tern files with register initialisation and ternary exit status

Install

pip install setnex-sim

Or from source:

git clone https://codeberg.org/setnex/setnex-sim
cd setnex-sim
pip install -e ".[dev]"

CLI usage

Assembler

setnex-asm program.sasm -o program.tern

CSR operations accept symbolic names (case-insensitive) as an alternative to numeric addresses:

CSRR t0, FLAGS        ; equivalent to `CSRR t0, 3`
CSRW t0, EVEC         ; equivalent to `CSRW t0, 6`

Recognised names: PC, LMODE, FLAGS, EPC, ECAUSE, EVEC, STATUS, ESAVE, ETVAL (addresses 1..9, §2.2).

Runner

setnex-run program.tern --set a0=5 --print a0

Options:

  • --set REG=VAL — initialise a register before execution (repeatable)
  • --print REG — print a register after HALT (default: a0)
  • --verbose — print all registers and cycle count

Exit status follows the ternary convention via a1 at HALT:

  • a1 < 0 (N) → shell exit 1 (error)
  • a1 = 0 (Z) → shell exit 0 (indeterminate)
  • a1 > 0 (P) → shell exit 0 (success)

Example programs

Example programs are provided in fixtures/:

sum.sasm : sum of integers 1..N (validates the full pipeline: LI, CMP, BF, ADD, ADDI, JMP):

setnex-asm fixtures/sum.sasm -o sum.tern
setnex-run sum.tern --set a0=5 --print a0
# a0 = 15

count_p.sasm : count P trits in a0 using TGET and BRT3:

setnex-asm fixtures/count_p.sasm -o count_p.tern
setnex-run count_p.tern --set a0=13 --print a0
# a0 = 3  (13 = +++ in balanced ternary — three P trits)

modsum.sasm : sum with sign driven by symmetric Euclidean i mod 3 ∈ {−1, 0, +1}, dispatched natively by BRT3 — a computation that has no natural binary equivalent:

setnex-asm fixtures/modsum.sasm -o modsum.tern
setnex-run modsum.tern --set a0=6 --print a0
# a0 = -2

sign_check.sasm : looks for a zero between a0 and a1, returns a1 = P if found, Z if absent, and a0 the results if a1 == P

setnex-asm fixtures/sign_check.sasm -o sign_check.tern
setnex-run sign_check.tern --set a0=3 a1=7 --print a0 --print a1
# a0 = 3
# a1 = 0
setnex-run sign_check.tern --set a0=-3 a1=7 --print a0 --print a1
# a0 = 0
# a1 = 1

sum_saturated.sasm : iterated saturating add (ADDS), counting iterations until t1 = t1 + t0 reaches T27 saturation. Uses TMIN for the all-P saturation test and BRT3 for the three-way loop dispatch — the native balanced-ternary idiom for bounded accumulation:

setnex-asm fixtures/sum_saturated.sasm -o sum_saturated.tern
setnex-run sum_saturated.tern --set a0=1 a1=0 --print a0
# a0 = iterations to saturate adding 1 to 0

fdiv.sasm : divides a0 by a1 in TFP (T26F), traps via ECALL to a minimal handler if the result is (division by zero in floating-point produces infinity, detected through FLAGS.carry = P). Illustrates the full v0.5 exception path: handler installation via CSRW t0, EVEC, trap on condition, handler advances EPC and returns via IRET:

setnex-asm fixtures/fdiv.sasm -o fdiv.tern
setnex-run fdiv.tern --set a0=10 a1=2 --print a0 --print a1
# a0 = 5 (finite), a1 = 1, exit 0
setnex-run fdiv.tern --set a0=10 a1=0 --print a0 --print a1
# a0 = 0, a1 = -1, exit 1 (handler trapped on ∞)

Exception handling

v0.5 implements the full §8 exception contract. Five architectural causes are wired:

Code Name Trigger ETVAL
−13 EXC_DIV0 DIV/MOD with rs2 = 0 0
−11 EXC_FAULT LOAD/STORE on unmapped address, or fetch from unmapped PC faulting address
−10 EXC_ILLEGAL reserved opcode (−20, −19, +14..+40) or unrecognised word raw instruction word
0 EXC_ECALL ECALL instruction 0 (syscall number in a7)

Entering any of these saves STATUS → ESAVE, PC → EPC, writes ECAUSE and ETVAL, forces STATUS.mode = STATUS.ie = N (kernel, interrupts masked) and jumps to EVEC. Return via IRET atomically restores PC ← EPC and STATUS ← ESAVE.

Minimal handler pattern (install at EVEC, do work, advance EPC, return):

    LI    t0, handler
    CSRW  t0, EVEC           ; install handler
    ; ... user program, eventually triggers a trap ...
handler:
    ; ... handler body (read ECAUSE, ETVAL, a7 for syscall number, …) ...
    CSRR  t0, EPC
    ADDI  t0, t0, 1          ; resume at instruction after the trap
    CSRW  t0, EPC
    IRET

Machine-check reset — a synchronous fault raised while STATUS.mode = N (i.e. inside a handler) is not dispatchable with the single-slot EPC/ESAVE of v0.5. The processor restarts from power-on state instead: PC ← 0, all CSRs and GPRs cleared. Proper nested handling is deferred to v0.6.

Python API

from setnex_sim.cpu import CPU
from setnex_sim.assembler import encode_R, encode_I

cpu = CPU()
program = [
    encode_I(-24, rd=1, rs1=0, imm=5),   # LI r1, 5
    encode_I(-24, rd=2, rs1=0, imm=3),   # LI r2, 3
    encode_R(-40, rd=3, rs1=1, rs2=2),   # ADD r3, r1, r2
    encode_R(0,   rd=0, rs1=0, rs2=0),   # HALT
]
cpu.load(program)
cpu.run()
print(int(cpu.regs[3]))  # 8

Setnex ISA

The full ISA specification is available at setnex.org and on Codeberg — Apache 2.0, patent-free.

Licence

MIT — Copyright 2026 Eric Tellier

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

setnex_sim-0.5.0.tar.gz (33.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

setnex_sim-0.5.0-py3-none-any.whl (17.4 kB view details)

Uploaded Python 3

File details

Details for the file setnex_sim-0.5.0.tar.gz.

File metadata

  • Download URL: setnex_sim-0.5.0.tar.gz
  • Upload date:
  • Size: 33.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for setnex_sim-0.5.0.tar.gz
Algorithm Hash digest
SHA256 f71df752c089d18f3463c7420c2a2ec32b81aad986a2c700d17a76970d8f16dd
MD5 e9ccc0363abf65e05fd3f55bdb173b16
BLAKE2b-256 4bf7dcc568bf3b480d0ad5ce6e37293a8d3ba26c9ee6b3cef7df354a57f2befe

See more details on using hashes here.

File details

Details for the file setnex_sim-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: setnex_sim-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 17.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for setnex_sim-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a82f38a953211ec1097f59c0ca8a7cf7c84a11dfdc735cfb042dc8025b7772e6
MD5 442fad9c6bcb42f2c641b413a2bdeb5a
BLAKE2b-256 ab8f2f71796971fa39a2f942e3584b9ce23f1abbb46372b179cc8430de55b7c0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page