A cycle-accurate PRU assembly simulator with an interactive HTML dashboard. No hardware required: Runs entirely on a PC, with the option for your AI to use the simulator through an MCP server.
The simulator currently models the PRU subsystems on the AM243x (PRU_ICSSG, V4 ISA) and AM263x (PRU-ICSS, V3 ISA).
These are simulator-side capabilities, not features of the PRU hardware itself.
- Interactive dashboard — register panel, memory panel, IO pin control, disassembly view
- ELF binary loading — load compiled .out files directly (TI PRU CGT ELF32) with automatic disassembly
- Step-back — reverse any instruction; full machine state including SD filter is restored
- Signal graph — digital logic analyzer for GPO/GPI pin transitions
- Memory graph — analog scope for memory buffer waveform visualization
- GPIO loopback — wire GPO groups directly to GPI for firmware loopback testing without hardware. Can specify loopback latency/jitter/clock-drift
- Device bus — attach external device models (
DeviceModel) to any core's GPIO pins and wire pins between cores. A sharedDeviceBusresolves open-drain nets as wired-AND and reports push-pull contention as a fault instead of last-writer-wins; devices exposeevents()andfaults()for checking firmware - SSI encoder model and reader — independent binary/Gray encoder
DeviceModelwith up to 64-bit frames, error fields and 12 encoder presets, clock-rate and monoflop faults, generated shared-memory ABI, and PRU1 reader and PRU0 emulator examples. See SSI device model and reader - UART decoder — bit-bang UART decode in the IO panel (8N1, auto-detect bit period)
- Multi-core simulation & debug view — PRU_ICSSG supports simultaneous simulation & debugging of up to 3 PRU cores; PRU-ICSS supports simultaneous simulation & debugging of both PRU cores
- Tiling window manager — drag, split, collapse/expand, and persist panel layouts
- MCP server — AI assistant integration via Model Context Protocol
Status per processor:
- ✅ implemented in the simulator
- ❌ present in silicon, not currently simulated
- N/A not present on that processor (per the PRU Subsystem Features Comparison app note)
| Category | Feature | AM243x | AM263x |
|---|---|---|---|
| Core | Full PRU ISA (ALU, branches, bit ops, LOOP, LBCO/SBCO, LBBO/SBBO, XIN/XOUT/XCHG, MVI) | ✅ | ✅ |
| General PRU Specs | PRU cores | ✅ | ✅ |
| General PRU Specs | RTU_PRU (Auxiliary PRU) cores | ✅ | N/A |
| General PRU Specs | TX_PRU (Transmit PRU) cores | ❌ | N/A |
| General PRU Specs | INTC | ❌ | ❌ |
| Accelerators: Data Processing | MPY/MAC | ✅ | ✅ |
| Accelerators: Data Processing | Scratchpad | ✅ | ✅ |
| Accelerators: Data Processing | IPC Scratch Pad | ✅ | N/A |
| Accelerators: Data Processing | Broadside RAM | ❌ | N/A |
| Accelerators: Data Processing | BSWAP | ✅ | N/A |
| Accelerators: Data Processing | CRC16/32 | ❌ | ❌ |
| Accelerators: Data Processing | SUM32 | ❌ | N/A |
| Accelerators: Data Processing | Task Manager | ❌ | N/A |
| Accelerators: Data Processing | Spinlock | ❌ | N/A |
| Accelerators: Data Processing | Filter Data Base (FDB) | ❌ | N/A |
| Accelerators: Data Movement | XFR2VBUS | ❌ | ❌ |
| Accelerators: Data Movement | PSI TX & RX | ❌ | N/A |
| Accelerators: Data Movement | XFR2TR | ❌ | N/A |
| Peripherals | Sigma-delta filter (3 ch, CIC accumulators, Fast Detect, pattern generators) | ✅ | ✅ |
| Peripherals | Peripheral Interface / 3 ch EnDAT (protocol-agnostic serializer/deserializer, used for EnDat/BiSS/HDSL/line-code) | ✅ | ✅ |
| Peripherals | UART | ❌ | ❌ |
| Peripherals | eCAP | ❌ | ❌ |
| Peripherals | IEP (Industrial Ethernet Peripheral: timer/compare/capture) | ✅ | ✅ |
| Peripherals | MII_RT / MII_G_RT (RGMII) | ❌ | ❌ |
| Peripherals | MDIO | ❌ | ❌ |
| Peripherals | SGMII | ❌ | N/A |
| Peripherals | PWM | ❌ | N/A |
| Mode Class | Specific I/O Mode | Core Registers Used | Primary Use Case / Description | AM243x | AM263x |
|---|---|---|---|---|---|
| GPI (Input) | Direct Input Mode | R31[31:0] |
Lowest latency direct pin sampling and software bit-banging | ✅ | ✅ |
| GPI (Input) | 16-Bit Parallel Capture | R31 |
Captures 16 bits of parallel data synchronous to a sampling clock | ❌ | ❌ |
| GPI (Input) | 28-Bit Shift In Mode | R31 |
Automatically deserializes incoming high-speed serial streams | ❌ | ❌ |
| GPO (Output) | Direct Output Mode | R30[31:0] |
Direct software-driven pin control for custom low-latency protocols | ✅ | ✅ |
| GPO (Output) | Shift Out Mode | R30 |
Automatically serializes data written to the register over a clock pin | ❌ | ❌ |
| Interface | Peripheral IF Mode | Slice Hardware Co-processor | Industrial motor position encoder feedback (EnDAT, BiSS-C, Tamagawa) | ✅ | ✅ |
| Interface | SD Mode | Internal SDFM Blocks | Connects to external Sigma-Delta ADCs for isolated current/voltage sensing | ✅ | ✅ |
| Interface | MII / RGMII Mode | ICSSG Real-Time Ethernet Switch | Real-time industrial communication hardware layer (EtherCAT, PROFINET, EtherNet/IP) | ❌ | ❌ |
- Python 3.9 or later
- Dependencies:
pip install -r requirements.txt
python ui/server.pyOpen http://localhost:8080 in your browser.
pru_simulator/
├── config/ Memory layout configs (AM243x, AM263x)
├── core/ PRU ISA implementation (ALU, parser, disassembler, ELF loader, …)
├── mem/ Memory bus and region model
├── mcp_server/ MCP server for AI tool integration
├── pru_io/ GPO/GPI port model, SD filter (R30/R31 interface), DeviceModel/DeviceBus, device models (SSI encoder, TCA9538), generated ABIs
├── perif/ 3-channel Peripheral Interface (SCU), GPCFG mux, TX→RX loopback
├── schema/ Shared-memory ABI schemas (source for generated .py/.inc files)
├── source/ Example PRU assembly programs
├── tests/ Pytest test suite
├── tools/ Headless runner, ABI code generators, perif drift report
├── ui/
│ ├── server.py FastAPI WebSocket server
│ └── static/ Dashboard HTML/JS (index.html, app.js, layout.js)
├── xfr/ XFR scratchpad + MAC accelerator
├── memory.cfg Default memory configuration (AM243x)
├── requirements.txt
└── simulator.py Simulator entry point
The simulator loads memory.cfg at startup. Three built-in configs are provided in config/:
| File | Target | Notes |
|---|---|---|
memory_am243x.cfg |
AM243x | Default |
memory_am263x.cfg |
AM263x | |
memory_am261x.cfg |
AM261x | MSRAM burst timing measured on LP-AM261; selected by cfg file, not by target |
Switch config by editing memory.cfg or copying one of the config/ files over it.
Clock keys in the [device] section:
| Key | Applies to | Default if absent |
|---|---|---|
pru_clock_mhz |
PRU0, RTU0, and the IEP OCP clock | 200 |
pru1_clock_mhz |
PRU1, RTU1 | inherited core |
iep_clock_mhz |
IEP while ICSS CFG IEPCLK bit 0 is clear |
200 |
Omitting pru_clock_mhz preserves the simulator's 200 MHz core default; omitting pru1_clock_mhz inherits the first rate. Explicitly configured rates, including 250 MHz, remain unchanged. The external IEP defaults independently to 200 MHz, the rate the TRM gives for ICSSG_IEP_CLK (SPRUIM2J §6.4.13.2.4). Core and IEP clocks remain independently configurable, including positive fractional MHz values, and changing the core speed leaves the external IEP clock unchanged. Firmware's IEPCLK register selects which configured source drives the timer.
With both rates omitted, 1000 unstalled core cycles produce 1000 IEP ticks with CNT_ENABLE=1 and DEFAULT_INC=1 while IEPCLK bit 0 is clear. The supplied memory.cfg explicitly selects 250 MHz cores; with the omitted external IEP rate of 200 MHz, it produces 800 ticks for the same 1000 core cycles. See clock sources and configuration for the cited TI firmware examples. RTU1 is available only on the AM243x/AM64x profiles; an omitted target selects AM243x.
The simulator's IEP GLOBAL_CFG reset value, 0x550, follows the TRM register defaults (IEP_GLOBAL_CFG_REG, Table 14-10907: CMP_INC and DEFAULT_INC reset to 5h, CNT_ENABLE to 0h). DEFAULT_INC=5 counts nanoseconds at the default 200 MHz IEP clock, where one tick is 5 ns, so firmware that enables the counter with a read-modify-write (GLOBAL_CFG | 1) gets a nanosecond count. If you set a different IEP clock, for example 250 MHz (a 4 ns tick), firmware must set DEFAULT_INC itself (4 at 250 MHz) to keep a nanosecond count. A full-word write of 1 leaves DEFAULT_INC=0, and the counter does not advance. The SSI runtime (pru_io/ssi_runtime.py) is the in-repo example: it enables a stopped IEP with DEFAULT_INC=1 written explicitly and sizes its timeouts from the active IEP clock.
All cores share one IEP timer. Each core's elapsed cycles, including stalls, are converted to time with exact rational clock periods, so cores at different clocks stay aligned without float drift.
Count-register writes preserve the running IEP clock phase, including byte and 64-bit writes. Enabling the counter or changing its increment or effective clock starts a new phase.
Dashboard core and load selectors follow the configured target, including RTU1 on AM243x/AM64x. Each core has an independent constant table. Fixed C10, C11 and C22 addresses follow the executing core on AM243x/AM64x (SPRUIM2J, Table 6-65, pp. 581–582); AM263x PRU0/PRU1 use their documented C11/C22 addresses. Other configured entries are preserved. AM263x's legacy simulated RTU0 has no hardware counterpart and keeps the common table; this change does not add hardware support for that core. Correct address selection does not implement the task manager, control or RAT register behavior. Accesses still require the corresponding memory region/model in the chosen configuration. The dashboard constant-table panel shows the common sidecar reference, not the executing core's corrected table.
AM263x C10 is reserved in the TRM but currently retains the inherited AM243x sidecar value. Its silicon behaviour and simulator handling are tracked in #79; firmware must not rely on that inherited mapping.
Dashboard step-back uses one ordered history across all cores. Undo restores all cores, shared memory, the IEP and devices to the same saved point, and removes later history entries so peer state stays consistent.
Paced stepping limits each follower to 1000 catch-up instructions per lead step.
step_paced and step_paced_many return complete, actual lead_steps, and the selected targets
(in exact IEP time units). An unfinished catch-up returns complete=False with
reason="pacing_incomplete"; MCP reports the same reason and dashboard Run stops
with a message. Another call continues execution from the actual core state;
elapsed time is never skipped. A follower that reaches HALT or the end of its
program finishes normally, while a recorded CPU fault remains an error. The
guard target is fixed for that catch-up even if firmware changes the mux.
Unfinished targets are retained per lead and follower set: retries finish them
before advancing that lead again, so lead_steps can be zero during catch-up.
Other follower selections do not execute or discard that saved work. A core
reset discards pending targets; dashboard step-back restores them with time.
Batched callers must count lead_steps, not the requested batch size: a
bounded catch-up may return before the batch finishes. Retry pacing_incomplete;
report pacing_catchup_failed instead of treating it as a completed batch.
Peripheral Interface timestamps use the same core-time origin as the IEP,
including after resetting one core. A full hardware reset starts both at zero.
The MCP pru_step_multicore response includes total lead_steps on success,
incomplete catch-up and core faults, so callers can account for partial batches.
Multicore pacing executes complete instructions before catching up peer cores; it does not order all shared effects cycle by cycle. A lagging core's firmware write to IEPCLK takes effect at the already-advanced shared IEP time, rather than retroactively at that core's earlier time. Configuring the clock before advancing the cores avoids this case. Chronological multicore execution, including this clock-write limitation, is tracked in #57.
Simulator.hard_reset() now calls IepTimer.hardware_reset(), so the counter, compare and capture state no longer survive a full reset. Register defaults follow AM64x/AM243x TRM SPRUIM2J, Tables 14-10907 and 14-10925: the counter stays disabled, with GLOBAL_CFG defined fields 0x550 and CAP_CFG defined fields 0x1FC00. Reset leaves DEFAULT_INC=5, so enabling only CNT_ENABLE with a read-modify-write advances the counter by five per IEP tick; firmware must set DEFAULT_INC explicitly when another increment is required. Capture configuration follows Table 14-10925: bits 9:0 enable slots, bits 17:10 configure synchronization, and bits 23:18 select external sources. The simulator latches the first event until its valid bit is cleared; input synchronization, source routing and continuous overwrite are not modelled. The earlier synthetic first/last bits have been removed because they overlapped these fields.
See getting_started.md for step-by-step walkthroughs of all examples in source/.
| File | What it demonstrates |
|---|---|
running_led.asm |
GPO pin toggle, LOOP instruction |
mem_copy.asm |
SBCO/LBCO data memory read/write |
xfr_test.asm |
XFR scratchpad (XOUT/XIN, SPAD banks) |
mac_example.asm |
MAC accelerator (MPY mode + accumulate mode) |
crc_example.asm |
CRC16/32 accelerator: CRC-16 (half-word writes) and CRC-32 (word writes) over one frame |
crc_bitswap_example.asm |
CRC16/32 accelerator bit-mirrored reads: byte-wide flip (R27) and 32-bit flip (R28), with byte-wide and 32-bit-wide data writes |
uart_tx.asm |
Bit-bang UART TX (115200 baud, 8N1) with UART decoder |
uart_rx_11frame.asm |
Bit-bang UART RX (4 Mbaud, 8N1) with frame injection from IO panel |
ssi_generic_reader/ssi_generic_reader.asm |
SSI encoder reader (1-64 bit frames) and generated shared-memory mailbox; see timing and manual 300 MHz instructions |
ssi_generic_emulator.asm |
SSI encoder emulator on PRU0 (1-64 bit frame read from a shared-memory block at every frame start); pairs with the reader in a board loopback, see docs/ssi_device_model.md |
mvi_gpio_loopback.asm |
MVIB register-indirect + GPIO loopback (walking-bit pattern) |
sdfm_sinc3_demo/ |
Free-running SINC3 filter adapted from AM261x ICSS-M firmware |
perif_duty_cycle_sweep.asm |
Peripheral Interface TX: 125 Mbit 0%→100% duty-cycle pulse sweep on PRU0 ch0 (needs memory_perif_125mbit_demo.cfg) |
pif_eth/ |
8b/10b line-coded Ethernet TX over the Peripheral Interface (PRU0 ch0): firmware PRNG, CRC-32 on the CRC16/32 broadside accelerator, running-disparity 8b/10b via DRAM0 LUT, pcap output. See PROJECT_REPORT.md (PDF) · handoff note. Experimental higher-clock variants pif_eth_tx_n2*.asm (not wired into the test suite): design note. PRU1 RX over the PRU0→PRU1 loopback (pif_eth_rx_o1_raw.asm + rx_driver.py), CI-exercised: design note · handoff note |
CI and agent workflows can execute an assembly source or a linked TI PRU ELF without starting the web UI. The command writes exactly one deterministic JSON object to stdout; load errors exit 2, unmet conditions or exhausted budgets exit 3, and unexpected runner errors exit 4.
python -m tools.headless_runner --json --assembly source/example.asm --core pru0 --max-steps 10000
python -m tools.headless_runner --json --elf build/firmware.out --until 'reg:r0==0x55' --max-cycles 5000--until accepts halt (the default), cycles>=N, register equality or
inequality, 32-bit little-endian memory comparisons, and GPI/GPO pin equality.
Both --max-steps and --max-cycles are hard budgets. JSON remains the only
stdout format when --json is omitted; the flag makes the CI contract explicit.
python -m pytest tests --tb=short -qThe maintained test suite lives under tests/. Files under references/ are
standalone experiments and may require optional packages (for example,
matplotlib); they are not part of the CI test suite.
v0.3.0 — hover over PRU SIM in the dashboard header to confirm.
v0.3.0
- SSI encoder simulation — binary or Gray frames up to 64 bits, optional error fields and twelve documented RM08/SICK presets, with faults for excessive clock rate, incomplete frames and extra pulses.
- Reader and emulator firmware — generated configuration ABI, seqlock mailbox and host runtimes support model-to-reader and two-core firmware loopback tests. Separate Python decoding keeps validation independent; failed reader reloads preserve the last working configuration.
- Generic MCP device tools — discover, attach, detach and inspect SSI or TCA9538 devices, events and faults through the MCP SDK stdio transport. See SSI setup and limitations.
v0.2.10
- Shared device bus — extends the DeviceModel contract from #44 with GPIO drive ownership, pin release on detach, core-to-core wires and contention faults. TCA9538 uses the same device path; time-driven devices include stall cycles.
- Coherent multi-core step-back — restores all cores, shared memory, MAC/XFR, IEP and attached devices to one saved point, discarding later peer history.
v0.2.9
- Exact shared IEP time — one rational timebase includes memory stalls and preserves compare/capture behavior across different core clocks. Omitted core and external IEP rates default to 200 MHz; explicit rates remain configurable.
- RTU1 and paced execution — AM243x/AM64x profiles gain RTU1. Multi-core stepping follows elapsed time, reports actual progress when catch-up is bounded, and keeps Peripheral Interface timestamps aligned after a core reset.
- IEP-aware step-back — snapshots restore the shared timer, reject unsafe undo after a peer core progresses, and clear all core histories on configuration changes.
- Per-core constant addresses — C10/C11/C22 selection follows the executing core where the target defines it; corrected addresses do not add the corresponding register models.
v0.2.8
- Core faults and wait instructions — memory faults are available in simulator, MCP and dashboard state;
WBS/WBCaccept implicit R31 and explicit register forms, with immediate bit numbers limited to 0–31. - Hardware reset — resets IEP counter, compare and capture state to the documented register defaults. See the IEP reset notes above for increments and capture limits.
- Configuration and MCP safety — invalid configurations are rejected before writing, copied configurations can locate their constants file, and MCP calls validate argument types.
v0.2.7
- CRC16/32 broadside accelerator (XFR
device_id=1) — modeled on AM64x/AM243x TRM (SPRUIM2H) §6.4.6.2.2.1 / Table 6-429:R25CRC_CFG,R27CRC_DATA_8_BFLIP (read),R28CRC_SEED / CRC_DATA_32_BFLIP (read),R29CRC_DATA (destructive read resets the accumulator to the seed). CRC-16 (standard/mod) and CRC-32, matched to AM243x silicon:R29is the non-reflected accumulator andR28the reflected (zlib/Ethernet) value; CCITT mode feeds bytes MSB first. Byte, half-word and word writes give the same result. Shares the broadside window with the MAC accelerator, selected bydevice_id. The dashboard shows a live CRC status widget (mode and accumulator) in the single-core and multi-core views. Backend inxfr/crc_accelerator.py; functional spec of the underlying RTL inreferences/icss_m_xfr_crc_functional_spec.md. - CRC examples —
source/crc_example.asm(CRC-16 with half-word writes, CRC-32 with word writes, using the TRM's NOP-based programming model) andsource/crc_bitswap_example.asm(reads R27 byte flip, R28 32-bit flip and R29, with R29 read last). Both are checked end-to-end intests/test_crc_example.pyandtests/test_crc_bitswap_example.py. - pif_eth computes the Ethernet FCS on the CRC accelerator — new
pif_eth_crc32_hw.incis a drop-incrc32_corewith the same contract as the bit-serial routine: 32-bit words go through CRC_DATA in a zero-overheadLOOP, and 1–3 trailing bytes run as a byte-wide session re-seeded via CRC_SEED (the TRM requires one write width per session). All four firmware images use it, and the n2 TX variants drop their inlined bit-serial copies.pif_eth_crc32.incstays as the software fallback. A 128-octet FCS goes from ~9.5k to 181 cycles on TX and 187 on RX (~50x), and is now data-independent.rx_driver.pystill passes all 28 rung/seed combinations. README and PROJECT_REPORT (+PDF) updated. - IEP follows simulated time on its own clock — the IEP used to tick once per instruction on every core, so memory stall cycles were lost and two cores stepping together ran it at twice the rate. Each core now reports its elapsed time (cycles including stalls, over its own clock), and the shared IEP follows the core that is furthest ahead, emitting one edge per IEP clock period. A core whose cycle counter is reset rejoins at the IEP's current time instead of freezing the counter. The IEP clock is independent of the PRU clocks:
iep_clock_mhzunder[device](defaultpru_clock_mhz), orSimulator.set_iep_clock_mhz()at runtime without a count jump. - Hardware test plan for the CRC changes —
docs/test-plans/2026-09-24-crc-accelerator-hw-test-plan.md(TC1–TC7, results table to fill in on silicon). The CRC results in this release are from the simulator and tested on silicon.
v0.2.6
- Peripheral Interface TX test patterns (
source/perif_tx_patterns.asm) — the existingperif_tx_pattern.asmstreams an 8-bit counter, which is ~90% zeros MSB-first and reads as sparse noise on the Signal Graph. New self-configuring firmware transmits the classic bit patterns instead, selected by aPATTERN .setat the top of the file:0x00,0xFF,0xAA,0x55, walking 1, walking 0, counter-only, or (the default) one byte of each followed by the counter —00 FF AA 55 | 01 02 04 08 10 20 40 80 | FE FD FB F7 EF DF BF 7F | 00 01 02 …. Twenty bytes, about 1280 graph samples, so the 2048 window catches the whole sequence in one single-shot capture. Patterns come from a table the prologue writes to DRAM at0x1F00, so changing them is fiveldipairs. Keeps the same pre-shifted start-bit framing, so the receiver still byte-aligns; everyPATTERNvalue is checked end-to-end over the ch0 loopback intests/test_perif_tx_patterns.py.perif_tx_pattern.asmis untouched — the drift experiment andtools/perif_drift_report.pydepend on its exact counter output. Cross-machine handoff note.
v0.2.5
- Signal Graph: Run now captures at the signal's own rate — the graph was fed only by the state push at the end of each Run chunk, so it sampled once per 100+ instructions. That is orders of magnitude coarser than a Peripheral Interface bit (2 core cycles at the channel-0
N=2divider), so a Run capture ofperif_duty_cycle_sweep.asmaliased away to nothing while the same firmware traced correctly under SIM, which steps one instruction per message. The server now samples inside its run loop and ships the batch as acapturemessage: every instruction while peripheral mode is active, every 100th otherwise — GP traces are firmware-paced (a 115200-baud bit-bang bit is ~1736 cycles) and the UART decoder's bit-period detection depends on the wider time span. The stride is decided per instruction, since firmware enables peripheral mode from inside the run. Peripheral captures are single-shot — at full rate a run fills the window in milliseconds, so it fills once and REC switches itself off, the way a logic analyzer does; GP captures keep rolling as before. Cross-machine handoff note.
v0.2.4
- Signal Graph: peripheral mode no longer plots GPO/GPI — in perif mode the GP Mux hands the pads to the Peripheral Interface, so the
GPO n/GPI nlanes the graph kept drawing from R30/R31 showed pins that do not exist on the wire, next to the perif lanes that do. The digital graph now keys offio.mode(recorded per sample): in perif mode only the peripheral lanes are drawn, and each active channel contributes three of them — the newperifN_out_enjoinsperifN_outandperifN_clk, so it is visible when the channel actually drives the pad. A channel qualifies as active if any of its three signals toggles, and then all three lanes are drawn, so a steadyout_enstill appears beside the data it qualifies. GP mode is unchanged. CSV export gainsmodeandperifN_out_encolumns. Cross-machine handoff note.
v0.2.3
- Signal Graph: Peripheral Interface clock lanes — the graph recorded
perifN_out(the serial data line) but not its bit clock, so a correct capture was unreadable: the perif serializer sends raw MSB-first bits with no framing, leaving no reference for where a bit starts. NewperifN_clklanes (one clock edge per bit, paired with the data lane and included in the CSV export) make the bit grid visible. Note that firmware prefixing a start bit — e.g.source/perif_tx_pattern.asm— shifts the payload right by one bit, so wire octets begin one bit after the first rising edge. - Tests no longer depend on the UI's core-speed selection — the speed selector rewrites
memory.cfgin place, and any test constructingSimulator()/PRUSimulatorMCP()with no arguments inherits that clock. Picking anything other than 200 MHz in the dashboard therefore broke six cycle-timed tests (bit-bang UART RX ×4, PRU1 clock config, SD channel switching). Newtests/conftest.pyprovides asim_config/nominal_configfixture that builds a throw-awaymemory.cfgwith the clock pinned; the SD test now matches its modulator clock to the configured core clock instead of a hardcoded 200 MHz. Suite verified green at 200, 250 and 333 MHz. - Reset now clears Peripheral Interface state — the UI's HW Reset (and the per-core Reset) left the perif's latched status bits standing, so a TX overrun/underrun,
rx_valid/rx_ovf,busyor a half-full FIFO from the previous run stayed visible in the Peripheral panel and in R31 after the reset.PRUCore.reset()now resets its IO port, which resets the attachedPeripheralInterface: per-channel TX/RX FIFOs, overrun/underrun, RX valid/overflow/EOF, capture progress, FSM/pins, recorded line history and the ns timeline, plus the R30 channel select and the TXCFG busy bits. Config is deliberately preserved — perif config registers (clock dividers, frame sizes, delays), the GP Mux selection and the loopback parameters all survive a reset, as does GPI (external stimulus, not core state). Cross-machine handoff note covering all three v0.2.3 changes.
v0.2.2
- pif_eth — PRU1 RX over the PRU0→PRU1 loopback (Option 1) — new
pif_eth_rx_o1_raw.asm+rx_driver.pyreceive the 8b/10b line-coded frames PRU0 transmits, at 2x-oversampled RX sample clock (a hardware requirement — the RX shift register doesn't decimate), with SOF from the RX hardware's own start-bit detection and EOF from a 2-consecutive-zero-byte proxy (one zero byte is a legal 5-bit run, not EOF). Post-frame comma-align, 8b/10b decode, CRC-32 and PRNG BER-check happen off the realtime path. Measured zero BER across the full n_tx ladder (2/4/6/8 → 125.00/62.50/41.67/31.25 Mbaud), 7 seeds each — includingn_tx=2, which the design's own risk section expected might not be reachable by the 8-cycle realtime budget; it passes, but at zero headroom (7 instructions + a 1-cycle DRAM write stall exactly fill the budget). This also promotespif_eth_tx_n2*.asmfrom reference-only artifacts (v0.2.1) to firmwarerx_driver.pyactually selects and exercises by divider. Design:2026-07-21-pif-eth-pru1-rx-design.md; cross-machine handoff note. - fix(pif_eth): bound the RX frame buffer — the post-frame decode stage wrote decoded octets into the 256 B reconstructed-frame buffer with no bound, so
payload_len > 252walked writes into the adjacent stats/control blocks, poisoninggo/seed/payload_len/rxcfgfor later frames. Fixed with a firmware-side backstop (eof_status = 2on overrun) plus a host-sideValueErrorfor oversizedpayload_leninrx_driver.py.
v0.2.1
- PRU core speed selector — controls-bar dropdown (200/225/250/300/333 MHz) sets
pru_clock_mhzandpru1_clock_mhztogether via a newGET/PUT /config/clock_speedendpoint, which patchesmemory.cfgin place (preserving formatting/comments — noconfigparserround-trip) and reloads the simulator. Always syncs all three cores; the PRU0→PRU1 perif clock-drift demo remains a separate manualpru1_clock_mhzedit, applied after picking a base speed from the dropdown. Design:2026-07-20-pru-core-speed-selector-design.md. - IEP timer clock — IEP0 now runs on its own clock, set with
iep_clock_mhzunder[device](default:pru_clock_mhz) or at runtime withSimulator.set_iep_clock_mhz(). It is driven by simulated time, not instruction count: memory stall cycles advance it, and two cores stepping together advance it once, not twice. See the TIMING notes inperif/iep.py. - pif_eth experimental 125 Mbaud TX firmware (
source/pif_eth/pif_eth_tx_n2*.asm) — batched 4-octet loads and fixed-shift unrolled 8b/10b bit-packing get the TX loop's own cost down to the perif drain-rate boundary. The all-checks build is a robust ~4x speedup (83.33 MHz @ n=3, zero BER) over the original 25 MHz design; a second build with 2 of the loop's FIFO-full checks removed hits the full 125 Mbaud (n=2) target with zero BER, but is hard-pinned to that exact divider — it silently corrupts frames at any other clock/divider combination. Neither file replacespif_eth_tx.asmin the test suite. Caveat and full results:2026-07-21-pif-eth-n2-125mbaud-design.md; cross-machine handoff note.
v0.2.0
- pif_eth — 8b/10b line-coded Ethernet TX over the Peripheral Interface (PRU0, ch0): self-configuring firmware with an xorshift32 PRNG, bit-serial CRC-32 FCS, true 8b/10b (running disparity) via a 256-entry DRAM0 LUT (
LBCO/c24), K28.5 inter-frame commas, and a Python decoder/driver that checks BER=0 and writes Wireshark pcaps. Two example frames — BERT (132 B) and UDP "Hello World Text" (64 B). Full write-up:source/pif_eth/PROJECT_REPORT.md(PDF); cross-machine handoff note.
v0.1.9
- TX continuous mode (live FIFO streaming) — corrected against the AM243x TRM (Table 6-424):
tx_frame_size = 0now pops one byte at a time from a live FIFO as each finishes shifting out (instead of snapshotting the queue at go-time), so software can push the next byte while the current one transmits. R31'stx_fifo_sts0(bits[4:2]) reports true live occupancy. - Half-empty refill —
source/perif_duty_cycle_sweep.asmnow streams its whole 9-byte sweep as one continuous, gapless transmission (verified: exactly 576 ns / 9×64 ns, zero dead time between bytes), refilling whenever occupancy drops to the TRM-specified 2-byte level, instead of one preload-and-go frame per byte. - R31 TX status corrected for channel 0 against TRM Table 6-424:
ovr0[0]/unr0[1]/tx_fifo_sts0[4:2]/busy0[5]confirmed bit-exact. Channels 1/2 are flagged in the spec as a known simplification (still byte-aligned, not yet TRM-verified) pending the remaining register field table.
v0.1.8
- Peripheral Interface (3-channel SCU) — protocol-agnostic serializer/deserializer modeled on the validated
references/endatRTL. Per-channel TX FIFO + wire/Tst-delay FSM + MSB-first serializer (frame size, bit-swap, clock modes 0–3, overrun/underrun) and RX FIFO + start-bit/oversample capture + frame-size EOF. Backend inperif/. - GP-Mux mode select —
GPCFG.PRU_GP_MUX_SEL[29:26] == 1(GPCFG00x26008/ GPCFG10x2600C) routes R30/R31 to the peripheral and switches the IO window to the Peripheral panel; selectable from the new "GP Mux" dropdown. - Timed PRU0→core-1 loopback — per-channel serial-sample loopback with configurable latency, jitter, and clock drift (±100 ppm) on independent per-core nanosecond timelines; large drift produces real RX bit-slip.
- Peripheral UI panel — per-channel TX/RX FIFO, FSM/status flags and config, plus a loopback control block; step-back captures peripheral + line state. GPO recording via the existing Signal Graph.
- MMR block per core at the AM243x
EDPRU0/EDPRU1offsets (0x260E0/0x26100).
v0.1.7
- UART RX receiver — 4 Mbaud bit-bang UART receiver assembly (
uart_rx_11frame.asm): polls GPI0 for start bit, assembles 11-byte frames into register buffer, stores via single SBCO to DRAM0, framing error detection with sticky flag at DRAM0+0x0FFE - UART frame injection UI — new "UART RX Inject" panel in the IO section: hex/ASCII payload toggle, configurable pin/baudrate (Mbaud with 2 decimals)/frame count, arms UARTFrameGenerator at current cycle for step-through reception
- UARTFrameGenerator peripheral — pre-computes bit-level UART waveform timeline on GPI pins; supports multi-frame, configurable baudrate, framing error injection for testing
- MCP
pru_uart_injecttool — end-to-end UART RX testing via MCP server (load assembly, inject frames, verify DRAM0 contents) - Baudrate tolerance tested: receiver handles 3.75–4.00 Mbaud (−6.25% to nominal)
v0.1.6
- Disassembler validation — compiled reference assembly (
pru_encoding_test.asm) with TI CGT clpru v2.3.3; 150 parametrized tests verify all instruction formats decode correctly - ISA execution validation — 97-test firmware (
isa_execution_test.asm) runs on real PRU hardware and in simulator; byte-for-byte DRAM comparison confirms execution match - Pseudo-instruction reconstruction — disassembler now shows NOP, MOV, ZERO, FILL instead of raw AND/XIN encodings
- Fix: WBS/WBC were swapped in disassembler (QBBS+offset=0 is WBC, not WBS)
- Fix: LBCO/SBCO constant table resolution from ELF-loaded code (C24 was treated as raw address 24)
- Fix: Carry/borrow convention corrected to match hardware (carry=1 when no borrow, ARM-like)
- Fix: SUC semantics corrected to a - b - ~C (subtracts NOT-carry as borrow)
v0.1.5
- MVI instructions — MVIB/MVIW/MVID with all 16 addressing modes (direct, register-file indirect, pre-decrement, post-increment)
- GPIO loopback — compact toggle strip in IO panel wires GPO groups to GPI combinatorially; guarded against SD mode
mvi_gpio_loopback.asm— walking-bit GPO→GPI demo using MVIB register-file indirect
v0.1.4
- Sigma-delta filter peripheral — full ICSS-G SCU SD model: 3-channel CIC filter (SINC3/SINC2/SINC1), Fast Detect sliding window, 2nd-order ΣΔ modulator pattern generator, memory-mapped registers at real ICSS-G addresses
- SD UI panel — R30 decode bar, per-channel accumulator display, pattern generator controls, config register shortcuts
- Step-back — full machine snapshot/restore including SD filter integrators and modulator state
- SDFM SINC3 demo — free-running SINC3 filter adapted from real AM261x ICSS-M firmware (
source/sdfm_sinc3_demo/) - Preprocessor
.asgdirective support (TI alias syntax, reversed arg order vs.set)
v0.1.3
- UART decoder — ▶ Decode / Clear buttons in IO panel; auto-detects bit period, displays decoded bytes as hex + ASCII
v0.1.2
- ELF binary loading — Open > Browse > select .out file; auto-disassembles and loads
- Disassembler — decodes all PRU instruction formats (PDSP v2.4.8 spec)
- Signal graph and memory graph split into separate, independent tileable panels
- IO panel now displays only GPO/GPI pin indicators (compact)
- Collapse/expand button (−/+) on every panel title bar for quick window management
- Resizable divider between Registers and C-Table sections
- Updated default layouts for single-core and multi-core modes
- Fix: LBBO/SBBO/XFR byte-address offset (RxByteAddr) now correctly handled
- Fix: Partial register writes preserve untouched bytes (byte-level write strobes)
- Fix: LSL/LSR/CLR/SET mask shift/bit amount to 5 LSBs per hardware spec
v0.1.1
- Signal graph with digital and analog zones
- Memory fill modal (pattern, sequence, waveform)
- Tiling window manager with drag-to-split
v0.1.0
- Initial release: full PRU ISA, dashboard, multi-core, MCP server