Skip to content

feat(validation): add HIL validation app (firmware terminal + Python/AD3 test suite) - #120

Merged
gabrielfrasantos merged 44 commits into
mainfrom
claude/hil-validation
Oct 3, 2026
Merged

gabrielfrasantos merged 44 commits into
mainfrom
claude/hil-validation

Conversation

@gabrielfrasantos

@gabrielfrasantos gabrielfrasantos commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a hardware-in-the-loop validation app under validation/. It exercises every hal-ti driver on EK-TM4C123GXL and EK-TM4C1294XL, using a Digilent Analog Discovery 3 to drive and measure the pins and a CANable as the CAN bus peer. Build, flash, wiring and usage are described in validation/README.md.

The peripheral pins follow LaunchPads modified for the e-foc project, which is the hardware baseline. Nothing else of e-foc is assumed: there are no e-foc defaults in the firmware and no e-foc configuration in the tests.

validation/PROTOCOL.md: the command contract

  • Commands are line based and answer OK, ERR <reason> or EVT ....
  • There is one command group per peripheral, and every exposed driver option can be set at runtime.
  • The generic framing is specified in EMIL's docs/Hil.md.

validation/firmware: C++ firmware hal_ti.validation_firmware

  • Built on EMIL's services.hil and services.hil.commands (embedded-infra-lib#152). The EMIL GIT_TAG is pinned to 0059646. That commit also brings in the ISB after NVIC_DisableIRQ (embedded-infra-lib#154) and the watchdog auto-feed in the early-warning callback (embedded-infra-lib#155).
  • The firmware itself keeps:
    • the board profiles;
    • a Tiva pin factory and the board info;
    • one factory per peripheral that maps *.open arguments to the Tiva driver Config;
    • the Tiva-only pwm.fault and pwm.count commands.
  • Pin aliases name peripheral functions: m0pwm*, qei0*, ain*, can0*, led*, gpio*.
  • Defaults are neutral:
    • pwm.open needs gens or pins, and runs at 10 kHz, edge-aligned, no dead time;
    • adc.open needs pins, and an async sequencer needs an explicit PWM trigger.
  • Newly exposed options:
    • rise and fall dead time set separately;
    • trigger and interrupt source per generator;
    • pwm.fault configurable for comparators, fault pins, latch and minimum period;
    • ADC digital-comparator bands and modes, and the reference selection;
    • CAN bit timing, filter match and loopback;
    • the comparator's ADC trigger sense;
    • wdt.start pin=, which toggles a pin on every early warning so its period can be measured.
  • The terminal is HilTerminal over UartWithDma at 921600 8N1: UART0 on TM4C123, UART2 PD4/PD5 on TM4C129.
  • A debug LED blinks while the firmware runs: PF2 on TM4C123, LED D1 (PN1) on TM4C129.
  • There is no heap. Build it optimised with the -RelWithDebInfo build presets. At -O0 the TM4C123 can't keep up with interrupt-driven UART at 921600 Bd. It builds only when HAL_TI_BUILD_EXAMPLES is on, so CI's embedded builds compile it.

validation/host: Python package and pytest suite

  • It depends on ad3-waveforms-bench, pinned at dcb3754. That is v0.2.0 plus the UART parity and logic-analyzer reset fixes; the Windows ad3-bench-server needs the same commit. It provides the Analog Discovery 3 layer, the fakes, signal analysis and the serial terminal client. python-can drives the CANable.

  • Tests are generic parameter matrices per driver, running both the synchronous and asynchronous drivers wherever both exist. --depth quick runs a deterministic pairwise subset; --depth full runs the full product.

  • Coverage per driver:

    Driver What is covered
    PWM 1–4 generators (1–3 on TM4C1294), A-only/B-only outputs, every irq and trigger source, fault from a pin and from an ADC comparator (outputs forced inactive, one event until pwm.stop)
    UART all baud rates × parity × stop × interrupt/DMA/sync, bit rate measured on the wire, flow control, full-duplex streams
    SPI modes × baud up to sysclk/2 × sync, with or without chip select, MOSI→MISO jumper
    ADC every sequencer at full depth, sample-and-hold × averaging × delay, PWM-triggered, digital comparators
    QEI frequency × direction × capture mode × inversions, index reset, velocity, clock/direction mode
    CAN loopback and/or a CANable on the bus (--can-mode): standard and extended IDs, DLC 0–8, explicit timing, filters; on the bus also ackError, receive errors, bus off and recovery
    Watchdog timeouts, early-warning period measured on the toggle pin, reset and feed
    Comparator pin source, c0 source and reference ladder, interrupts
    GPIO, EEPROM, Ethernet, system pin states, EEPROM contents, link, board info
  • Known driver gaps are marked xfail(strict=False) (see below).

  • The board files hold the constants measured on each board: ADC sample spread, the comparator reference ladder, and QEI clock/direction counts.

Wiring: two fixed AD3 bundles per board, all on the BoosterPack headers

  • bundle1:
    • all 16 DIOs, W1/W2 on the ADC inputs;
    • runs nearly the whole suite.
  • bundle2:
    • the comparator tests, plus UART flow control and the locked pin on TM4C1294;
    • W1/W2 and the scope move to the comparator inputs;
    • a jumper ties the second comparator's input to the same channel: PC7–PC4 on TM4C123, PC5–PC6 on TM4C1294;
    • on TM4C1294, DIO0, DIO1 and DIO14 move to PP4, PP5 and PD7.
  • Each pin serves several tests, and the firmware frees every pin between tests:
    • on TM4C123, the PWM outputs PB4–PB7 are also SSI2 and GPIO test pins, QEI0 phase A is also the fault input, and UART1 flow control shares the PWM generator 3 pins;
    • on TM4C1294, SPI is SSI2 on PD0–PD3, and PD1 is also the comparator output.
  • The board files and README give the header pin of every connection. A connection can list jumpered pins.
  • On TM4C1294, PF0 is not on the headers, so PWM generator 0 is not measured and there is no LED output test.
  • On TM4C123, DIO6 and DIO8 are left out of bundle2, because PC4 and PC6 are comparator inputs there. Only test_comparator.py runs in that bundle.

CAN bus: transceiver + CANable

  • CAN0 goes through a 3.3 V transceiver to a CANable; the AD3 is not on the bus.
  • --can-peer takes slcan:COM7, slcan:socket://host:5002, gs_usb:0, candle:0, socketcan:can0, or port-bridge:host:5001 (fixed bit rate).
  • On TM4C123 the transceiver stays on PF0/PF3. So comparator 0's output is read through the firmware only, and PF0 is not tested as a locked pin.

Windows host + Docker (bridge mode)

This setup is described in validation/README.md. The Analog Discovery 3, the LaunchPad UART, the ICDI debugger and the CANable stay on Windows; the build, flashing and pytest run in the devcontainer.

Windows Port Container
ad3-bench-server 5025 --ad3-remote host.docker.internal:5025 / AD3_REMOTE
port-bridge --serial-port COMx --serial-baudrate 921600 5000 --port socket://host.docker.internal:5000 / HAL_TI_PORT
port-bridge --probe openocd --openocd-board ek-tm4c1294xl 3333 gdb-multiarch (target extended-remote)
second port-bridge on the CANable's slcan COM port 5002 --can-peer slcan:socket://host.docker.internal:5002 / HAL_TI_CAN_PEER
  • .devcontainer/devcontainer.json adds --add-host=host.docker.internal:host-gateway, so the name also resolves under Docker Engine in WSL2.
  • .vscode/launch.json adds one "OpenOCD on host" cortex-debug configuration per board (servertype: external).

What the Analog Discovery 3 can emulate

Protocol Capability How the tests use it
UART Full TX and RX (SDK FDwfDigitalUart*) Peer in both directions
CAN TX and RX at logic level only. ACK behaviour unverified Not used: the bus peer is a CANable behind a transceiver
SPI SDK SPI master. A slave mode is reported for recent WaveForms, but not verified in the SDK for this device Logic-analyzer decode plus a MOSI→MISO jumper
I2C SDK master and spy. Slave only via newer scripting Not used: hal-ti has no I2C driver

T1/T2 (trigger pins) and V+/V- (supplies) are not used. The trigger pins can't be driven or read as levels, and a supply can't replace a wavegen for the comparator sweeps and square waves.

Driver issues found

Found by this suite and fixed in #119. The bench confirmed each fix.

  • PWM: channelAInverted/channelBInverted were ignored; PWMFAULTVAL was never programmed, so a fault did not force the outputs.
  • SpiMaster: hung above sysclk/2, its prescaler overflowed at low rates, and it asserted on overrun. On the TM4C123 every async transfer timed out (erratum SSI#07).
  • UartWithDma:
    • RX stalled and lost characters around the time-out;
    • RX inserted stale characters at a ping-pong half switch;
    • TX dropped bytes on the TM4C123.
  • Uart (interrupt): overran at 921600 Bd.
  • GPIO: fast edges flooded the event queue.
  • Watchdog 1 reset with reset=0 (erratum WDT#03).
  • ADC: a digital-comparator END step lost one FIFO sample per run on the TM4C123 (erratum ADC#03); conversions after the clock is enabled needed a reset (ADC#14).
  • Comparator: wrong ADC-trigger enable bit, and the TM4C129 output PCTL.
  • QEI: Resolution() was off by one.
  • Firmware built at -O0 because the Tiva presets had no build type.

Still open (not fixed here):

  1. hal::tiva::Uart cannot be constructed: using UartBase::UartBase inherits protected constructors.
  2. GpioPin::ResetConfig leaves the pin as a driven output.
  3. GetPeripheralPinConfig ignores the peripheral index (TM4C123 UART4 on PC4/PC5, SSI3 on PD0/PD1).
  4. The analog comparator never clears its reference register, so reopening it with a different reference asserts.
  5. Leftover PWM mode and trigger bits after destruction; the firmware resets the module.
  6. CAN bus errors can flood the event queue, and CAN schedules [this] lambdas from its ISR with no way to cancel them. The app works around this in can.close.
  7. Gpio::ReservePin asserts when a pin is reserved twice.
  8. Eeprom calls its completion callback from a different context for write, read and erase.
  9. Adc::Config::externalReference is never written to the hardware.
  10. Comparator level interrupts cannot be reached through hal::InterruptTrigger.
  11. A rare console garble (ERR usage on a well-formed ~140-char line). It happens during 921600 full duplex on UART1, with or without the latest UART fixes. Not classified yet.

Test plan

  • Fresh FetchContent builds with warnings as errors: tm4c123gh6pm, tm4c1294ncpdt and host-single-Debug.
  • TM4C123 image (RelWithDebInfo): about 101 KB of 256 KB flash (38 %) and 18.8 KB of 32 KB RAM.
  • pytest validation/host/tests/unit: 154 passed.
  • --fake with bundle1 and bundle2 on both boards; bridge mode against ad3-bench-server --fake; CAN against the fakes.
  • Harness pins checked against the BoosterPack header maps of both LaunchPads.
  • markdownlint, markdown-table-formatter, yamllint, prettier, ruff and ls-lint are clean.
  • HIL on the EK-TM4C123GXL with fix!: second assessment round — driver bugs, family #ifdef refactor, host tests, docs #119 merged locally, optimised build:
  • HIL on the EK-TM4C1294XL with fix!: second assessment round — driver bugs, family #ifdef refactor, host tests, docs #119 016afa5 merged locally, optimised build, bundle1 full depth, CAN loopback, nothing deselected: 2418 passed, 3 failed, 132 skipped, no driver failures.
    • The 3 failures were ADC DC-level cases. The scope reference they compare against scattered ±20 mV over its 10 ms window because of mains hum.
    • de9e417 averages it over 100 ms (±0.2 mV). test_adc.py full depth then passed 338/0 twice.
    • Earlier bundle2 full (-O0 build): 78 passed, 0 failed.
  • The PC5–PC6 from_c0 cases on the 1294.
  • CAN bus run with the transceiver and a CANable, including the listen-only, receive-error and bus-off tests.

🤖 Generated with Claude Code

https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX


Generated by Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
Terminal-driven firmware exposing every hal-ti driver with runtime
configuration, using the e-foc pinout and peripheral assignment for
EK-TM4C123GXL and EK-TM4C1294XL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
@gabrielfrasantos

gabrielfrasantos commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor Author

✅⚠️MegaLinter analysis: Success with warnings

Descriptor Linter Files Fixed Errors Max errors Warnings Elapsed time
✅ ACTION actionlint 5 0 0 0.04s
✅ CPP clang-format 38 0 0 0 0.27s
✅ CPP cppcheck 38 0 0 0.6s
✅ DOCKERFILE hadolint 1 0 0 0.11s
✅ JSON jsonlint 10 0 0 0.09s
✅ JSON prettier 10 6 0 0 0.98s
⚠️ MARKDOWN markdownlint 20 0 12 0 1.38s
✅ MARKDOWN markdown-table-formatter 20 0 0 0 0.24s
✅ REPOSITORY betterleaks yes no no 1.08s
✅ REPOSITORY checkov yes no no 22.04s
✅ REPOSITORY git_diff yes no no 0.06s
✅ REPOSITORY grype yes no no 79.69s
✅ REPOSITORY ls-lint yes no no 0.01s
✅ REPOSITORY secretlint yes no no 0.88s
✅ REPOSITORY syft yes no no 1.48s
✅ REPOSITORY trivy yes no no 12.48s
✅ REPOSITORY trivy-sbom yes no no 0.19s
✅ REPOSITORY trufflehog yes no no 2.61s
⚠️ SPELL lychee 50 1 0 0.88s
✅ YAML prettier 10 2 0 0 0.79s
✅ YAML v8r 10 0 0 5.53s
✅ YAML yamllint 10 0 0 0.67s

Detailed Issues

⚠️ SPELL / lychee - 1 error
📝 Summary
---------------------
🔍 Total...........69
🔗 Unique..........48
✅ Successful......61
⏳ Timeouts.........0
🔀 Redirected.......8
👻 Excluded.........7
❓ Unknown..........0
🚫 Errors...........1
⛔ Unsupported......1

Errors in validation/README.md
[403] https://digilent.com/reference/software/waveforms/waveforms-3/start (at 190:12) | Rejected status code: 403 Forbidden

Hint: Followed 8 redirects. You might want to consider replacing redirecting URLs with the resolved URLs. Use verbose mode (`-v`/`-vv`) to see redirection details.
Hint: You can configure accepted/rejected response codes with `-a` or `--accept`
⚠️ MARKDOWN / markdownlint - 12 errors
.claude/agents/executor.md:8 error MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "You are the executor agent for..."]
.claude/agents/orchestrator.md:8 error MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "You are the orchestrator agent..."]
.claude/agents/planner.md:8 error MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "You are the planner agent for ..."]
.claude/agents/reviewer.md:8 error MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "You are the reviewer agent for..."]
.github/agents/executor.agent.md:11 error MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "You are the executor agent for..."]
.github/agents/orchestrator.agent.md:18 error MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "You are the orchestrator agent..."]
.github/agents/planner.agent.md:11 error MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "You are the planner agent for ..."]
.github/agents/reviewer.agent.md:14 error MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "You are the reviewer agent for..."]
.github/prompts/orchestrate.prompt.md:8:401 error MD013/line-length Line length [Expected: 400; Actual: 615]
.github/prompts/orchestrate.prompt.md:8 error MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "Analyze the following task for..."]
AGENTS.md:79:401 error MD013/line-length Line length [Expected: 400; Actual: 712]
AGENTS.md:81:401 error MD013/line-length Line length [Expected: 400; Actual: 557]

Notices

⚠️ Your configuration references items that have been removed from MegaLinter and are ignored: REPOSITORY_GITLEAKS, REPOSITORY_KICS. See Removed linters to find their replacements.

See detailed reports in MegaLinter artifacts

Your project could benefit from a custom flavor, which would allow you to run only the linters you need, and thus improve runtime performances. (Skip this info by defining FLAVOR_SUGGESTIONS: false)

  • Documentation: Custom Flavors
  • Command: npx mega-linter-runner@10.1.0 --custom-flavor-setup --custom-flavor-linters ACTION_ACTIONLINT,CPP_CPPCHECK,CPP_CLANG_FORMAT,DOCKERFILE_HADOLINT,JSON_JSONLINT,JSON_PRETTIER,MARKDOWN_MARKDOWNLINT,MARKDOWN_MARKDOWN_TABLE_FORMATTER,REPOSITORY_CHECKOV,REPOSITORY_GIT_DIFF,REPOSITORY_BETTERLEAKS,REPOSITORY_GRYPE,REPOSITORY_LS_LINT,REPOSITORY_SECRETLINT,REPOSITORY_SYFT,REPOSITORY_TRIVY,REPOSITORY_TRIVY_SBOM,REPOSITORY_TRUFFLEHOG,SPELL_LYCHEE,YAML_PRETTIER,YAML_YAMLLINT,YAML_V8R

MegaLinter is provided by OX Security
Show us your support by starring ⭐ the repository

…g work

- can.close masks the CAN interrupt and destroys the driver behind the
  events it already queued, so none runs on a destroyed driver
- adc.close answers a pending adc.measure with ERR failed
- reset uses its own timer so it no longer cancels a pending delay

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…l layers

