Skip to content

Core PRU blocks: data handling, program control, PRU I/O and utilities - #24

Open
pratheesh wants to merge 82 commits into
TexasInstruments:mainfrom
pratheesh:feat/core-blocks
Open

pratheesh wants to merge 82 commits into
TexasInstruments:mainfrom
pratheesh:feat/core-blocks

Conversation

@pratheesh

Copy link
Copy Markdown

Stacked pull request. Depends on #22, #23. GitHub cannot base a pull request on another fork branch, so until they merge the diff here also contains their commits. The 24 commits of this pull request alone: feat/device-models-simulator...feat/core-blocks. It will be rebased onto main as the earlier ones merge.

Summary

Builds on the device-model / simulator pull request. Adds 45 blocks with their validators, tests and test helpers.

New blocks

  • data_handling: bs_ram_block, bswap_block, iqmath_park_clarke_block, iqmath_pid_block, mac_hw_block,
    stitch_fifo_block, sum32_block, svpwm_block, trigger_router_block
  • program_control: event_block, scheduler_block
  • pru_io_blocks: can_classic_decoder_block, capture_block, counter_block, dshot_block, encoder_speed_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
  • utils: iep_block, logic_analyzer_block

Each block has a hidden blockMaturity field (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 submit
    size), 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 pull
    request 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_FIELDS for 15 blocks whose macros use
    fixed 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.
  • Palettes in soc/pru_blocks_am243x.syscfg.js, pru_blocks_am64x.syscfg.js and pru_blocks_am261x.syscfg.js.
  • Scripts: check_pin_collisions.js (command-line pin check, with an empty baseline: it reports nothing
    for 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

  • mailbox_block: a consume with Bytes per slot above 4 is a two-register output in TI's 64-bit convention (output64
    port, macro m_mailbox_run_wide); 32-bit blocks read it through the Data Splitter block.
  • pwm_block / three_phase_pwm_block: the IEP is programmed through c26. pru-simulator main maps c26 to 0x00010000 in
    config/constants_am243x.cfg while 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 at
    the 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.
  • pwm_block: Deadband (ns) is accepted but not inserted between the main and complementary edges.
  • three_phase_pwm_block: the smallest half-bridge dead gap is an observed value pinned by tests, not a guarantee.
  • mac_hw_block: one instruction between the last operand write and XIN 0 / XOUT 0 is enforced. This follows a bench
    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 branch 544d5ea).

python -m pytest tests scripts/sim -q -p no:cacheprovider
for t in scripts/sim/test_*.js scripts/test_*.js; do node "$t"; done
node scripts/check_syscfg_arity.js
  • with SysConfig, clpru and PRU_SIMULATOR_PATH: 781 passed, 0 failed, 32 skipped;
  • without PRU_SIMULATOR_PATH: 447 passed, 0 failed, 366 skipped;
  • with PRU_SIMULATOR_PATH, gmake on PATH and a repository-root imports.mak: 812 passed, 0 failed, 20 skipped;
  • 17 node test scripts: all pass; 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 gmake and a repository-root imports.mak
(test_group_entry.py 5, test_spi_cs_timing_elf.py 4, test_board_variant_builds.py 3,
test_memory_placement.py 2), which are not part of a default checkout. With gmake on PATH and an imports.mak
they run; the 20 skips that remain are the 18 device-binding cases and 2 test_group_entry.py cases (pru-simulator
main has no PRU_CTRL model for c28). test_memory_placement.py (3 passed) builds the examples/empty design with a
Memory Variable in shared RAM and checks the linked map: .usmem lies in PRU_SHAREDMEM (0x10000) with the linker
rules of the shared-infrastructure pull request, and lands in instruction RAM after .text with those rules
removed.

SysConfig Sanity Tests: the eight contexts of make syscfg-tests-am243x, syscfg-tests-am64x and
syscfg-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

  • No block here is verified on silicon. The broadside blocks (bs_ram, bswap, fdb_gp_ram, sum32, xfr2psi, xfr2spin,
    xfr2tr) are checked against the TRM and in simulation only.

🤖 Generated with Claude Code

…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.
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.
…hey do not shadow the pru-simulator's; firmware test fixture; GUI simulator register tests; neutral wording
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.
Comment on lines +78 to +81
` .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`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

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

Dismiss ↗ | View ↗

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +93 to +97
` .if IS_IEP == 1`,
` lbco &TEMP_REG3, c26, 0x10, 4`,
` sub VEL_REG, TEMP_REG3, TEMP_REG1`,
` mov TEMP_REG1, TEMP_REG3`,
` .endif`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

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

Dismiss ↗ | View ↗

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +93 to +98
` 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`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

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

Dismiss ↗ | View ↗

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

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

Dismiss ↗ | View ↗

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +19 to +21
{ 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 },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

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

Dismiss ↗ | View ↗

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +64 to +68
{ 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);
}},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

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

Dismiss ↗ | View ↗

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +14 to +16
{ 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 },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

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

Dismiss ↗ | View ↗

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

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

Dismiss ↗ | View ↗

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread scripts/check_pin_collisions.js Outdated
Comment on lines +240 to +245
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 });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

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

Dismiss ↗ | View ↗

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread scripts/sim/run_bound_devices.py Outdated
print("REFUSED: manifest has no device bindings. An empty manifest is REFUSED.", file=sys.stderr)
return EXIT_REFUSED

sim_dir = find_pru_simulator()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

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

Dismiss ↗ | View ↗

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants