Repository navigation
Conversation
…module Register allocator: - release a block's registers after its last data consumer, including values produced outside a loop and read inside it - keep pre-initialized loop values in their own register and support explicit loop accumulator bindings and exported loop results - route each consumer input from the output port it is wired to and refuse connections to ports that own no register - record register-pool exhaustion per block so validation can name it - reserve user-typed registers that blocks emit verbatim and report overlapping ones - give multi-result macros one destination register per output - keep 64-bit (two register) sources and outputs Static module and templates: - report allocation failures, pinned-register conflicts, unconnected required inputs, invalid port connections, loop/container boundary violations and macro arity mismatches at validation time - program CTPPR0 so c28 points at shared RAM when a block uses c28, and drive SPI controller CS pins to their idle level, at every entry into generated code - warn in the GUI when a GPO/GPI pad is shared or a GPO has two drivers - gate blocks that need a hardware widget on the device capability table - define TEMP_REG3/TEMP_REG4 (R26/R27) for macros - expand multi-line instructions in the simulator path New shared scripts: pru_reserved_names.js (clpru reserved identifiers), pru_spi_timing.js (SPI delay limits and IRAM budget), pru_device_caps.js (per-SoC hardware widgets), block_control_flow_data.js.
- CRC16/CRC32: scale the table index to the entry size before the load (the byte offset does not fit the LBBO offset operand); 13 cycles - Access Lookup Table: scale the index for ushort/uint tables (7 cycles) - PRU GPI: build the bit mask in the result register so pins above 7 work; 3 cycles, 4-byte output - PRU SPI Read/Transfer: clear the result register before sampling so short packets are zero-extended; reserve the whole register for 17-24 bit packets; one extra overhead cycle - PRU SPI Read/Write/Transfer: refuse CS/data delays above 256 cycles, warn when the SPI blocks exceed the PRU IRAM budget - If/Else: an unconnected true/false branch is an error (the generated code would reference an undefined label) - Load Constant: optional explicit output width - Loop: reject a hardware LOOP nested in a finite loop body; add exported result blocks and accumulator bindings - Delay: document the exact cycle count and the nesting restriction - Memory Access: warn when two blocks share one auto-created Memory Variable - CRC, Lookup Table, Memory Variable, Group, If/Else: reject symbols that clash with clpru reserved identifiers - PRU GPI/GPO and SPI blocks: declare the pins they use (getPinUsage)
- tests/test_syscfg_add_instance.py: every block module adds an instance through the SysConfig CLI (skipped when SysConfig is not installed) - tests/test_fail_closed_gating.py: the device-capability gate fails closed on every degraded path - tests/test_shared_memvar_rename.py, tests/test_conditional_unconnected_branch.py - scripts/static_module_validate_runner.js, scripts/block_validate_runner.js, scripts/shared_memvar_runner.js: run block validators under node
… test The register allocator now releases a register once its last reader has run, so main.asm can no longer rely on a released register still holding its value. Name the registers once in main.asm, and keep the received frame live on the receiving core by reading it into an otherwise unused block output. Add a firmware test that runs the example on the simulator for both boards and both cores, checking the stored data byte and the CRC match/mismatch result.
…es not exist; neutral test wording
The MODE1 LSB loop advanced its bit counter in the low half, where the delay formulas assume none, so SCLK ran one cycle short high and one cycle long low. The MODE3 LSB loop sampled a 1 bit one instruction slower than a 0 bit, so its high half grew by a cycle on every 1. Both variants now count in the half the MSB variant counts in, and both sample paths take two instructions.
The Memory Variable block reserves buffers in .udmem (data RAM) and .usmem (shared RAM). With no rule for either section clpru placed them in instruction RAM after .text. Each example linker file now places .udmem in the data RAM its .bss uses and .usmem in PRU_SHAREDMEM, and a test checks every tracked linker file.
Behavioural peripheral models (perif/, core/device_model.py, pru_io/), the in-tool device bus and device models used by the Simulation Settings GPI "Attached Device Model" mode and the Device Model block, the device-binding manifest emitted into pru_syscfg.asm, and the TI pru-simulator run path (scripts/sim: ELF co-simulation, bound-device runner, Run on TI pru-simulator launcher). The static module gains the device-mode GPI fields, the device-bus preview, the pad check between attached devices and PRU outputs, and the TI pru-simulator result banner.
Tests for the device models and bindings, the Run on TI pru-simulator launcher, and regression tests of the shared allocator and existing blocks that run the generated firmware on TI pru-simulator (CRC/LUT index scaling, SPI zero-extension, loop nesting, register-pool exhaustion, loop-carried accumulators, load-constant width). A repository-level conftest reports tests that need a TI pru-simulator checkout as skipped, with the reason, when none is available.
…ck registrations only for blocks present
…whose device model the simulator lacks
…hey do not shadow the pru-simulator's; firmware test fixture; GUI simulator register tests; neutral wording
…ware that programs the IEP through c26
validate() passed a copy of the instance, with the companion pins defaulted from the bound block, to validateDeviceInput, which logged errors against that copy. SysConfig's report.logError throws "Unknown instance" for any object it does not own, so a device-mode GPI with a refused setting aborted validation, code generation and serialization. The effective pins are now passed separately and errors are logged against the real instance.
SysConfig display names are Title Case, at most 36 characters. The Device Model block now takes each field's display name from displayTitle() in the device model table: an explicit title where the label is a sentence, else the label converted word by word. The label itself stays the noun used in messages. The live web view settings get Title Case names and the long tooltip moves to the long description.
…dths in every mode and bit order Each read, write and transfer macro variant is built with the block's own delay components, run on TI pru-simulator against a model peripheral, and every SCLK phase inside the packet is checked, along with the word read.
New blocks with their palette entries on AM243x, AM64x and AM261x, and the shared code they need: per-block register pins in the allocator and the fixed-window reservations of XFR2PSI (R1-R5) and SUM32 accumulate (R2-R9), the global validators of the broadside blocks (BSWAP, Stitch FIFO, SUM32, XFR2TR, XFR2SPIN, XFR2PSI, BS RAM, FDB generic RAM), the PID state reservation and the SPI ADC sequencer CS idle level in the static module, and the regenerated block control-flow table. The new blocks use TI's typed input32/output32 data ports so they connect to the existing blocks.
| ` .if IS_PRODUCER == 1`, | ||
| ` ; Not fully macroizable dynamically because XOUT takes &Rn syntax natively.`, | ||
| ` ; The register allocator must substitute this safely if required, but XFR handles &R# syntax.`, | ||
| ` .endif`, |
There was a problem hiding this comment.
1. Scratchpad transfers do nothing 🐞 Bug ≡ Correctness
pru_spad_block generates a macro whose producer branch is empty and whose consumer path contains no instructions. Either transfer mode can be selected and emitted, but neither moves data through the scratchpad.
Agent Prompt
## Issue description
The scratchpad macro emits no transfer instructions for either mode.
## Fix Focus Areas
- .metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/pru_spad_block.syscfg.js[74-83]
## Recommended Fix
Generate the appropriate XIN/XOUT and handshake instructions for each supported mode, or reject modes that cannot yet be generated.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Left for a follow-up: the scratchpad macro body is still a stub; generating the XOUT/XIN and handshake needs a decision on how a macro takes a register window and a check of the handshake on hardware, so the block is not changed in this PR.
| ` .if IS_IEP == 1`, | ||
| ` lbco &TEMP_REG3, c26, 0x10, 4`, | ||
| ` sub VEL_REG, TEMP_REG3, TEMP_REG1`, | ||
| ` mov TEMP_REG1, TEMP_REG3`, | ||
| ` .endif`, |
There was a problem hiding this comment.
2. Pulse-count speed stays at zero 🐞 Bug ≡ Correctness
encoder_speed_block clears VEL_REG and writes it again only inside the IEP-specific assembler branch. Selecting the offered pulse-count method leaves velocity at zero while position and direction continue to update.
Agent Prompt
## Issue description
The selectable pulse-count mode never updates its velocity register.
## Fix Focus Areas
- .metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/encoder_speed_block.syscfg.js[68-118]
## Recommended Fix
Emit a pulse-count velocity calculation in the non-IEP branch, or remove and reject that mode until it is implemented.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Fixed in 7cea5dd: the pulse-count velocity method is removed from Encoder Speed because the macro never computed a velocity for it; only the IEP method is offered.
| ` qbbc cp_iw?, TEMP_REG1, 0`, | ||
| ` lbco &DATA_REG, c26, 0x20, 4`, | ||
| ` .else`, | ||
| ` .if MEASURE_TYPE == 0`, | ||
| `cp_pw_r?:`, | ||
| ` qbbc cp_pw_r?, R31, PIN`, |
There was a problem hiding this comment.
3. Internal captures read a gpio pin 🐞 Bug ≡ Correctness
capture_block sends every trigger source other than iep through its GPIO measurement branch. Selecting internal therefore polls the configured R31 pin rather than waiting for an internal trigger.
Agent Prompt
## Issue description
The internal capture option falls through to GPIO polling.
## Fix Focus Areas
- .metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/capture_block.syscfg.js[41-56]
- .metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/capture_block.syscfg.js[81-131]
## Recommended Fix
Add an internal-trigger generation branch, or reject and hide the option until that source is supported.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Fixed in 7cea5dd: the internal trigger source is removed from Capture Timer because it was emitted as GPIO polling; iep and gpio remain.
| .endif | ||
| .endif | ||
| .if (${mode} != 0) ; signal or waitThenSignal | ||
| ldi R31.b0, ${outputEvent} + 16 |
There was a problem hiding this comment.
4. A signaled event has the wrong number 🐞 Bug ≡ Correctness
event_block adds 16 when loading R31.b0, although outputEvent already contains the configured system-event number. For the accepted default of 16, generation signals 32 rather than the selected event.
Agent Prompt
## Issue description
Event signaling adds an offset to an already numbered system event.
## Fix Focus Areas
- .metadata/sysconfig/.meta/pru_blocks/program_control/event_block.syscfg.js[34-36]
## Recommended Fix
Load the validated event number directly into R31.b0 and test the default and range endpoints.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Not changed: R31.b0 for event generate is the strobe bit (0x20) plus the channel (system event - 16), which is system event + 16 for events 16..31 (event 20 -> 0x24, as in TI's icss_emac emac_MII_Rcv.h:620). The macro in event_block.syscfg.js now carries a comment saying so (5aff2d3) and a test pins the endpoints.
| { name: "numOfOutputPorts", default: 2, hidden: true }, // D, Q | ||
| { name: "output1Size", default: 8, hidden: true }, // D and Q are 8 bytes total | ||
| { name: "output2Size", default: 4, hidden: true }, |
There was a problem hiding this comment.
5. The second transform output is unwritten 🐞 Bug ≡ Correctness
iqmath_park_clarke_block declares two separately allocated output ports, but its macro signature has no operand for the second port. The transform loads its paired result through OUT1, so a graph consumer wired to output2 reads a register that the macro never populates.
Agent Prompt
## Issue description
The transform advertises a second output port without writing its allocated destination.
## Fix Focus Areas
- .metadata/sysconfig/.meta/pru_blocks/data_handling/iqmath_park_clarke_block.syscfg.js[18-49]
- .metadata/sysconfig/.meta/pru_blocks/data_handling/iqmath_park_clarke_block.syscfg.js[155-158]
## Recommended Fix
Give the second port an explicit macro destination and write its value there, keeping output widths consistent with the allocator.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Fixed in c21f0ac: the second port now has its own macro destination (OUT2); D/Alpha is written to OUT1 and Q/Beta to OUT2 for both the forward and inverse transforms.
| { name: "constant4", hidden: true, default: 0, getValue: (inst) => { | ||
| let addr = parseInt(inst.addr7, 16); | ||
| if (isNaN(addr)) addr = parseInt(inst.addr7); | ||
| return (addr << 1) | (inst.rw === 'read' ? 1 : 0); | ||
| }}, |
There was a problem hiding this comment.
15. I2c accepts addresses wider than 7 bits 🐞 Bug ≡ Correctness
The I2C validator checks only that the SCL and SDA pins differ. addr7 values above 0x7F pass, and (addr << 1) | rw then builds a 9-bit or wider address byte. The frame sent on the bus then carries an address and R/W bit different from what the user configured, and nothing reports it.
Agent Prompt
## Issue description
I2C addr7 is not range-checked.
## Fix Focus Areas
- .metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/i2c_block.syscfg.js[104-106]
## Recommended Fix
Parse addr7 the same way as at lines 65-66 and log an error if it is NaN or outside 0..0x7F.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Fixed in dbeec95: the I2C validator now rejects an addr7 that is not a number or is outside 0x00..0x7F, so the address byte can no longer be wider than 8 bits.
| { name: "filterChannel", displayName: "Filter Channel", default: 0, options: [{name: 0}, {name: 1}, {name: 2}, {name: 3}] }, | ||
| { name: "osr", displayName: "Over Sampling Ratio (OSR)", default: 64 }, | ||
|
|
There was a problem hiding this comment.
17. Sdfm oversampling setting is ignored 🐞 Bug ≡ Correctness
The SDFM block exposes osr, but the value is never validated, never exported as a constant, and never passed to the macro, which takes only RES, CHANNEL. Changing the oversampling ratio has no effect on the generated firmware.
Agent Prompt
## Issue description
The SDFM `osr` config does not affect the generated code.
## Fix Focus Areas
- .metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/sdfm_block.syscfg.js[14-49]
## Recommended Fix
Pass osr as a constant and program the SDFM OSR register with it, or remove or hide the option.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Fixed in 7cea5dd: the oversampling-ratio option is removed from the SDFM block, since the macro only reads the result and never programmed it.
| if (inst.maxSpeedSps <= 0) validation.logError("maxSpeedSps must be > 0", inst, "maxSpeedSps"); | ||
| if (inst.maxSpeedSps > 0 && requestedPeriodCycles(inst) < MIN_STEPPER_PERIOD_CYCLES) | ||
| validation.logError(`maxSpeedSps requires a period of at least ${MIN_STEPPER_PERIOD_CYCLES} PRU cycles (maxSpeedSps <= clkHz / ${MIN_STEPPER_PERIOD_CYCLES})`, inst, "maxSpeedSps"); | ||
| if (inst.numSteps <= 0) validation.logError("numSteps must be > 0", inst, "numSteps"); |
There was a problem hiding this comment.
18. Stepper step counts above 65535 are accepted 🐞 Bug ☼ Reliability
The stepper validator checks only numSteps > 0, but the macro loads the count with `ldi TEMP_REG2.w0, STEPS`, which holds at most 16 bits. Larger counts pass validation and then fail in the assembler, or get truncated. The quadrature block has the same gap: sampleCycles goes into ldi TEMP_REG2.w0 with only a lower-bound check.
Agent Prompt
## Issue description
numSteps has no upper bound even though it is loaded into a 16-bit register.
## Fix Focus Areas
- .metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/stepper_block.syscfg.js[96-96]
- .metadata/sysconfig/.meta/pru_blocks/pru_io_blocks/quadrature_block.syscfg.js[73-73]
## Recommended Fix
Require whole numbers from 1 to 65535 for numSteps and sampleCycles, or switch the loops to 32-bit registers loaded with ldi32.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Fixed in dbeec95: numSteps (stepper) and sampleCycles (quadrature) must now be whole numbers from 1 to 65535, matching the 16-bit register half they are loaded into.
| const pin = parseInt(p.pin); | ||
| if (isNaN(pin)) { | ||
| problems.push(`${name}: unparseable ${p.dir} pin "${String(p.pin)}"`); | ||
| continue; | ||
| } | ||
| claims.push({ inst: idx, block: name.split('/').pop(), iname: typeof inst.$name === 'string' ? inst.$name : `#${idx}`, dir: p.dir, pin, bidi: p.bidi === true, cfg: inst }); |
There was a problem hiding this comment.
19. Pin checker accepts malformed pin values 🐞 Bug ≡ Correctness
extractClaims turns pin strings into numbers with parseInt(p.pin) and rejects only NaN. Values such as "3junk" become pin 3, and "-1" is accepted as a pin. This goes against the script's stated job of reporting malformed pins, and can produce false collisions or hide bad configurations.
Agent Prompt
## Issue description
Pin values are parsed with parseInt and only NaN is rejected.
## Fix Focus Areas
- scripts/check_pin_collisions.js[240-245]
## Recommended Fix
Accept only strings matching /^\d+$/ whose value is within the valid R30/R31 bit range, and report anything else as malformed.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Fixed in 6051e21: pins are accepted only as whole numbers 0..31 (number or digit-only string); "3junk", "-1", "32" and "3.5" are now reported as unparseable pins.
| print("REFUSED: manifest has no device bindings. An empty manifest is REFUSED.", file=sys.stderr) | ||
| return EXIT_REFUSED | ||
|
|
||
| sim_dir = find_pru_simulator() |
There was a problem hiding this comment.
20. Missing simulator crashes the device runner 🐞 Bug ☼ Reliability
run_bound_devices.py calls find_pru_simulator() outside its refusal handling, allowing SimulatorNotFound to escape with a traceback instead of a refusal diagnostic. When neither simulator path is present, the runner exits with code 1 rather than its documented REFUSED code 2, so callers that distinguish refusal from failure by exit code misclassify it.
Agent Prompt
## Issue description
A missing simulator checkout lets `SimulatorNotFound` escape `run_bound_devices.py` instead of producing its refusal verdict.
## Fix Focus Areas
- scripts/sim/run_bound_devices.py[347-361]
- scripts/sim/elf_cosim.py[95-113]
## Recommended Fix
Catch `SimulatorNotFound` at the `find_pru_simulator()` lookup in `run_bound_devices.py`, print the reason as a refusal diagnostic, and return `EXIT_REFUSED`.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Fixed in 2819fa3: a missing simulator checkout now prints a REFUSED diagnostic and exits with the refusal code instead of a traceback.
The option was not recognised in the argument loop, so its name was taken as the block path or the instance JSON and the file was never read. It is now parsed with its path, read and parsed with a clear error on failure, and listed in the usage text.
getDevicePadSharing returned an empty set when the device data had neither a usable runtime pin interface nor pin common info, which the validator read as "no pad is shared" and so emitted neither a collision warning nor the "could not be checked" warning. It now returns null in that case, keeping an empty set only when available data holds no shared pad.
…ry ports A multi-output macro reserved the full width for output2 and up but emitted only the first register of the pair, and consumers wired to such a port read only that register, so a 64-bit second result lost its upper half. The macro destination and the consumer's source token now carry both registers, as output1 already did.
TEMP_REG3 and TEMP_REG4 alias R26 and R27, which were still in the allocatable pool, so a macro writing them could overwrite a live value or its own result. Before allocation the pool now drops R26 and/or R27 when any block's macro text names TEMP_REG3 and/or TEMP_REG4; designs that use neither keep the full 28-register pool. The summary comments list the scratch registers in use and the out-of-registers error mentions the bytes held for them.
…zero counts-per-turn setting The Python quadrature encoder and its independent decoder stepped B first on forward motion, the opposite of the in-tool model, so the same motion counted in opposite directions in the two simulators. Forward motion now rises A first in both. A counts-per-turn of zero is recorded as a fault as before, but the index check no longer divides by it, so the run is not aborted.
… 8b/10b running disparity A second correctly timed frame in the same run was counted on top of the first frame's clocks, reported as overclocked and produced no frame event. The model now treats a clock idle for frame_gap_cycles as the end of a frame, reports it and starts the next one from clean state. Line coding always used the negative-disparity code of every byte, so repeated bytes with unbalanced codes drifted in disparity. The encoder now selects the RD- or RD+ code, including the alternate D.x.A7 symbol, and carries the running disparity across the symbols of a frame; the decoder accepts both columns.
…POSIX The wrapper started its commands in the parent's session and only killed the direct child on POSIX, so the compiler or simulator started by gmake kept running after a timeout while the log said the process tree was terminated. Commands now lead their own session, the group is signalled, and the child is reaped.
…device runner main() called the simulator lookup outside its refusal handling, so a missing checkout ended in an uncaught traceback and exit code 1. It now prints a REFUSED diagnostic and returns the refusal exit code.
Each symbol was decoded at an offset counted from the start of the file, so names, values and sizes came from the ELF header and the bytes after it. The offset now starts at the symbol table section.
The GUI core rounded the count up to whole registers and ignored the byte or word offset of the operand, so zero &R2.b2, 1 cleared all of R2. The headless simulator called a register-reference parser it does not define and threw a TypeError on either instruction. Both now start at the operand's byte, change exactly the requested number of bytes and stop after R31.
…GUI core The headless timer recognised count, status and compare at the offsets of an older timer layout (0x0C, 0x44, 0x48/0x4C), counted unconditionally and reset on any compare at or above CMP0, so firmware using the generated timer code (GLOBAL_CFG 0x00, COUNT 0x10/0x14, CMP_CFG 0x70, CMP_STATUS 0x74, CMPj at 0x78 + 8j) read and wrote different registers and saw compare hits it had not enabled. The map, the 64-bit count, CNT_ENABLE and DEFAULT_INC, CMP_EN, equality compare and CMP0_RST_CNT_EN now match the GUI core. LBCO and SBCO also read the register operand through fields the parser does not return, so register-sourced stores moved no data; they now use the parsed register number and byte offset.
With devices attached, R30 started at the bus idle word but R31 stayed zero until the first bus settlement after an instruction, so firmware that read R31 in its first instruction saw zero instead of the level the devices and pull-ups give the idle outputs. The bus is now settled once at startup and its result seeds R31 and the R31 history.
The frame mask, Gray conversion and bit extraction used 32-bit JavaScript bitwise operators, so a 32-bit frame was masked to zero and wider frames (the table accepts up to 64 bits) had their shift counts wrapped. The position is now latched and shifted as a BigInt.
buildDeviceBindings wrote the raw clock and index fields, mapping the unset value to null, while validation, the pin-conflict check and the in-tool simulator use the pins defaulted from the bound block. A clocked device whose clock came from its block therefore reached the Python binding without a clock pin and was refused. The manifest now carries the same effective pins.
The I2C block accepted a 7-bit address above 0x7F, so the address byte built as (addr << 1) | rw grew past 8 bits and the frame on the bus carried a different address than the one configured. addr7 is now checked to be 0x00..0x7F. The stepper step count and the quadrature sample delay are loaded with ldi into a 16-bit register half. Larger or fractional values passed validation and then failed to assemble or were truncated. Both now require a whole number from 1 to 65535.
encoder_speed offered a pulse-count velocity method, but the macro only writes the velocity register in the IEP branch, so selecting it left the velocity at zero. capture offered an internal trigger source that fell through to the GPIO polling branch. sdfm exposed an oversampling ratio that was never passed to the macro. Remove these three options so the generator only offers behaviour it emits.
The AM261x (ICSSM) palette listed the XFR2PSI block twice, but the block requires the xfr2psi widget and the AM261x capability table does not have one, so every instance failed device validation. Remove both entries; the AM64x and AM243x palettes keep the block.
validateXfr2psiGlobal reported every value live across an XFR2PSI transfer as sitting in R3, without looking at where the allocator put it. The allocator already keeps R1:R5 out of the pool when XFR2PSI is present, so valid designs were rejected. The validator now reads the producer's allocation and reports only a value that lies in R1..R5. validateXfr2spinGlobal counted try_acquire as an acquire when checking that a cross-core writer is protected by a lock. try_acquire makes one attempt and execution continues whether or not the lock was won, so it cannot protect the write. Only acquire now counts for that check; acquire/release pairing still accepts try_acquire. The check is driven through a new runner that loads the shipped static module.
…ster The block declared two output ports but its macro had a single destination that received an 8-byte load, so anything wired to the second port read a register the macro never wrote. Both ports now get their own destination (D/Alpha in OUT1, Q/Beta in OUT2) and the macro loads each into its register.
Clear Event After Wait defaulted to on but only loaded R0 with the input bit and never acknowledged anything, overwriting R0 on the way. The option now defaults to off, the validator rejects it with an explanation, and the macro no longer touches R0. The signal value (system event + 16 = strobe bit plus channel) is documented and covered.
…ary registers The macro always programmed and polled compare 0 registers while the wait tested the selected compare's status bit, so one-shot compares 1..15 waited forever; it now derives the compare register offset, CMP_CFG enable bit and status clear mask from the compare index. The capture sub-function emitted nothing and is now rejected by the validator. Scratch values use the temporary registers instead of R0-R2.
…very task id timerSource external was selectable but the generated dispatch always reads the IEP counter, and only task1_id was range checked. The validator now refuses anything but the IEP timer and applies the 0..31 or 255 rule to all four task ids.
…egister The delay loop counted down in R0, which the register allocator can assign to a live value. It now uses the temporary register reserved for block macros.
The runner accepted --elf on its own, but the ELF loader only records data and symbols and the simulator executes assembly text, so the run failed later with a no-instructions error. The missing-instructions check now applies whether or not an ELF is given and says what --elf provides.
… numbers Pin strings went through parseInt and only NaN was rejected, so "3junk" was read as pin 3 and "-1" or "32" were accepted as pins. A pin must now be a non-negative integer or a string of digits within 0..31; anything else is reported as an unparseable pin.
The compiled PRU firmware decodes transition 0 -> 1 as forward motion (B rises before A). The Python encoder and decoder models, along with the in-tool JavaScript simulation phase table, now consistently follow this B-leading convention. Forward motion advances positive count across both simulation environments and agrees with firmware cosimulation.
Summary
Builds on the device-model / simulator pull request. Adds 45 blocks with their validators, tests and test helpers.
New blocks
stitch_fifo_block, sum32_block, svpwm_block, trigger_router_block
fdb_gp_ram_block, hall_block, i2c_block, i3c_sdr_initiator_block, mailbox_block, manchester_block,
mdio_master_block, multi_axis_motion_block, onewire_block, parallel_bus_block, pdm_block, pru_spad_block, pwm_block,
quadrature_block, queue_empty_block, safety_dual_monitor_block, sdfm_block, sent_block, serdes_block, servo_block,
spi_adc_sequencer_block, stepper_block, three_phase_pwm_block, ws2812_block, xfr2psi_block, xfr2spin_block,
xfr2tr_block
Each block has a hidden
blockMaturityfield (metadata-only, assembles, simulator-tested, dual-oracle-tested,hardware-validated) that records how far it has been checked; nothing reads it at generation time, and no block in
this pull request is
hardware-validated.Changes to TI-owned shared files
common/pru_blocks_static_module.syscfg.js(+953 lines): global validators for BSWAP, XFR2TR (ring vs submitsize), XFR2SPIN (lock pairing, shared-variable protection), SUM32 (R2-R9 window), XFR2PSI, and pin usage.
The XFR2SPIN cross-core writer registry (
CROSS_CORE_WRITER_BLOCKS) is empty here; the application-blocks pullrequest registers
ipc_shared_mem_block, so the cross-core checks have no writer to act on until then.common/register_allocation/pru_register_allocator.js:PINNED_REGISTER_FIELDSfor 15 blocks whose macros usefixed registers; R1-R5 reserved for XFR2PSI and R2-R9 for SUM32 accumulate.
common/device_model_table.js,common/block_control_flow_data.js: entries for the new blocks.soc/pru_blocks_am243x.syscfg.js,pru_blocks_am64x.syscfg.jsandpru_blocks_am261x.syscfg.js.check_pin_collisions.js(command-line pin check, with an empty baseline: it reports nothingfor TI's 39 example designs),
check_syscfg_arity.js,check_syscfg_schema.js,emit_all_blocks.js,dual_oracle_harness.js,cosim/reference scripts for PID, SVPWM and three-phase PWM.
Notes on specific blocks
port, macro
m_mailbox_run_wide); 32-bit blocks read it through the Data Splitter block.config/constants_am243x.cfgwhile its IEP model sits at 0x0002E000, so the timing tests map c26 to the IEP(
elf_cosim.IEP_CONSTANTS), as on the device, and skip when the checkout has no IEP model. They also run the IEP atthe core clock: the counted and the single-call runs are compared cycle for cycle, and with an IEP clocked
separately (200 MHz IEP, 250 MHz PRU, as one pru-simulator branch models) an edge that waits on a compare event
moves by the IEP-to-core phase at which its period starts (4 cycles on one three-phase edge). That is a property of
the clocking, not of the counted mode; with the clocks equal both simulators tried give identical edges.
observation on LP-AM261; it is not stated in the TRM and no measurement log is part of this change.
How it was tested
Tools: node; SysConfig 1.28 (
SYSCFG_PATH); TI PRU CGT 2.3.3 (CGT_TI_PRU_PATH); TI pru-simulator checkout(
PRU_SIMULATOR_PATH, main branch544d5ea).PRU_SIMULATOR_PATH: 781 passed, 0 failed, 32 skipped;PRU_SIMULATOR_PATH: 447 passed, 0 failed, 366 skipped;PRU_SIMULATOR_PATH, gmake on PATH and a repository-rootimports.mak: 812 passed, 0 failed, 20 skipped;check_syscfg_arity.js: 69 modules checked, 0 unexpected failures.The 32 skips with every tool present: 18 device-binding cases whose model is not in pru-simulator main (see the
previous pull request), and 14 cases that need
gmakeand a repository-rootimports.mak(
test_group_entry.py5,test_spi_cs_timing_elf.py4,test_board_variant_builds.py3,test_memory_placement.py2), which are not part of a default checkout. With gmake on PATH and animports.makthey run; the 20 skips that remain are the 18 device-binding cases and 2
test_group_entry.pycases (pru-simulatormain has no PRU_CTRL model for c28).
test_memory_placement.py(3 passed) builds theexamples/emptydesign with aMemory Variable in shared RAM and checks the linked map:
.usmemlies inPRU_SHAREDMEM(0x10000) with the linkerrules of the shared-infrastructure pull request, and lands in instruction RAM after
.textwith those rulesremoved.
SysConfig Sanity Tests: the eight contexts of
make syscfg-tests-am243x,syscfg-tests-am64xandsyscfg-tests-am261x, each run as the makefile runs it with SysConfig 1.27.0:Total 0 error(s)and no "failed"line in every context. Display names of the new blocks follow SysConfig's convention (Title Case, at most 36
characters; configuration names are unchanged), tooltips are at most 200 characters with the detail in the long
description.
Known limitations
xfr2tr) are checked against the TRM and in simulation only.
🤖 Generated with Claude Code