The WaveForms binding, AD3 wrapper, fakes, signal analysis, terminal
client and pytest AD3 options now live in
github.com/embedded-pro/ad3-waveforms-bench; the host package keeps the
hal-ti command API, board wiring, fake firmware and HIL tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
The protocol plumbing, pin pool and per-peripheral command groups now
come from EMIL services.hil / services.hil.commands; the firmware keeps
the board profiles, the Tiva pin factory and board info, one factory
per peripheral with the Tiva Config mapping, and the Tiva-only
pwm.fault/pwm.count commands. EMIL is pinned to the
embedded-infra-lib#152 head until it merges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…ted hil services

TivaPwmFactory hands out HilPwmAdapter instances instead of its own
PWM start dispatch, and the fault report uses
infra::AtomicTriggerScheduler. EMIL is pinned to the
embedded-infra-lib#152 head.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
Aliases name peripheral functions (m0pwm*, qei0*, ain*, can0*, led*,
gpio*) on the same pins; pwm.open and adc.open require explicit
generators/pins and use neutral defaults; separate rise/fall dead time,
per-generator trigger and irq sources, a configurable pwm.fault, adc
dcmp bands/modes and reference, can bit timing and filter match,
comparator trigger sense and a wdt early-warning toggle pin are exposed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
Board files use the generic aliases on the same wiring; every driver is
exercised through parameter matrices (sync and async where available)
with a --depth quick|full option (pairwise subset vs full product).
Known driver gaps are marked xfail. The README documents the generic
wiring sets and what the Analog Discovery 3 can emulate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
… pin is busy

pwm.fault now claims a new fault pin before rebuilding the PWM module,
so ERR pin/busy leaves the running configuration untouched; repeating
the held pin keeps it. The fake firmware follows the same order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
PC7 is C0-; C0+ is PC6 on the TM4C1294, as on the TM4C123.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…d on a Windows host

The README describes bridge mode: ad3-bench-server and port-bridge on
Windows share the AD3, the terminal COM port and an OpenOCD GDB server
over TCP; the devcontainer builds, flashes with gdb-multiarch and runs
pytest with --ad3-remote and a socket:// port. The devcontainer maps
host.docker.internal to the host, VS Code gains launch configurations
that attach to the host's OpenOCD, and the host package pins
ad3-waveforms-bench to v0.2.0, the first release with remote support.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
Comment thread .devcontainer/devcontainer.json
…ck selectable

The AD3 is no longer a CAN node. CAN0 goes through a 3.3 V transceiver to a
CANable, and `--can-mode loopback|bus|both` selects the link the frame,
bit-timing and acceptance-filter tests run over (loopback stays the default and
needs no wiring).

- hal_ti_validation.can_peer: the CANable through python-can (`slcan:COM7`,
  `slcan:socket://host:5002`, `gs_usb:0`, `candle:0`, `socketcan:can0`) or
  port-bridge's CAN bridge (`port-bridge:host:5001`, fixed bit rate).
- Bus-only tests: ackError with a listen-only CANable, receive errors and bus
  off with the CANable at a wrong bit rate, recover=0/1.
- --fake: the fake firmware joins a python-can virtual bus that stands in for
  the CANable.
- Board files and README: transceiver wiring, CANable options, bridge mode
  with a second port-bridge for the CANable's COM port.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
Copilot AI balanced review requested due to automatic review settings October 1, 2026 08:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The fake GPIO model omits valid TM4C129 interrupt ports, and the CAN socket test has a nondeterministic short-read failure.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
What changed in this PR

Adds a complete HIL validation application for TI LaunchPads, combining terminal firmware with an AD3-driven Python test suite.

Changes:

  • Adds peripheral factories, board profiles, and terminal firmware.
  • Adds host tooling, fakes, pairwise matrices, and HIL/unit tests.
  • Adds build, container, debugger, protocol, and usage configuration.
