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.
|
/agentic_describe |
|
/agentic_review |
PR Summary by QodoAdd device models and firmware-backed PRU simulation
AI Description
Diagram
High-Level Assessment
Files changed (145)
|
| if getattr(model, "nets", None) is None: | ||
| raise DeviceRefused( | ||
| f"device kind {entry['kind']!r} " | ||
| f"({type(model).__module__}.{type(model).__name__}) uses the " | ||
| f"pru_device_models.device_model contract with no `nets`; TI pru-simulator's " | ||
| f"device bus cannot attach it, so gmake sim does not support " | ||
| f"this kind yet") |
There was a problem hiding this comment.
1. Four selectable devices cannot run simulations 🐞 Bug ≡ Correctness
attach_bound_devices() rejects every model without nets, including the repository-native SPI ADC, step/dir, WS2812 and MDIO models offered in device configuration. Selecting any of these kinds for a bound-device run raises DeviceRefused before the model is attached, so neither the headless run nor the live view can simulate it.
Agent Prompt
## Issue description
Four selectable repository-native device kinds are rejected by the bound-device runner because they implement the `tick()` contract rather than `nets`.
## Fix Focus Areas
- scripts/sim/run_bound_devices.py[174-200]
- scripts/sim/device_bindings.py[178-200]
- pru_device_models/device_model.py[4-15]
## Recommended Fix
Adapt these models to the simulator's pin-bus contract or add an attachment path that drives the bus using their `tick()` interface. Keep the selectable kinds and supported runtime kinds consistent, and test each through the bound-device runner.
ⓘ 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: these four models use the tick() contract, not per-pin nets, so the bound-device runner refuses them by name (scripts/sim/run_bound_devices.py:178-190) rather than attach them wrongly. Running them on the TI simulator needs a bus adapter that states which pins each model drives; that is a design change we have not made yet.
| self._clocks_in_burst += 1 | ||
|
|
||
| if self._clocks_in_burst > len(self._wire_bits): | ||
| if not self._overclock_flagged: |
There was a problem hiding this comment.
2. Later biss frames fail as overclocking 🐞 Bug ≡ Correctness
BissLineEncoder.tick() increments _clocks_in_burst on every rising edge without resetting it for a subsequent frame, while _frame_emitted remains set after flush(). A second correctly timed transaction in the same run exceeds the first frame's clock count, records an overclock fault and produces no second frame event.
Agent Prompt
## Issue description
The BiSS line model retains its first frame's clock count and emitted flag, so a second transaction is rejected.
## Fix Focus Areas
- pru_device_models/bissline_device_model.py[258-305]
## Recommended Fix
Detect the inter-frame boundary and reset the per-frame clock, capture, shift and emission state before sampling the next transaction. Add a test that clocks two complete frames with an idle gap.
ⓘ 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 2d06275: a clock idle for frame_gap_cycles (default 64) now ends a frame and the next burst starts from clean state, so a second frame is no longer counted as overclocked; overclocking inside one frame is still flagged.
| def _init_8b10b(): | ||
| for bv in range(256): | ||
| c6 = _5B6B_RD_NEG[bv & 0x1F] | ||
| c4 = _3B4B_RD_NEG[(bv >> 5) & 0x07] | ||
| sym = (c6 << 4) | c4 | ||
| _8B10B_ENC[bv] = sym | ||
| _8B10B_DEC[sym] = bv |
There was a problem hiding this comment.
3. Line-coded frames accumulate disparity 🐞 Bug ≡ Correctness
encode_8b10b_byte() always combines the fixed negative-disparity table entries and has no running-disparity state or alternate encoding. With line coding enabled, repeated bytes can therefore produce successive symbols with the same nonzero disparity instead of a valid balanced 8b/10b stream; the model's inverse table can still decode its own output.
Agent Prompt
## Issue description
Optional BiSS line coding reuses one fixed codeword per byte rather than maintaining 8b/10b running disparity.
## Fix Focus Areas
- pru_device_models/bissline_device_model.py[42-78]
- pru_device_models/bissline_device_model.py[238-251]
## Recommended Fix
Implement both disparity variants and update running disparity across symbols when constructing a frame. Test emitted wire symbols against independently specified vectors, including repeated bytes.
ⓘ 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 2d06275: line coding now tracks running disparity, using the RD- or RD+ code (including the alternate D.x.A7 symbol) per byte; the decoder accepts both. Tests check written-out symbol vectors and repeated bytes.
| // present every clocked device with a falling edge the firmware never | ||
| // generated. Open-loop runs (no bus) keep the historical zero start. | ||
| if (deviceBus) { | ||
| pruState.registers[30] = deviceBus.idlePruWord(); |
There was a problem hiding this comment.
4. First input sample ignores attached devices 🐞 Bug ≡ Correctness
simulatePruInstructions() initializes R30 to the attached bus's idle output but leaves R31 at zero until the post-instruction bus settlement. Firmware that reads R31 in its first instruction receives zero rather than the device-resolved input level at startup, affecting first-cycle samples and branches.
Agent Prompt
## Issue description
Attached-device simulation initializes R30 but does not resolve R31 before executing firmware's first instruction.
## Fix Focus Areas
- .metadata/sysconfig/.meta/pru_blocks/common/simulation/pru_core.js[1984-2015]
- .metadata/sysconfig/.meta/pru_blocks/common/simulation/device_bus.js[128-136]
## Recommended Fix
Settle the attached bus at startup with the initial R30 value, assign the resolved level to R31 and initialize its history consistently before instruction dispatch. Test a first-instruction R31 read against an attached device's idle level.
ⓘ 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 7c14f0c: the attached bus is settled once at startup and its result seeds R31 and the R31 history, so a first-instruction R31 read sees the idle level.
| const raw = this.position & ((1 << this.bits) - 1); | ||
| this._latched = (this.gray ? toGray(raw) : raw) >>> 0; | ||
| this._bitIndex = 1; | ||
| this._data = (this._latched >> (this.bits - 1)) & 1; |
There was a problem hiding this comment.
5. Gui ssi encoder sends wrong data at 32+ bits 🐞 Bug ≡ Correctness
SsiEncoder._fallingEdge builds the frame with 32-bit JS bitwise operations: `(1 << this.bits) - 1, >>> 0, and >> shifts by bits - i`. The Device Model table accepts SSI frame widths up to 64, and the constructor only checks bits >= 1. At the common width of 32, the mask works out to 0, so the encoder always sends zeros. At widths above 32, the shift counts wrap modulo 32 and the frame is corrupted.
Agent Prompt
## Issue description
The GUI SSI encoder model builds frames with 32-bit JS bitwise operations, but the parameter table accepts widths up to 64. At bits=32 the mask becomes 0. Above 32, the shifts wrap.
## Fix Focus Areas
- .metadata/sysconfig/.meta/pru_blocks/common/simulation/device_models.js[276-287]
- .metadata/sysconfig/.meta/pru_blocks/common/simulation/device_models.js[212-214]
- .metadata/sysconfig/.meta/pru_blocks/common/device_model_table.js[67]
## Recommended Fix
Either do the mask, gray-code and bit-extract steps with BigInt (or with Math.pow/Math.floor arithmetic up to 53 bits), or lower the table max and the constructor check to 32 bits.
ⓘ 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 19bff5c: the SSI encoder builds the frame with BigInt, so 32-bit frames are no longer masked to zero and frames up to the table's 64 bits keep every bit.
| self._shift_index = self._clocks_in_burst | ||
| driven_val = self._drive() | ||
| slave_bit = (driven_val >> self.slo_pin) & 1 | ||
| self._captured_bits.append(slave_bit | mo) |
There was a problem hiding this comment.
6. Biss model misreads zero bits when master data is high 🐞 Bug ≡ Correctness
BissLineEncoder.tick records each clocked bit as slave_bit | mo, where mo is sampled from the separate master-data GPO pin. Whenever firmware holds MO high during the reply, every 0 bit the slave sent is recorded as 1. This corrupts the model's own 8b/10b, Hamming and CRC decode in _emit_frame, so a correct firmware run can produce false decode faults or a wrong position event.
Agent Prompt
## Issue description
The BiSS Line model ORs the master MO pin into each captured slave bit, so MO=1 corrupts the decoded frame.
## Fix Focus Areas
- pru_device_models/bissline_device_model.py[258-285]
## Recommended Fix
Append `slave_bit` alone to `_captured_bits`. If MO driving during the reply is meant to represent bus contention, record a fault for it instead of changing the captured value.
ⓘ 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: BiSS Line shares one line between master and slave, and the model deliberately captures slave_bit | mo so a master that drives the line during the reply corrupts the frame (pru_device_models/bissline_device_model.py tick(); asserted by tests/test_bissline.py TestMasterDataCorruption). Reporting that case as a contention fault instead is a possible refinement we have not made.
| # x4 state table: state = 2*A + B. Forward (CW) order 0,1,3,2,0... | ||
| _FWD = {0: 1, 1: 3, 3: 2, 2: 0} | ||
| _REV = {v: k for k, v in _FWD.items()} |
There was a problem hiding this comment.
7. Python and gui encoders count opposite directions 🐞 Bug ≡ Correctness
The Python QuadratureIndexEncoder uses state 2*A+B with forward order 0→1→3→2, so B rises first on a forward step. The GUI JS model's QUAD_PHASES order is [A,B] 00→10→11→01, so A rises first. The same 'forward' motion therefore reaches firmware as opposite rotation in the TI-simulator harness and in the GUI simulator, and a decoder validated against one model counts backwards against the other.
Agent Prompt
## Issue description
The Python and JS quadrature models disagree about which channel leads on forward motion.
## Fix Focus Areas
- pru_device_models/quadrature_index_encoder.py[24-26]
- .metadata/sysconfig/.meta/pru_blocks/common/simulation/device_models.js[51]
## Recommended Fix
Pick one convention (A leads for CW is the usual one). In Python, change `_FWD` to 0→2→3→1 (state=2A+B), or change the JS table, so both models emit the same A/B sequence for forward motion.
ⓘ 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 a29c4e8: aligned the in-tool simulation model phase table and the Python encoder/decoder models to the PRU firmware's B-leading positive direction convention, so forward motion counts positively in both simulation environments and matches compiled firmware cosimulation.
| if self.count % self.cpr == 0 and self.count != 0: | ||
| self._z_high_until = cycle + self.index_width_cycles |
There was a problem hiding this comment.
9. Encoder with zero counts per turn crashes 🐞 Bug ☼ Reliability
The constructor only appends 'cpr must be positive' to the fault list and keeps going. On the first edge, tick() evaluates self.count % self.cpr. With cpr=0, this raises ZeroDivisionError and aborts the whole co-simulation instead of reporting the fault the model already recorded.
Agent Prompt
## Issue description
With cpr<=0, QuadratureIndexEncoder.tick divides by zero.
## Fix Focus Areas
- pru_device_models/quadrature_index_encoder.py[44-51]
- pru_device_models/quadrature_index_encoder.py[98-100]
## Recommended Fix
Either raise ValueError in the constructor, or skip the index check when `self.cpr <= 0`.
ⓘ 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 af07099: with a counts-per-turn of zero the fault is reported as before and the index check no longer divides by it, so the run is not aborted.
| subprocess.run(["taskkill", "/F", "/T", "/PID", str(proc.pid)], | ||
| capture_output=True, text=True, timeout=30) | ||
| else: | ||
| proc.kill() |
There was a problem hiding this comment.
8. Timed-out gui runs leave build processes running on linux 🐞 Bug ☼ Reliability
kill_process_tree only calls proc.kill() on POSIX, and run_logged starts the command without start_new_session, so gmake's children are never signalled. After a sysconfig, build or simulator timeout, compiler or simulator descendants keep running and can keep writing generated files. Meanwhile the log claims the process tree was terminated.
Agent Prompt
## Issue description
On POSIX, a timeout kills only the direct child, so gmake's descendants are orphaned.
## Fix Focus Areas
- scripts/sim/gui_run.py[152-179]
## Recommended Fix
Pass `start_new_session=True` to Popen on POSIX. In `kill_process_tree`, call `os.killpg(os.getpgid(proc.pid), signal.SIGKILL)` (catching ProcessLookupError), then `proc.wait()`.
ⓘ 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 31e1883: on POSIX commands now run in their own session and a timeout signals the whole process group, then reaps the child.
|
|
||
| bindings = manifest.get("bindings", []) | ||
| if not bindings: | ||
| print("REFUSED: manifest has no device bindings. An empty manifest is REFUSED.", file=sys.stderr) |
There was a problem hiding this comment.
10. Missing simulator gives a traceback, not a refusal 🐞 Bug ☼ Reliability
run_bound_devices.main calls find_pru_simulator() outside any try block. That function raises SimulatorNotFound, so a missing or invalid checkout ends with an uncaught traceback and exit code 1. The runner's documented REFUSED result and exit code are never produced in that case.
Agent Prompt
## Issue description
run_bound_devices.main does not handle SimulatorNotFound.
## Fix Focus Areas
- scripts/sim/run_bound_devices.py[358]
## Recommended Fix
Wrap `find_pru_simulator()` in try/except SimulatorNotFound, print `REFUSED: ...` to stderr 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 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 shared-infrastructure pull request. Adds behavioural device models, the Device Model block, device
bindings from SysConfig to TI pru-simulator, the GUI simulator support for attached devices, and the Python harness
that builds a block's macro with clpru and runs the ELF on TI pru-simulator. Most firmware-backed tests for the
allocator and existing-block fixes of the previous pull request live here, because they need this harness.
What changes
pru_device_models/(repository root):device_model.py(base class), SPI ADC, step/dir driver,WS2812 string, MDIO PHY, Hall sensor, dual OSSD emitter, quadrature/index encoder, SSI/EnDat encoder, BiSS line
model,
buscosim.py(independent pin-trace decoders) andwaveform_player.py(cycle-keyed GPI stimulus). The nameis chosen so it does not shadow the pru-simulator checkout's own top-level packages (
core,perif,pru_io),which the tests import in the same process;
tests/test_package_names.pychecks both the names and animport-model-then-simulator sequence in one interpreter.
scripts/sim/:elf_cosim.py(build a block macro or design with clpru, run it on TI pru-simulator in a closed pinloop; optional constant-table override and
IEP_CONSTANTSfor firmware that programs the IEP through c26; optionalIEP-at-core-clock),
device_bindings.py,run_bound_devices.py,extract_device_bindings.py,gui_run.py,live_view.py, and node helpers (emit_block_asm.js,crc_dut.js,lut_cycles.js, ...).tools/gpi_stimulus_runner.py. Rootconftest.py: a missing simulator checkout, or a checkout without a devicemodel a test imports, is reported as SKIPPED with the reason.
utils/device_model_block,common/device_model_table.js,common/pru_ti_run.js(GUI "Run onTI pru-simulator"),
common/pru_device_bindings.json.xdt,common/simulation/device_bus.js,common/simulation/device_models.js; the Device Model block is registered on AM243x, AM64x and AM261x.pru_blocks_static_module.syscfg.jsandpru_syscfg.asm.xdt: Simulation Settings devicefields per GPI pin, the device-binding manifest, reader/register annotation for
gmake sim, and the GUI previewbanner for attached devices. Device-binding errors are logged against the module instance SysConfig passes to
validate()(SysConfig'sreport.logErrorrejects any other object);scripts/sim/test_device_binding_syscfg.jscovers it (on the commit before the change 14 passed, 1 failed; now 15 passed).
field's display name from
displayTitle()indevice_model_table.js; configuration names (the keys saved in.syscfgfiles) are unchanged.scripts/sim/test_device_model_titles.jsapplies the rule to every field the tableproduces. The "Open Live Web View After Run" option keeps its detail in the long description, so its tooltip stays
within SysConfig's length limit.
common/simulation/pru_core.js:Rn.wKis bits 8K+15..8K (shift 8K, was 16K, which made every.w1access wrong);LBCOis executed asLBBO,SBCOasSBBO, with c26 = IEP (0x0002E000) and c28 = 0; LBBO results areunsigned;
ZERO,FILL, XIN/XOUT for the MAC (signed 32x32) and the scratchpad (bounds-checked);MULLis an error;HALTwith devices attached idles the bus until the cycle budget instead of jumping the cycle counter;MAC,MACUandWBCused as instructions stop the run with an error (main skipped them silently); macro callsand directives are still skipped.
tests/test_gui_simulator_core.pycovers the word-register shift, unsigned sub-register writes and theMAC/MACU/WBC error: 9 passed on this branch; on main's
pru_core.js8 of 9 fail.tests/fixtures/firmware/(minimalmain.asmthat jumps to the generated code and halts atsysconfig_generated_end, plus the AM243x PRU0linker.cmd) so the ELF tests of the allocator fixes run withoutan example project. Its
linker.cmdplaces.udmemand.usmemlike the example linker files.tests/,scripts/sim/): device bindings, models and catalogue, GUI run glue, waveform player, BiSS line,crc and access-LUT index scaling, SPI read/transfer zero-extend, loop accumulators, Load Constant width,
register-pool exhaustion, fan-out register alias, nested hardware LOOP, SPI CS timing, board variant builds,
elf_cosimconstant override, andtests/test_spi_sclk_duty.py: every pru_spi_read / write / transfer controllervariant (four modes, MSB and LSB first) built with the block's own delay components and run on TI pru-simulator
against a model peripheral; each SCLK high and low phase must equal the configured width and the word read must
match. Without the SPI read fix of the previous pull request the MODE1 and MODE3 LSB read cases fail (2 of 24).
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, run here against its main branch544d5ea).PRU_SIMULATOR_PATH: 340 passed, 0 failed, 25 skipped;PRU_SIMULATOR_PATH: 226 passed, 0 failed, 139 skipped;PRU_SIMULATOR_PATH, gmake on PATH and a repository-rootimports.mak: 366 passed, 0 failed, 18 skipped;The 25 skips with every tool present: 18
scripts/sim/test_device_bindings.pycases (17 whose device model is notin pru-simulator main:
pru_io.ssi_encoder,quadrature_encoder,endat_encoder,bissc_device_model,bissline_device_model,sd_modulator_device,uart_device; one declared-sink case); 7 cases oftests/test_spi_cs_timing_elf.py(4) andtests/test_board_variant_builds.py(3) that needgmakeand arepository-root
imports.mak, which is not part of a default checkout. With gmake and animports.makthey run andpass; the 18 skips that remain are the device-binding cases.
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 (the version the workflow installs):Total 0 error(s)and no "failed" line in every context.Known limitations
ssi,bissc,endat,quadrature,uart,sigma_delta,tca9538) need a pru-simulator revision that ships those models; on main they are skipped.pru_device_modelsmodels (SPI ADC, step/dir, WS2812, MDIO PHY) use atick()contract and are refused bypru-simulator's device bus by name; they are used by this repository's closed-loop tests.
config/constants_am243x.cfg; tests of IEP-programming blocks pasself_cosim.IEP_CONSTANTSso c26 reaches the simulator's IEP model, as on the device.🤖 Generated with Claude Code