fix!: second assessment round — driver bugs, family #ifdef refactor, host tests, docs - #119
gabrielfrasantos wants to merge 37 commits into
Conversation
- ClockTm4c129: report the frequency actually produced by the PLL instead of the requested one; stop forcing RSCLKCFG.OSCSRC to PIOSC after setup - system files: SystemCoreClock starts at the 16 MHz PIOSC reset clock (SystemInit is not called by the startup code) - LaunchPad (TM4C123): pass the configured divider and PLL choice to ConfigureClock - TracingReset: fix instantiations namespace typo - FreeRTOS example: allow EMIL normal/high IRQ priorities to call FreeRTOS FromISR APIs (max syscall priority 1) - CMake: HAL_TI_INCLUDE_LWIP option instead of forcing lwIP off Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…, IRQ table size - set the module-level PWMINTEN.INTPWMn bit so generator interrupts reach the NVIC (only the per-generator source was enabled) - 0% and 100% duty drive the output constant low/high via the generator action register; coinciding LOAD/CMP events otherwise made 0% ~100% - apply channelAInverted/channelBInverted through PWMINVERT - keep Config by value (was a reference that dangles for temporaries) - interrupt table sized for every TM4C123/129 vector (PWM1 IRQs 134-138) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
- TM4C129 pin table mapped MISO to SSInXDAT0 and MOSI to SSInXDAT1; in legacy SPI mode XDAT0 is TX and XDAT1 is RX, so data lines were crossed on every SSI instance - clock divider search: validate the requested rate, keep CPSDVSR in 2..254 and SCR in 0..255, pick the fastest rate not above the request, mask CPSR - SynchronousSpiMaster: actually drain stale RX data in the constructor (the loop never read DR and could hang) - SpiMaster: register the interrupt handler once after configuration instead of creating and destroying it per transfer (it was destroyed from inside its own Invoke) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…gth, DMA example - UartWithDma: decide which ping-pong half is filling from the uDMA ALTSET state; the old check misread it after the primary half was re-armed and dropped received bytes - RX DMA: single requests with arbitration size 2 to match the 1/8 FIFO trigger, so the uDMA never reads from an empty FIFO - DmaChannel keeps its Channel by value - UART word length: only 8-bit words exist on this API; assert instead of silently ignoring _16_bytes - terminal_uart_with_dma example now actually uses Dma + UartWithDma Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…t, QEI reset - ADC: encode AIN16-19 through SSEMUX instead of leaking bit 4 into the SSCTL nibble (they sampled AIN0-3 in differential mode); poll only the sequencer's own RIS bit; clamp the returned sample range - Adc registers its interrupt handler after the peripheral is clocked and configured, and releases it first on destruction - comparator: ACCTL.TOEN is bit 11, set when the ADC trigger is enabled; drop the unimplemented routeToPwmFault option - QuadratureEncoder: software-reset the QEI before configuring it (ENABLE cannot be cleared otherwise), Resolution() returns the configured value, remove unused interrupt members BREAKING CHANGE: AnalogComparator::Config::routeToPwmFault is removed (it had no effect). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…t, shared WDT vector, IRQ ordering - Can: the accept-all filter required IDE=0 and rejected every 29-bit frame; the ISR now uses only IF2 (thread uses IF1) and clears the TX request on bus-off before reporting the failed send - Eeprom: completion callbacks always run from the event dispatcher (were ISR / synchronous / timer depending on the operation) - WatchDog: WDT0 and WDT1 share one vector through a single handler, so two instances no longer collide; masking/unmasking uses EMIL Register/Unregister instead of raw NVIC calls - Ethernet: build the MAC filter words from the six address bytes (was an out-of-bounds read), use DEVICE_HEADER, keep Delay file-local, report the selected PHY address - Can, Eeprom, WatchDog, Ethernet register their interrupt handler after the peripheral is clocked and configured, and release it first on destruction Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…dlers, atomic Set - releasing a pin (and analog config) left it as a push-pull output driving the stale DATA latch; restore input with the digital function disabled, and configure analog pins as the datasheet describes - GPIO interrupts now work on every port with a vector, including the TM4C129 ports G..N and the per-pin P/Q vectors; InterruptType::immediate runs the action in the ISR instead of going through the event loop - GpioPin::Set writes through the masked DATA alias so concurrent writers to other pins of the same port can't lose updates - pin reservation checks use really_assert so they stay in release builds Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
The only host test was a placeholder. Move the two pure calculations out of the drivers into device-independent headers (CanBitTiming.hpp, SpiClockDivisor.hpp) so they can be unit tested on the host, and share the SPI divisor search between SpiMaster and SynchronousSpiMaster instead of duplicating it. Can::BitTiming is now an alias of hal::tiva::CanBitTiming. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
- interrupt dispatch goes through EMIL's InterruptTable via Default_Handler; drivers register an EMIL handler last in the constructor (after clearing the pending IRQ) and release it first in the destructor; no per-ISR startup-file edits are needed - hal::cortex::InterruptTable namespace, CAN status write-back (LEC = 7), ctest --preset host, QuadratureEncoder class name, TimeKeeper is not part of EMIL's cortex_m - README: CMake 3.24, hal_tiva.tiva target, build options incl. HAL_TI_INCLUDE_LWIP, host unit tests; drop broken CODEOWNERS links - add doc/EK-TM4C1294XL.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…ily headers
AGENTS.md forbids #ifdef TM4C123/TM4C129 in C++, but about a dozen files
used them. Family-specific constants, types and small register helpers now
live in hal_tiva/tiva/family/<FAMILY>/<Driver>Family.hpp (same API for both
families) and the BSP in hal_tiva/instantiations/family/<FAMILY>/; CMake
adds only family/${TARGET_MCU_FAMILY} to the include path and lists
family-only sources with generator expressions.
- Gpio: port table (incl. IRQs and per-pin P/Q vectors), AHB aliases and
commit-locked pins per family
- Pwm/SynchronousPwm: module count, IRQs, clock divider access
- AnalogComparator, QuadratureEncoder, UniqueDeviceId, UartWithDma DMA
completion (TM4C123 uses DMACHIS, which TM4C129 does not have)
- family-neutral PinoutTableDefault.hpp for bringup; LaunchPad gains
SecondLed() so the FreeRTOS example needs no conditional
- DmaChannel exposes ChannelNumber() instead of a TM4C123-only API
RAM use is unchanged; code is 84-208 bytes smaller per example because the
commit-lock check is no longer inlined into every GpioPin constructor.
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
✅
|
| Descriptor | Linter | Files | Fixed | Errors | Max errors | Warnings | Elapsed time |
|---|---|---|---|---|---|---|---|
| ✅ ACTION | actionlint | 5 | 0 | 0 | 0.05s | ||
| ✅ CPP | clang-format | 7 | 0 | 0 | 0 | 0.06s | |
| ✅ CPP | cppcheck | 7 | 0 | 0 | 0.14s | ||
| ✅ DOCKERFILE | hadolint | 1 | 0 | 0 | 0.12s | ||
| ✅ JSON | jsonlint | 10 | 0 | 0 | 0.05s | ||
| ✅ JSON | prettier | 10 | 7 | 0 | 0 | 0.84s | |
| markdownlint | 18 | 0 | 12 | 0 | 0.72s | ||
| ✅ MARKDOWN | markdown-table-formatter | 18 | 0 | 0 | 0 | 0.15s | |
| ✅ REPOSITORY | betterleaks | yes | no | no | 1.03s | ||
| ✅ REPOSITORY | checkov | yes | no | no | 19.24s | ||
| ✅ REPOSITORY | git_diff | yes | no | no | 0.03s | ||
| ✅ REPOSITORY | grype | yes | no | no | 62.79s | ||
| ✅ REPOSITORY | ls-lint | yes | no | no | 0.0s | ||
| ✅ REPOSITORY | secretlint | yes | no | no | 0.61s | ||
| ✅ REPOSITORY | syft | yes | no | no | 1.11s | ||
| ✅ REPOSITORY | trivy | yes | no | no | 12.32s | ||
| ✅ REPOSITORY | trivy-sbom | yes | no | no | 0.09s | ||
| ✅ REPOSITORY | trufflehog | yes | no | no | 2.27s | ||
| ✅ SPELL | lychee | 44 | 0 | 0 | 1.22s | ||
| ✅ YAML | prettier | 8 | 2 | 0 | 0 | 0.59s | |
| ✅ YAML | v8r | 8 | 0 | 0 | 3.87s | ||
| ✅ YAML | yamllint | 8 | 0 | 0 | 1.27s |
Detailed Issues
⚠️ 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
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