File Description
CMakeLists.txt Updates EMIL and enables validation builds.
.devcontainer/​devcontainer.json Adds host bridge resolution.
.vscode/​launch.json Adds external OpenOCD launch profiles.
validation/​CMakeLists.txt Includes validation firmware.
validation/​README.md Documents setup and operation.
validation/​PROTOCOL.md Defines the terminal protocol.
validation/​firmware/​CMakeLists.txt Defines the firmware target.
validation/​firmware/​AdcFactory.cpp Implements ADC command mapping.
validation/​firmware/​AdcFactory.hpp Declares the ADC factory.
validation/​firmware/​BoardTypes.hpp Defines shared board types.
validation/​firmware/​CanFactory.cpp Implements CAN command mapping.
validation/​firmware/​CanFactory.hpp Declares the CAN factory.
validation/​firmware/​ComparatorFactory.cpp Implements comparator mapping.
validation/​firmware/​ComparatorFactory.hpp Declares the comparator factory.
validation/​firmware/​Console.cpp Configures the terminal UART.
validation/​firmware/​Console.hpp Defines console storage.
validation/​firmware/​EepromFactory.cpp Lazily constructs EEPROM.
validation/​firmware/​EepromFactory.hpp Declares the EEPROM factory.
validation/​firmware/​EthernetGroup.cpp Implements TM4C129 Ethernet commands.
validation/​firmware/​EthernetGroup.hpp Declares Ethernet registration.
validation/​firmware/​EthernetGroupUnsupported.cpp Provides unsupported Ethernet commands.
validation/​firmware/​Main.cpp Assembles validation services.
validation/​firmware/​PwmFactory.cpp Implements PWM extensions and mapping.
validation/​firmware/​PwmFactory.hpp Declares the PWM factory.
validation/​firmware/​QeiFactory.cpp Implements QEI command mapping.
validation/​firmware/​QeiFactory.hpp Declares the QEI factory.
validation/​firmware/​SpiFactory.cpp Implements SPI command mapping.
validation/​firmware/​SpiFactory.hpp Declares the SPI factory.
validation/​firmware/​TivaBoardInfo.cpp Reports board and reset information.
validation/​firmware/​TivaBoardInfo.hpp Declares board information.
validation/​firmware/​TivaPinFactory.cpp Implements managed Tiva pins.
validation/​firmware/​TivaPinFactory.hpp Declares the pin factory.
validation/​firmware/​UartFactory.cpp Implements UART variants.
validation/​firmware/​UartFactory.hpp Declares the UART factory.
validation/​firmware/​WatchDogFactory.cpp Implements watchdog validation.
validation/​firmware/​WatchDogFactory.hpp Declares the watchdog factory.
validation/​firmware/​boards/​tm4c123/​BoardProfile.hpp Defines TM4C123 hardware.
validation/​firmware/​boards/​tm4c129/​BoardProfile.hpp Defines TM4C129 hardware.
validation/​host/​.gitignore Ignores Python artifacts.
validation/​host/​pyproject.toml Defines host package and pytest settings.
validation/​host/​boards/​ek_tm4c123gxl.yaml Defines TM4C123 test wiring.
validation/​host/​boards/​ek_tm4c1294xl.yaml Defines TM4C129 test wiring.
validation/​host/​hal_ti_validation/​__init__.py Exports the host API.
validation/​host/​hal_ti_validation/​can_peer.py Implements CAN peers.
validation/​host/​hal_ti_validation/​config.py Loads board configuration.
validation/​host/​hal_ti_validation/​console.py Provides the validation console.
validation/​host/​hal_ti_validation/​expect.py Calculates expected hardware values.
validation/​host/​hal_ti_validation/​fake_firmware.py Implements offline firmware behavior.
validation/​host/​hal_ti_validation/​firmware.py Provides typed firmware commands.
validation/​host/​hal_ti_validation/​pairwise.py Generates parameter combinations.
validation/​host/​hal_ti_validation/​protocol.py Handles pins and aliases.
validation/​host/​tests/​conftest.py Implements pytest integration.
validation/​host/​tests/​hil/​test_adc.py Validates ADC behavior.
validation/​host/​tests/​hil/​test_can.py Validates CAN behavior.
validation/​host/​tests/​hil/​test_comparator.py Validates comparators.
validation/​host/​tests/​hil/​test_eeprom.py Validates EEPROM behavior.
validation/​host/​tests/​hil/​test_ethernet.py Validates Ethernet behavior.
validation/​host/​tests/​hil/​test_gpio.py Validates GPIO behavior.
validation/​host/​tests/​hil/​test_pwm.py Validates PWM behavior.
validation/​host/​tests/​hil/​test_qei.py Validates QEI behavior.
validation/​host/​tests/​hil/​test_spi.py Validates SPI behavior.
validation/​host/​tests/​hil/​test_system.py Validates system commands.
validation/​host/​tests/​hil/​test_uart.py Validates UART behavior.
validation/​host/​tests/​hil/​test_watchdog.py Validates watchdog behavior.
validation/​host/​tests/​unit/​test_can_peer.py Tests CAN peer framing.
validation/​host/​tests/​unit/​test_config.py Tests board configuration.
validation/​host/​tests/​unit/​test_expect.py Tests expected-value calculations.
validation/​host/​tests/​unit/​test_fake_firmware.py Tests fake firmware.
validation/​host/​tests/​unit/​test_firmware.py Tests the typed firmware API.
validation/​host/​tests/​unit/​test_pairwise.py Tests pairwise generation.
validation/​host/​tests/​unit/​test_protocol.py Tests protocol helpers.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread validation/host/hal_ti_validation/fake_firmware.py
Comment thread validation/host/tests/unit/test_can_peer.py Outdated
claude added 2 commits October 1, 2026 09:00
…it test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…ared roles

