Problem
The simulator models the PRU well and the wire hardly at all. IOPort holds gpo/gpi words, and the only external device it can host is hard-wired:
IOPort.attach_i2c_device() takes exactly one TCA9538, with SCL/SDA frozen at pins 0/1.
- Every other stimulus model grew its own shape and its own hook:
UARTFrameGenerator.tick() is called from inside PRUCore.step(), SigmaDeltaFilter has its own path, and Loopback.sample(channel, t_ns) is a wire rather than a device.
The result is that each new external device (an encoder, an ADC, a motor/inverter plant, a second PRU's pins) needs another special case in IOPort and another hook in PRUCore.step(). PR #41 shows the pattern directly: it adds io_port.ssi_generator as a second hard-coded pre-tick in the core, plus a separate add_wire_callback mechanism for core-to-core wires.
There is also no electrical model of a shared net. With more than one driver, nothing resolves open-drain wired-AND (I2C, I3C, 1-Wire), and nothing flags two push-pull drivers fighting, which is a real short on hardware and a firmware bug the simulator should report.
Proposal
One contract for anything wired to the PRU's pins:
DeviceModel.tick(cycle, bus) -> (drive_mask, drive_values): a device reports what it drives, not the resulting level, because only the bus can resolve several drivers.
DeviceModel.events(): protocol-level decode (what the device believes it received), for comparison against an independent decoder.
DeviceModel.faults(): protocol violations. A model that cannot reject wrong firmware is not a useful test oracle.
- The existing
reset() / snapshot() / restore() / get_state() convention, so step-back and UI state keep working.
DeviceBus: resolves the PRU and every attached device per pin, as OPEN_DRAIN (wired-AND with pull-up) or PUSH_PULL (contention recorded as a fault).
IOPort.attach_device(), which is opt-in and leaves pins that no device drives alone.
Existing paths (attach_i2c_device, UART generator, loopback) keep working unchanged; they can migrate later.
Problem
The simulator models the PRU well and the wire hardly at all.
IOPortholdsgpo/gpiwords, and the only external device it can host is hard-wired:IOPort.attach_i2c_device()takes exactly one TCA9538, with SCL/SDA frozen at pins 0/1.UARTFrameGenerator.tick()is called from insidePRUCore.step(),SigmaDeltaFilterhas its own path, andLoopback.sample(channel, t_ns)is a wire rather than a device.The result is that each new external device (an encoder, an ADC, a motor/inverter plant, a second PRU's pins) needs another special case in
IOPortand another hook inPRUCore.step(). PR #41 shows the pattern directly: it addsio_port.ssi_generatoras a second hard-coded pre-tick in the core, plus a separateadd_wire_callbackmechanism for core-to-core wires.There is also no electrical model of a shared net. With more than one driver, nothing resolves open-drain wired-AND (I2C, I3C, 1-Wire), and nothing flags two push-pull drivers fighting, which is a real short on hardware and a firmware bug the simulator should report.
Proposal
One contract for anything wired to the PRU's pins:
DeviceModel.tick(cycle, bus) -> (drive_mask, drive_values): a device reports what it drives, not the resulting level, because only the bus can resolve several drivers.DeviceModel.events(): protocol-level decode (what the device believes it received), for comparison against an independent decoder.DeviceModel.faults(): protocol violations. A model that cannot reject wrong firmware is not a useful test oracle.reset()/snapshot()/restore()/get_state()convention, so step-back and UI state keep working.DeviceBus: resolves the PRU and every attached device per pin, asOPEN_DRAIN(wired-AND with pull-up) orPUSH_PULL(contention recorded as a fault).IOPort.attach_device(), which is opt-in and leaves pins that no device drives alone.Existing paths (
attach_i2c_device, UART generator, loopback) keep working unchanged; they can migrate later.