Show us your support by starring ⭐ the repository
ls-lint requires snake_case directory names; rename family/TM4C123 and
family/TM4C129 to family/tm4c123 and family/tm4c129 and select them with
$<LOWER_CASE:${TARGET_MCU_FAMILY}>.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…e-out The receive time-out only fires while the RX FIFO holds a character. Single DMA requests (USEBURST clear) and a 2-item burst at the 2-character trigger level both empty the FIFO, so a short command stayed in the DMA half-buffer until 64 more bytes arrived. On an EK-TM4C1294XL the validation terminal answered each command only after later input. RX now uses bursts only, 4 items at an 8-character trigger level, so 1 to 7 characters always stay in the FIFO and the time-out hands them over. The halves of the RX buffer must be a multiple of the burst size. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
PWM channel updates can overwrite companion outputs, while several new shared IRQ registrations mishandle pending state or priority.
Review effort: Balanced
Findings: 5
Open (5)
Duty updates overwrite the other channel's output mode · New Clear GPIO port IRQ pending state before handler registration · New Clear generator and NVIC pending state before handler registration · New Reconfigure shared IRQ priority for all active watchdogs · New Clear pending watchdog IRQ before shared-handler registration · New
What changed in this PR
Broad correctness sweep across TI peripheral drivers, family-specific code, host tests, examples, and documentation.
Changes:
- Fixes PWM, SPI, UART DMA, GPIO, ADC, CAN, watchdog, clock, and interrupt handling.
- Moves family-specific logic into CMake-selected headers.
- Adds 47 host tests and updates examples/documentation.
| File | Description |
|---|---|
tiva/CMSIS/Device/TI/TM4C129/Source/Templates/system_TM4C129.c |
Sets reset clock to 16 MHz. |
tiva/CMSIS/Device/TI/TM4C123/Source/Templates/system_TM4C123.c |
Sets reset clock to 16 MHz. |
README.md |
Updates integration and testing docs. |
integration_test/test/Test.cpp |
Removes placeholder test. |
integration_test/test/SpiClockDivisorTest.cpp |
Tests SPI divisor selection. |
integration_test/test/CMakeLists.txt |
Registers new host tests. |
integration_test/test/CanBitTimingTest.cpp |
Tests CAN timing calculation. |
hal_tiva/tiva/WatchDog.hpp |
Adds shared-handler state. |
hal_tiva/tiva/WatchDog.cpp |
Shares the watchdog IRQ. |
hal_tiva/tiva/UniqueDeviceId.cpp |
Delegates family-specific behavior. |
hal_tiva/tiva/UartWithDma.cpp |
Fixes DMA completion and timeout handling. |
hal_tiva/tiva/UartBase.cpp |
Rejects unsupported word lengths. |
hal_tiva/tiva/SpiMaster.cpp |
Uses shared divisor logic and persistent IRQ registration. |
hal_tiva/tiva/SpiClockDivisor.hpp |
Adds device-independent divisor search. |
hal_tiva/tiva/Pwm.hpp |
Owns configuration and adds inversion setup. |
hal_tiva/tiva/Pwm.cpp |
Fixes interrupts, inversion, and endpoint duty cycles. |
hal_tiva/tiva/PinoutTableDefaultTm4c129.cpp |
Corrects TM4C129 SPI pins. |
hal_tiva/tiva/PinoutTableDefault.hpp |
Adds family-neutral declarations. |
hal_tiva/tiva/Gpio.hpp |
Expands interrupt routing support. |
hal_tiva/tiva/Gpio.cpp |
Fixes release, analog, masked writes, and IRQ dispatch. |
hal_tiva/tiva/family/tm4c129/UniqueDeviceIdFamily.hpp |
Provides TM4C129 unique ID access. |
hal_tiva/tiva/family/tm4c129/UartWithDmaFamily.hpp |
Defines TM4C129 DMA status handling. |
hal_tiva/tiva/family/tm4c129/QuadratureEncoderFamily.hpp |
Defines TM4C129 QEI resources. |
hal_tiva/tiva/family/tm4c129/PwmFamily.hpp |
Defines TM4C129 PWM resources. |
hal_tiva/tiva/family/tm4c129/GpioFamily.hpp |
Defines TM4C129 GPIO resources. |
hal_tiva/tiva/family/tm4c129/AnalogComparatorFamily.hpp |
Defines comparator registers and IRQs. |
hal_tiva/tiva/family/tm4c129/AnalogComparatorFamily.cpp |
Adds comparator 2 forwarding. |
hal_tiva/tiva/family/tm4c123/UniqueDeviceIdFamily.hpp |
Defines empty TM4C123 unique ID. |
hal_tiva/tiva/family/tm4c123/UartWithDmaFamily.hpp |
Defines TM4C123 DMA completion handling. |
hal_tiva/tiva/family/tm4c123/QuadratureEncoderFamily.hpp |
Defines TM4C123 QEI resources. |
hal_tiva/tiva/family/tm4c123/PwmFamily.hpp |
Defines TM4C123 PWM resources. |
hal_tiva/tiva/family/tm4c123/PwmFamily.cpp |
Adds PWM1 interrupt forwarding. |
hal_tiva/tiva/family/tm4c123/GpioFamily.hpp |
Defines TM4C123 GPIO resources. |
hal_tiva/tiva/family/tm4c123/AnalogComparatorFamily.hpp |
Defines TM4C123 comparator resources. |
hal_tiva/tiva/Ethernet.hpp |
Makes IRQ registration optional. |
hal_tiva/tiva/Ethernet.cpp |
Fixes lifecycle, MAC packing, and PHY reporting. |
hal_tiva/tiva/Eeprom.hpp |
Stores optional IRQ handler. |
hal_tiva/tiva/Eeprom.cpp |
Defers callbacks and fixes handler lifecycle. |
hal_tiva/tiva/Dma.hpp |
Owns channel data and exposes channel state. |
hal_tiva/tiva/Dma.cpp |
Implements alternate-state and channel queries. |
hal_tiva/tiva/CMakeLists.txt |
Selects family-specific implementation files. |
hal_tiva/tiva/ClockTm4c129.cpp |
Reports actual configured frequency. |
hal_tiva/tiva/CanBitTiming.hpp |
Extracts CAN timing calculation. |
hal_tiva/tiva/Can.hpp |
Uses shared timing type and optional handler. |
hal_tiva/tiva/Can.cpp |
Fixes filtering, IF usage, and lifecycle. |
hal_tiva/tiva/AnalogComparator.hpp |
Removes ineffective PWM routing API. |
hal_tiva/tiva/AnalogComparator.cpp |
Fixes trigger output configuration. |
hal_tiva/synchronous_tiva/SynchronousUart.cpp |
Rejects unsupported word lengths. |
hal_tiva/synchronous_tiva/SynchronousSpiMaster.cpp |
Shares divisor logic and drains RX data. |
hal_tiva/synchronous_tiva/SynchronousQuadratureEncoder.hpp |
Stores configured resolution. |
hal_tiva/synchronous_tiva/SynchronousQuadratureEncoder.cpp |
Resets QEI and fixes resolution reporting. |
hal_tiva/synchronous_tiva/SynchronousPwm.hpp |
Owns configuration and adds inversion setup. |
hal_tiva/synchronous_tiva/SynchronousPwm.cpp |
Fixes divisors, inversion, and endpoint duty cycles. |
hal_tiva/synchronous_tiva/SynchronousAnalogComparator.hpp |
Removes obsolete routing alias. |
hal_tiva/synchronous_tiva/SynchronousAnalogComparator.cpp |
Fixes trigger output configuration. |
hal_tiva/synchronous_tiva/SynchronousAdc.cpp |
Fixes channel encoding and result sizing. |
hal_tiva/instantiations/TracingReset.hpp |
Corrects namespace spelling. |
hal_tiva/instantiations/TracingReset.cpp |
Corrects namespace spelling. |
hal_tiva/instantiations/LaunchPadBsp.hpp |
Selects family-specific BSP. |
hal_tiva/instantiations/family/tm4c129/LaunchPadFamily.hpp |
Defines TM4C129 LaunchPad setup. |
hal_tiva/instantiations/family/tm4c123/LaunchPadFamily.hpp |
Defines TM4C123 LaunchPad setup. |
hal_tiva/instantiations/CMakeLists.txt |
Adds family BSP include paths. |
hal_tiva/bringup/Bringup.cpp |
Enlarges the interrupt table. |
examples/terminal_uart_with_dma/Main.cpp |
Demonstrates actual DMA UART usage. |
examples/freertos/Main.cpp |
Uses family-neutral second LED. |
examples/freertos/config/FreeRTOSConfig.h |
Adjusts syscall interrupt priority. |
doc/EK-TM4C1294XL.md |
Documents the TM4C129 LaunchPad. |
CONTRIBUTING.md |
Corrects project and reporting guidance. |
CMakeLists.txt |
Adds optional lwIP integration. |
CLAUDE.md |
Updates interrupt lifecycle guidance. |
AGENTS.md |
Updates architecture and workflow rules. |
.github/prompts/orchestrate.prompt.md |
Updates orchestration guidance. |
.github/instructions/hal-ti-cpp.instructions.md |
Updates C++ interrupt rules. |
.github/copilot-instructions.md |
Updates repository patterns and commands. |
.github/agents/reviewer.agent.md |
Updates review checklist. |
.github/agents/planner.agent.md |
Updates planning guidance. |
.github/agents/orchestrator.agent.md |
Updates routing guidance. |
.github/agents/executor.agent.md |
Updates implementation guidance. |
.claude/agents/reviewer.md |
Updates review checklist. |
.claude/agents/planner.md |
Updates planning guidance. |
.claude/agents/orchestrator.md |
Updates routing guidance. |
.claude/agents/executor.md |
Updates implementation guidance. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…ing handlers - Pwm, SynchronousPwm: two PinChannels on one generator (channel A and channel B) are separate Generators on the same registers; each now writes only its own GENA/GENB action, so a duty update no longer overwrites the companion's 0 %/100 % or compare action. - Gpio: clear the pending port and per-pin IRQs before their handlers enable them. - Pwm: clear stale generator and fault status and the pending NVIC bit before registering the generator and fault handlers. - WatchDog: the shared vector runs at the most urgent priority of the active watchdogs, recomputed on add and remove, and every registration clears a pending IRQ first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
…s TX DMA completions - At the receive time-out the FIFO residue was copied only into the space left in the active half; with 60 DMA'd bytes and 5-7 residue bytes the tail of a command stayed in the FIFO until more input arrived, so replies drifted out of step under load. The residue is now drained completely, reusing the half once it has been delivered, and a half that completed just before the time-out is delivered first so data stays in order. - A TX DMA done flag without a transfer in flight ran ProcessDmaTx again, which scheduled the already-cleared completion callback: the event dispatcher then aborted on an empty infra::Function. The completion is now only processed while sending and once the channel's transfer has actually stopped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
… after a DMA half The uDMA controller clears USEBURST at the end of a cycle, so the channel is armed again with burst mode restored on every start and re-arm. When a half completes and single requests have already emptied the FIFO, no receive time-out follows: the RX path is flushed right away, at most once per interrupt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The transmit interrupt fires when the FIFO drains through its trigger level, never for a FIFO that is already empty, so the first send after reset never started. SendData pends the UART interrupt and the handler fills the FIFO until it is full. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…FIFO fed SpiMaster scheduled a [this] lambda calling onDone; a close before it ran left an empty AutoResetFunction (abort). It now schedules onDone.Clone(), like UartBase. SynchronousSpiMaster sent one frame and waited for its reply, stopping the clock between bytes; it now keeps up to a FIFO of frames queued so they are clocked back to back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…keep comparator sequences converting Closing one sequencer gated the clock of the whole ADC, so closing another sequencer on it bus-faulted; Adc and SynchronousAdc now reference-count the clock per ADC. The digital comparator high band is CTC 0x3 (0xC00), not the reserved 0x2. A sequence with digital comparator steps feeds the PWM fault logic, so it starts converting when opened and keeps converting after Stop; Measure discards stale FIFO data first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… event per episode, safe teardown Start resets the counters of newly started generators together through PWMSYNC, so their edges line up. SetBaseFrequency recomputes the comparators of running generators so the duty cycle survives a frequency change. A fault pulse pends the NVIC line while the raw status follows the live fault, so a fault flooded the event loop; the line now stays disabled for the fault episode and is re-enabled after 1 ms without new pulses, and the event names the configured sources when the status is already clear. The destructor masks the PWM interrupts before releasing the handlers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GPIODATA returns the output latch for an output pin, so a released open-drain pin always read 1. Set(true) releases an open-drain pin by making it an input, Set(false) drives it low. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
QEIPOS ignores writes while the module is disabled, so the configured offset was lost. 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>
PD0/PD1/PD2 carry C0o/C1o/C2o on port-control function 5; the table used 8, which left the pin unconnected. Found on the EK-TM4C1294XL bench: with C1o on PD1 the pin stayed low while the comparator toggled, and writing 5 into PD1's PCTL nibble made the pin follow the comparator. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
Brings in the ISB after NVIC_DisableIRQ in EMIL's DisableIrq (#154), so an IRQ cannot be taken after UnregisterHandler has cleared its table entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
The fault path no longer polls with a 1 ms repeating timer or calls NVIC_DisableIRQ/NVIC_EnableIRQ directly: - HandleFaultIrq masks the fault interrupts in PWMINTEN and reports one FaultEvent, so a persistent or repeating fault cannot flood the event loop. - Stop() clears latched fault status (FLTSTAT is write-one-to-clear in latch mode) and re-enables the fault interrupts. Start() only sets duty cycles, so a control loop calling it cannot resume during a fault; the application stops, then starts again. - PWMFAULT/PWMFAULTVAL now drive the outputs of fault-configured generators to their inactive level during a fault, instead of the fault only raising an interrupt. - The destructor masks interrupts and releases the handlers before disabling the generators, without re-arming. 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
…er pin A dispatched GPIO interrupt scheduled one event-dispatcher action per edge, so an edge train faster than the event loop (200 edges at 20 kHz on the bench) filled the dispatcher queue and EMIL aborted. Each pin now counts its pending edges atomically and schedules only when the count leaves zero; the queued action runs the handler once per counted edge, so no edge is lost and the queue holds at most one action per pin. DisableInterrupt drops the pending count, and a handler removed meanwhile is not called. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
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
On the EK-TM4C123GXL every asynchronous transfer timed out. With CR1.EOT set, TXRIS never asserted at the end of transmission, so the received bytes stayed in the FIFO. The next transfer then overran it and hit really_assert in the SSI2 interrupt. A batch now completes once all of its frames have been read. Besides TXIM, the receive interrupts (RXIM for half full, RTIM for the receive time-out) also drain the FIFO, so completion no longer depends on TXRIS. Each transfer first drains stale receive data and clears ROR/RT. A zero-length transfer completes immediately. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
On the EK-TM4C123GXL at 80 MHz, interrupt-driven reception at 921600 Bd overran the receive FIFO. With the trigger at 7/8 full, the interrupt had only two characters (22 us) to start. At half full it has eight characters (87 us). UartWithDma still sets its own trigger levels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
TM4C123 erratum WDT#03: watchdog 1 resets the device on its second time-out even with WDTCTL.RESEN clear. On the EK-TM4C123GXL, wdt.start 1 reset=0 reset the board one period after the first warning, while CTL read back 0x80000001. Without reset, the interrupt handler now clears the time-out itself and reports the early warning once until the next Refresh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
TM4C123 erratum ADC#14: the first two conversions after the ADC clock is enabled may be wrong, and the workaround is a reset through SRADC. The reset also clears the averaging, delay and comparator settings a previous user left behind. 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
…e FIFO On the EK-TM4C123GXL, when the END step of a sequence was routed to a digital comparator, its interrupt fired before the previous step's sample reached the FIFO, so every run delivered one sample too few. All 20 digital-comparator HIL tests failed. Moving the sampler's routing by hand showed every sample arrives once the interrupting step goes to the FIFO. The interrupt now sits on the last FIFO step, and END stays on the last step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
On the EK-TM4C123GXL, UartWithDma at 115200 dropped up to 7 bytes, always starting at offset 18, and the bytes never reached the wire. How many were lost grew with the arbitration size: single requests moved a whole arbitration unit into a TX FIFO with fewer free entries. With USEBURST set, the channel only transfers on the half-empty burst request, so 4 items always fit. On the bench every size from 9600 to 921600 Bd went out complete, with no stretched stop bits, and the 68 DMA UART tests pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
Moving the interrupt off the comparator step (ac454b8) didn't help on the EK-TM4C123GXL: the bench showed that the END step being routed to a digital comparator is what drops one FIFO sample per run, wherever IE sits. Following the workaround for TM4C123 erratum ADC#03, a free step after it now carries END|IE into the FIFO and the driver discards its sample. With the comparator step anywhere but the END step, every sample arrives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
The Tiva configure presets use a single-config generator without a build type, so the build presets' configuration was ignored and firmware (CI's embedded builds included) compiled at -O0. On the EK-TM4C123GXL that made the UART1 interrupt too slow for 921600 Bd and skewed ADC sequencer timing. Configure with -DCMAKE_BUILD_TYPE=Debug to get an unoptimized build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX
On the EK-TM4C123GXL, with 921600 Bd full duplex, UartWithDma sometimes inserted 4-9 copies of the character received 16 earlier. A ring log showed the uDMA writing them itself, right after the switch from the primary to the alternate half. It served a burst request the UART had not yet withdrawn, and read the empty FIFO. With 1-item arbitration every extra read still finds data, because requests only come at 8 entries. 240 replay rounds and 40 test runs were clean afterwards (4/240 and 6/40 before), with no change on the console or in the full UART suite (160 passed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX

This fixes the findings from a second, independent review of
mainafter #117. Every finding was checked against the code before being fixed. I also re-checked the three High findings myself.Since then, the HIL bench (#120) has run these drivers on an EK-TM4C1294XL and an EK-TM4C123GXL. The hardware fixes it led to are listed under Found on hardware, several of them TM4C123 errata.
High
PWMINTEN.INTPWMn), soonNormalInterruptnever fired.SSInXDAT0is TX andSSInXDAT1is RX.ALTSETstate.Medium
Configis kept by value, not by a reference that dangles for temporaries. 0% and 100% duty now drive the output constant low/high; before, 0% could come out as roughly 100%.SSEMUX. Before, they sampled AIN0–3 in differential mode.ACCTL.TOENis bit 11 and is set when the ADC trigger is enabled.SystemCoreClockreports the frequency actually produced, not the requested one. The oscillator source is no longer forced to PIOSC after setup.InterruptType::immediateruns the action directly in the ISR.normal/highinterrupt priorities call FreeRTOS APIs from an ISR.Low
SystemCoreClockis 16 MHz (the reset clock).SynchronousSpiMasterdrains stale RX data correctly in its constructor.SpiMaster's interrupt handler is no longer destroyed from inside its own interrupt handler.DmaChannelstores its channel by value.GpioPin::Setwrites through the masked data address.really_assert, so they stay in release builds.Resolution()returns the configured value.instantiationsnamespace typo is fixed.DEVICE_HEADERand reports the selected PHY address.Found on hardware (HIL, #120)
PWMFAULT/PWMFAULTVALforce the outputs inactive on a fault. The ISR reports one event, andStop()clears the latch and re-arms.CMAKE_BUILD_TYPE=RelWithDebInfo. Before, the single-config generator built firmware at -O0, CI included, and the build presets' configuration had no effect.0059646, which includes the HIL watchdog auto-feed fix (embedded-infra-lib#155).Repo rules and consistency
#ifdefs in C++ (AGENTS.md rule). Family-specific constants, types and register helpers moved tohal_tiva/tiva/family/<family>/<Driver>Family.hpp, and the BSP tohal_tiva/instantiations/family/<family>/(<family>istm4c123ortm4c129). CMake adds onlyfamily/$<LOWER_CASE:${TARGET_MCU_FAMILY}>to the include path.SpiMasternow register their EMIL interrupt handler at the end of the constructor, after clearing the pending interrupt. Destructors release it first instead of callingNVIC_*directly.CanBitTiming.hpp,SpiClockDivisor.hpp); the two SPI drivers now share one implementation.terminal_uart_with_dmanow actually usesDma+UartWithDma. The newHAL_TI_INCLUDE_LWIPoption lets the lwIP instantiation build standalone.InterruptTable, so there are no per-interrupt startup-file edits. Also corrected: the handler registration order, CTest preset name,hal::cortexnamespace, CAN status write-back, CMake 3.24, thehal_tiva.tivatarget name and the broken CODEOWNERS links. Addeddoc/EK-TM4C1294XL.md.Breaking changes
AnalogComparator::Config::routeToPwmFaultis removed; it had no effect.DmaChannel::IsCompletionPending()/ClearCompletion()(TM4C123 only) are replaced byDmaChannel::ChannelNumber().Can::BitTimingis now an alias ofhal::tiva::CanBitTiming, with the same fields.NumberOfBytes::_16_bytesnow asserts; it was silently ignored before.-DCMAKE_BUILD_TYPE=Debugfor an unoptimised build.Verification
tm4c123gh6pmandtm4c1294ncpdtbuilds pass, examples included.HAL_TI_INCLUDE_LWIP=ON, the TM4C129 build passes.bundle1, full depth, async SPI included): 2652 passed and 1 failed. The failure was the UART 921600 DMA full-duplex duplicate. It is fixed by 016afa5 and confirmed clean afterwards (0/240 rounds, 40/40 runs), with no change in the full UART suite (160 passed).bundle1full, thenbundle2full, on an -O0 build): 2416 passed and 5 failed. Four were the watchdog failures fixed in EMIL; one was an SPI decode that passed on rerun.bundle2: 78 passed. The 1294 has not been re-run since the later UART, SPI, ADC and build-type changes.🤖 Generated with Claude Code
https://claude.ai/code/session_014WbtsfrCF7vqzVkHsKYMoX