The wiring sets reused DIO0-7 for every peripheral and left DIO8-15 almost
unused, so each set meant rewiring. The new `harness` set wires all 16 DIOs
once per board and each pin serves several tests: the PWM outputs are also
the SPI pins (SSI2 on PB4-PB7, SSI3 on PF0-PF3) and GPIO test pins, QEI0
phase A is also the TM4C123 fault input, and UART flow control shares the
TM4C123 PWM generator 3 pins. The analog sets only add W1/W2 and the scope.

- The CAN transceiver stays on its pins: on the TM4C123 comparator 0's output
  (PF0) is read through the firmware only and PF0 is no longer a locked-pin
  test pin.
- On the TM4C1294 the comparator sets move DIO15 to PD1 and the new `locked`
  set moves DIO14 to PD7.
- Tests look up the fault, flow-control and watchdog pins by pin instead of
  by role; the pwm4/faultpin/qei1/flow/dcmp tags are gone.
- Unit test: the harness uses every DIO once and every parameter pin is wired.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
claude added 3 commits October 1, 2026 09:15
…eaders

PF0 is not on X6-X9 (it only drives LED D4 and reaches the unpopulated X11
pad 66), so the harness can no longer use it for M0PWM0 and SSI3.

- SPI moves to SSI2 on PD0-PD3; PD1 is also C1o, so the comparator sets no
  longer move a DIO and combine with the harness.
- PWM tests use generators 1-3 on this board.
- UART3 flow control (PP4/PP5) becomes the `flow` set, which moves DIO14/15.
- No user LED is on the headers, so the board has no LED output test.
- The notes of both boards give the header pin of every connection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
The harness plus the small adc/comparator/comparator_c0/flow/locked sets
become two complete bundles, each selected with one --wiring-set:

- bundle1: the 16-DIO harness with W1/W2 on the ADC inputs; runs nearly the
  whole suite.
- bundle2: the comparator tests (and on the EK-TM4C1294XL UART flow control
  and the locked pin). A jumper ties the second comparator's input to the
  same wavegen channel (PC7-PC4 on TM4C123, PC5-PC6 on TM4C1294); the
  TM4C1294 moves DIO0/1/14 to PP4/PP5/PD7. On the TM4C123, DIO6/DIO8 are
  left out of bundle2 because PC4/PC6 are comparator inputs there.

A connection can list `jumpered` pins: the channel reaches them as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…feature

A manual install lands in the container's home directory and is lost on
every rebuild, and the image has no curl for the installer one-liner. The
feature installs the CLI on rebuild, so `claude remote-control` can run a
session against the HIL hardware from the devcontainer. The README's
bridge-mode section says how.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
Comment thread .devcontainer/devcontainer.json
claude and others added 9 commits October 1, 2026 14:22
On the real EK-TM4C1294XL through port-bridge, test_system got replies meant
for earlier commands (`info` -> ERR usage, timeouts). FirmwareTerminal takes
any reply that arrives after a command was written as that command's reply,
so one late reply shifts every later command.

- sync with the full command timeout instead of 0.5 s;
- quiesce() reads until the line is silent, after sync and before every HIL
  test, so a late reply is dropped instead of taken by the next command;
- --command-timeout / HAL_TI_COMMAND_TIMEOUT override the board's timeout.

A unit test reproduces the misaligned reply and checks the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
EMIL's services::DebugLed blinks the blue LED PF2 on the EK-TM4C123GXL (its
green LED PF3 is the CAN0 transmit pin) and LED D1 PN1 on the EK-TM4C1294XL,
so a glance at the board shows that the image is flashed and the event loop
runs. The pin is reserved like the terminal pins; test_system checks it
answers ERR busy, and the fake firmware reserves it too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
… bench

- Size the synchronous UART ring buffer for a full uart.recv (256 bytes
  plus the empty slot); 64 bytes dropped data in the large-payload and
  full-duplex stream tests.
- QEI clock/direction mode counts both edges of PhA on the TM4C129, so
  the EK-TM4C1294XL board file expects 2 counts per pulse.
