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.
…g checks The receive path did not decode a UART character: it treated each valid sample as a whole byte and looped once per byte; it wrote the 10-bit receive frame size into the transmit field (CHn_CFG0 bits 15:11) instead of bits 27:16; it read the data byte from R30, which is the output register; it tried to clear the valid flag by toggling the receive enable in R30 instead of writing a one to the flag in R31; and it ignored the parity bit. The receiver now runs at 8x oversampling and is re-armed for every character. For each of the 11 bits of a character it waits for the channel's valid flag, reads the middle sample from the channel's R31 byte, clears the flag by writing its R31 bit, and shifts the bit in. The start, stop and parity bits are checked and reported in a new status output (bit 0 parity error, bit 1 framing error); the data bytes are returned in output1 with the first byte most significant. rxBytes is limited to 0..4, the width of that output, and the RX clock divider is passed as a new macro argument. Simulator tests drive the channel with an IO-Link device model for both cores and all channels, including wrong parity and stop bits, and a SysConfig-generated design is checked to call the macro with matching arguments and to assemble.
The turnaround used a fixed 174-iteration inner loop for every COM mode, and that count was not even one bit period at COM3: the delay was about 7 us whatever the rate, instead of TURN_BITS bit periods (21.7 us at COM3, 1 ms at COM1). The inner loop count is now derived from the TX clock divider: one bit period is (CLK_DIV + 1) * 25 / 48 two-cycle iterations at the 200 MHz PRU clock. A simulator test measures the time from the end of the last transmitted character to the receiver being armed for COM1, COM2 and COM3.
Validation only required singleTurnBits + multiTurnBits <= totalWidth, so a 25-bit payload in a 25-bit frame with parity enabled was accepted: the macro takes the parity bit from the low end of the frame and shifts it out before the Gray decode, leaving 24 payload bits in a 25-bit configuration. The payload must now fit in totalWidth minus one bit for an enabled parity bit and one for an enabled error bit. The error names the payload bits available.
The macro wrote the parity and error-bit result only into TEMP_REG2.b0, a scratch register the register allocator can hand to any following block, while the block declared the captured frame as its only output. Connected blocks had no value with which to tell a bad reading from a good one. The block now has a second output, STATUS (0 = ok, 1 = parity error, 4 = error bit active, 5 = both), allocated like the frame register and passed to the macro as its second argument; the macro writes the result there instead of into a temporary register. The generated-firmware tests read the STATUS register from the generated call and check it for a good frame and for frames with a wrong parity bit, an active error flag and both.
The IPC block checked the variable name only against the clpru reserved names, and that helper (like the memory reservation and the undefined-symbol check) skips an empty value. A cleared Variable Name therefore passed validation and reached the assembler as an empty macro operand. The block now reports an error unless the name is a non-empty identifier (letter or underscore, then letters, digits and underscores), the same rule the memory variable block applies to its label.
The Park/Clarke macro now loads D into OUT1 and Q into OUT2 with a load each, so its default expansion is 100 instructions instead of 99.
…lders buildDeviceBindings resolves the effective companion pins through devicePinDefaults; the test that evaluates the extracted builders must define it.
READER_BLOCKS listed only the BiSS-C and SSI initiators although the EnDat encoder kind already has a clock-driver rule for the EnDat initiator. A bound EnDat encoder therefore had no reader record in the manifest and no reader in the Device Model display even when an EnDat initiator read its data pin.
…ecks The frame-length guards were written as if QBLE/QBGT compared the register against the operand, but the PRU compares the operand against the register. As generated, every frame of three or more bytes was reported as too short and the buffer store was skipped for the first 254 bytes. Use QBGE and QBLT so frames shorter than four bytes report status 3 and bytes are stored while the buffer has room.
The CRC lookup used the raw table index (0..255) as the LBBO byte offset although the reflected CRC-16 table has 2-byte entries, so any non-zero index read the wrong entry and valid frames were reported as CRC errors. Double the index into a 16-bit offset before the lookup.
…register Receive setup wrote all four bytes of the ENDAT channel CFG0 register, so a paired UART transmitter on the same channel lost its frame size and other transmit settings. Write only bytes 2-3 (receive frame size and bit swap), as the UART configuration block does.
The receiver programmed and shifted in 10-bit characters (start, 8 data, stop) while the silence threshold is computed for 11-bit Modbus characters. With the parity bit (8E1/8O1) or second stop bit (8N2) left unread, the next character started one bit early and the data drifted out of alignment. Program an 11-bit frame size and align the shifted-in character to it; the extra bit after the data is consumed and not decoded (parity is not checked).
t1Clocks other than the two-pulse lead-in produced a command the encoder rejects, and trecCycles between 500 and 999 let the block return before the 10 us recovery time (the loop takes two cycles per iteration), yet both only logged a warning. Report them as errors. The tests that deliberately put such values on the wire now relax that single check in a scratch copy of the block.
…dget The cycle estimate charged the start-bit hunt as a single clock pulse, but a late or absent encoder makes the macro clock up to timeoutCycles pulses before it gives up, so the reported budget was about 99 pulses short in that case. Charge timeoutCycles pulses and update the default to match.
The gateway macro defined sg_tx_turn, sg_tx_done and sg_rx_phase as plain labels, so a second instance in one design defined each symbol twice and the assembler rejected the program. Use '?' local labels like the other blocks.
When a transmit command was pending, the macro loaded MB_SLOT_BYTES bytes starting at TEMP_REG3. That overwrote the mailbox base kept in TEMP_REG3, so the read pointer was stored at an address taken from payload data, and with the default 32-byte slot the load also reached scratch registers and R30/R31. Nothing uses the payload yet because UART transmit is not implemented, so drop the load; the read pointer is now stored at base + 4 and R30/R31 are untouched.
The receive publish stored MB_SLOT_BYTES (up to 32) bytes starting at the single 32-bit status register, so every publish copied whatever registers the allocator had placed after it into the host's mailbox slot. Store the four status bytes only.
…on and recognize NEXT/PREV sequence ports portsOf() called ports() with a proxy whose properties convert to empty strings, so loops bounded by numOfInputPorts/numOfOutputPorts produced no data ports, and inferExecution() only recognised IO_NEXT/IO_PREV. Blocks such as SPI Read therefore listed zero data ports and an unclassified execution model. Evaluate ports() against the module's default configuration, treat NEXT/PREV like IO_NEXT/IO_PREV, and regenerate the reference.
The Data Setup Time setting is a 0-10000 ns value converted to PRU cycles, but the parameter tables still listed it as 0-100 PRU cycles and the setup/hold section called it a cycle count. Describe it in nanoseconds like CS Setup and CS Hold.
…blocks implement The MAC guide listed a Signed/Unsigned mode option and a 64-bit accumulator; the block has no mode option, selects multiply-only mode and adds the accumulator in the ALU on the lower 32 bits. The PID guide gave Kd a default of 0.05, listed a State Variable Base setting and 0-16 gain ranges that do not exist, and applied Ki to the error before integrating; the macro integrates the raw error and applies Ki to the integral. Document the actual defaults, the per-instance state symbol and the formulas.
The guide gave a fixed 8-cycle period for every bit; the block takes 8 cycles for a sampled 1 and 7 for a sampled 0, as its description says.
…ult use The tests fall back to sysconfig_1.28.0 under TOOLS_PATH, while imports.mak.default sets SYSCFG_PATH to sysconfig_1.26.2; say so and tell users to point SYSCFG_PATH at the same installation.
…rovide The Park/Clarke guide promised three phase outputs in inverse mode; the block has two output ports, so the guide now describes the two outputs and how to derive the third phase. The SDFM guide described a configurable oversampling ratio and a register offset the block does not use; the block reads one result register per channel from an assumed base address.
SysConfig now rejects an empty variable name and one that is not a plain identifier; say so in the parameter table.
Refresh the generated pages for the block option, port and description changes from the lower layers.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Builds on the application-blocks pull request. Adds user documentation for the blocks and tools of the previous four
pull requests, a generated block reference, README additions, and the tests that check documentation against the
code.
What changes
docs/**user guides (input/output, data handling, program control, utilities, simulation: using the simulator,device bindings, Simulation Settings) and
docs/readme.md.docs/block-reference/**, generated bynode build_block_docs.js: a user guide and a design page for each of the76 blocks, an index and a portability page. Nothing else is generated (no test-report or test-spec pages).
tests/test_block_docs_fresh.pyregenerates into a temporary directory and fails if the committed pages differ,if a relative link is dead or if a line has trailing whitespace.
README.md: PRU GPI is 3 cycles (was 1), and a "Running the tests" section (commands and theSYSCFG_PATH,CGT_TI_PRU_PATHandPRU_SIMULATOR_PATHvariables).docs/readme.md: the "Configure Constant Table", "PRU LBCO" and "PRU SBCO" rows are removed; no such blocks existin the repository. The PRU GPI row says 3 cycles, as the block does.
pru_spi_read,pru_spi_write,pru_spi_transfer): Data Setup Time is counted inside the SCLK half itprecedes, so it adds no program memory; a maxed controller block links to 2,888-2,904 bytes of
.text(a one-blockdesign with a Load Constant and a PRU GPO, across the three blocks and four SPI modes) and six of them to
17,228-17,300 bytes, measured with clpru 2.3.3.
gmake simruns the generated firmware on TI pru-simulator.test_delay_cycles.py,test_three_phase_scheduling.py,test_spi_cs_idle_prologue.py,test_pwm_jitter_doc.py,test_device_capability_gating.py,test_device_model_companion_conflicts.py.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, withoutimports.mak: 2788 passed, 0 failed,118 skipped;
PRU_SIMULATOR_PATH: 2349 passed, 0 failed, 557 skipped;PRU_SIMULATOR_PATH, gmake on PATH and a repository-rootimports.mak: 2860 passed, 0 failed, 65 skipped(the 65 are the device models and the PRU_CTRL model pru-simulator main does not ship, as in the application-blocks
pull request); this includes every case of
test_spi_cs_idle_prologue.py;examples/**/ti-pru-cgt/makefileprojects) builds withgmake all(SysConfig 1.28, clpru2.3.3,
DEVICEset to the project'sEXPECTED_DEVICE): 39 of 39.test_three_phase_scheduling.pymaps c26 to the simulator's IEP model and runs the IEP at the core clock (thedocumented edge offsets are in cycles = IEP counts): 34 passed on pru-simulator main.
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. The block reference shows the Title Case display names of the blocks.
Known limitations
🤖 Generated with Claude Code