- test_index_resets_position: the AD3 pattern puts an index at the start
  of every `every` cycles, so the last index can fall inside `extra`;
  expect the counts since that last index.
- test_warning_period: the capture triggers on the first toggle, one
  timeout after the start, so wait for that timeout as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SSI2XDAT0 (TX) is PD1 and SSI2XDAT1 (RX) is PD0. hal-ti#119 fixes the
TM4C129 pinout table accordingly, so the board file follows it: the SPI
instance, the DIO notes and the loopback note (the AD3 now only listens
on DIO14). Against main, whose table still swaps the pins, `spi.open`
with these pins fails with `ERR pin` until #119 is merged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… interrupt window

- apply_level reads the scope on a 5 V range centred on mid-supply; the
  default 10 V range read ~25 mV high, most of the 40-code ADC tolerance.
- The raw-sample spread limit is a board parameter (`spread_codes`). The
  EK-TM4C1294XL analog supply carries a ~100 kHz ripple of about 1 % of
  the reading (measured; gone with hardware averaging), so its single
  conversions spread up to ~110 codes; the 40-code mean tolerance stays.
- The PWM interrupt counter is cleared and read somewhere within host
  round trips, so the counted window is bounded by host timestamps
  instead of assumed equal to the delay; a slow round trip through the
  serial bridge no longer fails test_interrupt_count.

EK-TM4C1294XL, bundle1, quick depth: 555 passed, 78 skipped, 1 xfailed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ADC spread

- test_waveform: the driver rounds the duty to whole counts and drives a static
  level once it reaches LOAD; at 200 kHz with pwmclk 1.875 MHz a period has 9
  counts, so 90 % becomes full-on and the capture never sees an edge. Skip duties
  that round to 0 or LOAD (expect.pwm_duty_resolvable).
- ek_tm4c1294xl: the bench measured up to 131 codes peak-to-peak on raw samples
  at 3.1 V from the analog supply ripple; spread_codes 120 -> 150.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
… EK-TM4C1294XL

The bench measured all 16 steps of both ranges at VDDA 3.30 V: RNG=0 is
0.778 V + 0.1136 V/step and RNG=1 is 0.1473 V/step (residuals <= 26 mV), 6-14 %
above the constants the board file used, which failed every
test_internal_reference case. The driver writes ACREFCTL as specified; only the
expectation changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
Keeps the services.hil terminal (dd77048) and adds the ISB after
NVIC_DisableIRQ in DisableIrq (#154).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…inactive

Follows the hal-ti Pwm fault rework in #119:

- PROTOCOL: a fault drives the faulted generators' outputs to their inactive
  level and reports one EVT pwm; the next report needs pwm.stop.
- test_fault_forces_outputs is no longer xfail.
- New test_fault_reported_once_until_stop: five fault pulses give one event,
  pwm.stop re-arms the report for the next fault.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
claude added 5 commits October 2, 2026 11:30
embedded-infra-lib#155 refreshes the watchdog in the early-warning callback
instead of the scheduled report, so a 1 ms timeout no longer stretches to
1.117 ms between warnings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
Measured on the EK-TM4C123GXL bench, as on the TM4C1294: 50 pulses give 100 counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
The HIL runs use ad3-waveforms-bench main, which adds the WaveForms UART parity
code and the digital-in reset before the logic analyzer is armed. Neither fix is
released yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
Raw conversions on the EK-TM4C123GXL spread 83-165 codes peak-to-peak, and the
spread grows with the level, while the means stay within tolerance. Allow 180
codes, as the TM4C1294 board file does with its own measurement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
claude added 3 commits October 2, 2026 15:06
… bench

The 16 steps of both ranges, measured on the EK-TM4C123GXL, fit to
0.773 + 0.1133*n V (low) and 0.1481*n V (high), with residuals of at most 6 and
16 mV. The datasheet values were 50-300 mV low and failed 7 ladder points.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
On the EK-TM4C1294XL, the scope mean that sets the expected ADC code scattered
±20-25 mV between back-to-back acquisitions, while the ADC read the programmed
level within 8 mV at 64x averaging. That intermittently pushed DC-level cases
past the 40-code tolerance. The 10 ms window covered half a 50 Hz period; 100 ms
spans whole 50 and 60 Hz periods.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
@gabrielfrasantos
gabrielfrasantos merged commit b77237a into main Oct 3, 2026
5 checks passed
@gabrielfrasantos
gabrielfrasantos deleted the claude/hil-validation branch October 3, 2026 19:09
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.

3 participants