Skip to content

Merged changes ahead of the v6.5.2.202603 release - #80

Merged
fdesbiens merged 21 commits into
mainfrom
dev
Oct 2, 2026
Merged

fdesbiens merged 21 commits into
mainfrom
dev

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

Brings main up to the 6.5.2.202603 release content: twenty-one commits adding the STM32 NUCLEO-F429ZI sample, the NXP i.MX RT1064-EVK target with three demos and Renode CI, the PolarFire SoC Icicle Kit target, and the generic BSP framework the targets were moved onto, together with pinned Arm and RISC-V toolchains, cached toolchain fetches, and the contribution guide rewrite.

main carries one commit dev does not, the earlier fix for the CORE-V CLI host tests. dev has the same fix, so the merge is clean.

Two follow-ups this deliberately leaves out. The library submodules under libs/ are still pinned at 6.5.0.202601 series commits and will be repointed at the released master of each product, with .gitmodules switched to branch = master, in a separate change on top of this one. The dev pins move back to tracking dev after the release is published.

fdesbiens and others added 21 commits June 12, 2026 17:58
* refactor(stm32f767): restructure project to support scalable demos and default to threadx_basic

* feat(stm32f767zi): refactored BSP and added NetX Duo Echo demo
… and primitives showcase (#45)

* new file:   STMicroelectronics/NUCLEO_F401RE/CMakeLists.txt
	new file:   STMicroelectronics/NUCLEO_F401RE/README.md
	new file:   STMicroelectronics/NUCLEO_F401RE/app/CMakeLists.txt
	new file:   STMicroelectronics/NUCLEO_F401RE/app/common/board_init.c
	new file:   STMicroelectronics/NUCLEO_F401RE/app/common/board_init.h
	new file:   STMicroelectronics/NUCLEO_F401RE/app/common/console.c
	new file:   STMicroelectronics/NUCLEO_F401RE/app/common/sntp_client.c
	new file:   STMicroelectronics/NUCLEO_F401RE/app/common/startup/NUCLEO_F401RE.ld
	new file:   STMicroelectronics/NUCLEO_F401RE/app/common/startup/startup_stm32f401xe.s
	new file:   STMicroelectronics/NUCLEO_F401RE/app/common/startup/tx_initialize_low_level.S
	new file:   STMicroelectronics/NUCLEO_F401RE/app/common/stm32cubef4/stm32f4xx_hal_msp.c
	new file:   STMicroelectronics/NUCLEO_F401RE/app/starter/cloud_config.h
	new file:   STMicroelectronics/NUCLEO_F401RE/app/starter/main.c
	new file:   STMicroelectronics/NUCLEO_F401RE/cmake/FindCMSIS.cmake
	new file:   STMicroelectronics/NUCLEO_F401RE/cmake/FindSTM32HAL.cmake
	new file:   STMicroelectronics/NUCLEO_F401RE/cmake/arm-gcc-cortex-m4.cmake
	new file:   STMicroelectronics/NUCLEO_F401RE/cmake/arm-gcc-cortex-toolchain.cmake
	new file:   STMicroelectronics/NUCLEO_F401RE/cmake/utilities.cmake
	new file:   STMicroelectronics/NUCLEO_F401RE/lib/CMakeLists.txt
	new file:   STMicroelectronics/NUCLEO_F401RE/lib/nucleo_bsp/CMakeLists.txt
	new file:   STMicroelectronics/NUCLEO_F401RE/lib/nucleo_bsp/nucleo_bsp.c
	new file:   STMicroelectronics/NUCLEO_F401RE/lib/nucleo_bsp/nucleo_bsp.h
	new file:   STMicroelectronics/NUCLEO_F401RE/lib/stm32cubef4/CMakeLists.txt
	new file:   STMicroelectronics/NUCLEO_F401RE/lib/stm32cubef4/config/stm32f4xx_hal_conf.h

* Cmake files populated, HAL library and boot code copied and modified

* Startup files obtained from official repositories and adjusted to meet board specs and ThreadX requirements

* Fixed build sequence

* STM32F401RE: achieve first successful ThreadX hardware bring-up

Booted ThreadX on NUCLEO-F401RE
Validated scheduler operation
Validated UART console output
Validated LED thread
Validated flashing and hardware execution

* implement ThreadX board initialization and multi-threading demo for NUCLEO-F401RE (continued)

* Fixed licensing, added documentation

* added CMake build configuration for NUCLEO-F401RE board

* implemented dynamic memory management, added monitor thread

* added bytepools and demonstrations of threadx features such as mutex and event flags. Modified demo README file to reflect changes.

* Implemented block pools to prevent fragmentation of memory. Added semaphore functionality and 1 hz applicaion timer.

* Added missing licensing header to main.c

* Fixed incomplete NUCLEO licensing headers

---------

Co-authored-by: Ammar Okla <ammargawkla@gmail.com>
Co-authored-by: Frédéric Desbiens <frederic.desbiens@eclipse-foundation.org>
Co-authored-by: Codex <codex@openai.com>
Added an STM32F767ZI environmental station demo using ThreadX, FileX, NetX Duo, MQTT, and an HTS221 sensor with mock fallback. Included resilient telemetry queueing, local RAM-disk logging, DHCP/DNS handling, MQTT reconnection, and updated board support and documentation.

---------

Signed-off-by: alieissa-commits <ali.eissa.dev@gmail.com>
Co-authored-by: Frédéric Desbiens <frederic.desbiens@eclipse-foundation.org>
Co-authored-by: Codex <codex@openai.com>
* Add Microchip PolarFire SoC target, reusable BSP framework, and Renode CI pipeline

* Add dynamic runtime self-tests for _sbrk bounds, timer catch-up, and queue integrity

* Refine timer catch-up test and add per-thread run counters

* Initialize early mtvec, add deliberate timeout and trap testing hooks, and port collision fix

* Separate synchronous trap dispatch from interrupt context save

* feat(polarfire): Implement and dynamically verify PLIC Hart 1 configuration and MMUART1 interrupt routing

* Fixed PLIC machine-mode context and made the Renode self-tests gate CI

Addressed the outstanding review items on the PolarFire SoC target.

PLIC: plic.h programmed context 2, which is the supervisor-mode context for
u54_1 on PolarFire SoC; the machine-mode context for that hart is context 1.
Writing the supervisor context is silently accepted by the PLIC but leaves the
machine-mode enable bitmap clear, so MEIP never asserts and MMUART1 interrupts
could not be delivered. Threshold/claim move to 0x0C201000/0x0C201004 and the
enable word to 0x0C002088. The context and enable-word offsets are now derived
from the source ID rather than hardcoded.

Self-tests: run_startup_self_tests() printed "All startup verification tests
PASSED!" unconditionally, and test_renode.py grepped for exactly that string,
so a failing sub-test still produced a green build. Results now feed a failure
counter that decides the summary line, and the harness fails on any "[-] FAIL:".

Renode harness: the exit code considered only ticks and the alarm, discarding
the self-test and PLIC-RX flags it computed. All four assertions now gate the
result, and the harness injects a byte into MMUART1 so the PLIC path is
actually exercised instead of only having its registers inspected.

The timer catch-up self-test no longer writes mtime. That register is the
platform-wide monotonic counter shared by every hart, and this demo is meant to
be copied. Only the per-hart mtimecmp is staged into the past, with the expected
result bracketed by mtime sampled either side of hwtimer_ack().

Also:
- Hardened _sbrk() in templates/target, which is the copy-me template: bounds
  check against BSP_RAM_END, reject underflow and pointer overflow, return
  (void *)-1 with errno, and take ptrdiff_t so it stays correct on 64-bit.
- Implemented __malloc_lock/__malloc_unlock instead of leaving them empty, so
  the newlib arena is genuinely serialised rather than only appearing to be.
- Gave the tick source one owner: _tx_initialize_low_level calls hwtimer_init,
  board_init handles board peripherals, so board_init no longer runs twice.
- Pinned Renode to 1.16.1 and checksum-verified both CI downloads.
- Updated polarfire_demo.robot, which asserted on telemetry strings the demo no
  longer emits, and added an RX interrupt case.
- Derived the demo sleep intervals from TX_TIMER_TICKS_PER_SECOND.
- Corrected the architecture.md tree and normalised the licence URL to the form
  used in AGENTS.md.

* Fixed the timer catch-up self-test to exercise pure arithmetic

The previous self-test staged mtimecmp eight ticks behind by subtracting from
mtime. Renode starts mtime near zero, so that subtraction underflowed to
2^64-79900 and the test failed itself in CI. The production clamp was never
wrong; the test fed it a value real code cannot produce.

Extracted the catch-up decision into hwtimer_next_cmp(), a pure function of the
current comparand and mtime, and left hwtimer_ack() as a thin wrapper. The test
now covers both branches with synthetic values and touches no CLINT register at
all, which also removes the last reason for the demo to write timer state.

Replaced the TX_TIMER_TICKS_PER_SECOND fallback in hwtimer.h with
BSP_TICK_RATE_HZ in board_config.h. The BSP does not see the ThreadX headers,
so that fallback silently applied whenever the two disagreed; main.c sees both
and now carries a C99 compile-time check that they match.

The Renode harness no longer stops at the first failing self-test, so a single
failure reports the state of every other assertion instead of hiding it.

* Made the Renode run deterministic so the PLIC interrupt is actually exercised

The RX assertion added in the previous commit never fired in CI. Renode does not
read its monitor from stdin (it logs "Monitor available in telnet mode on port
1234"), so the byte the harness wrote was silently discarded and the PLIC path
still went untested.

Added renode/polarfire_ci.resc, which steps through fixed virtual-time intervals
with emulation RunFor, injects the byte itself via WriteChar, and quits. The run
is reproducible and terminates on its own rather than depending on wall clock.
It duplicates the machine setup instead of including polarfire_demo.resc because
Renode expands $ORIGIN only in variable assignment, so an included script cannot
be located relative to the file including it; both "include @$ORIGIN/..." and a
bare relative include hang the process rather than reporting an error.
polarfire_demo.resc is left free-running for interactive use.

Verified locally against Renode 1.16.1, both directions:

  context 1  -> [Console RX] PLIC IRQ 91 handled: byte 'X' ...   harness exit 0
  context 2  -> no RX line                                       harness exit 1

Both builds print an identical "[+] PASS: PLIC Hart 1 (IRQ 91 prio=1
en=0x08000000 thresh=0 mie=0x800)" self-test line, which is the point: register
readback cannot separate a correct context from an incorrect one, and only a
delivered interrupt can.

Also added timeout-minutes to both CI jobs as a backstop, and corrected the
target README, whose expected-output block still showed telemetry strings the
demo stopped emitting.

---------

Co-authored-by: Ammar Okla <ammargawkla@gmail.com>
Co-authored-by: Frédéric Desbiens <frederic.desbiens@eclipse-foundation.org>
…50)

* feat(nucleo): Relocate NUCLEO_F401RE into targets and migrate to generic BSP framework

* Bounded the newlib heap to its linker reservation

_sbrk() bounded the heap at BSP_RAM_END (0x20018000), the end of physical
SRAM. On this target that is not a guard at all. NUCLEO_F401RE.ld lays RAM
out as:

    _end                     0x20001170  heap base
    _end + _Min_Heap_Size    0x20001370  intended heap ceiling
    __RAM_segment_used_end__ 0x20001770  ThreadX byte pool starts here
    byte pool end            0x20017000
    _estack / BSP_RAM_END    0x20018000

so malloc() could grow the break through the reserved main stack, through
the entire ThreadX byte pool holding every thread stack and the queue
buffer, and into the live MSP, all while _sbrk() reported success. Only an
allocation past the top of SRAM returned ENOMEM.

The linker script now exports _heap_limit at the end of the heap
reservation and _sbrk() bounds against that, which keeps the heap inside
the 0x200 the script already set aside for it. Both bounds checks are also
computed in uintptr_t rather than by forming an out-of-bounds pointer
first, and the negative branch derives its magnitude without negating
PTRDIFF_MIN. BSP_RAM_END keeps documenting the memory map but now carries
a note against reusing it as a heap bound.

All newlib syscall overrides move into newlib_stubs.c, which the
application compiles directly. _write() and _read() previously sat in
bsp_console.c inside libbsp.a, where the linker extracted them only
because bsp_board.o happened to reference bsp_console_init(); breaking
that incidental chain would have silently handed printf to the libnosys
stubs, and CI only checks that the ELF exists. Console input stays board
specific behind nucleo_console.h, since the shared <bsp/console.h>
contract is write-only.

The BSP library is renamed nucleo_bsp, matching polarfire_bsp and freeing
the bare "bsp" target name.

The AI Disclosure header on newlib_stubs.c records the Codex-assisted
origin of the syscall shims moved in from console.c. That work was done by
Frederic Desbiens while editing PR #45, not by the PR author; the
squash-merge in 1bddd17 assigned git authorship to the contributor and its
Co-authored-by trailers are the accurate record.

* Restored console error handling and the AI disclosure headers

bsp_console_init() dropped the return value of HAL_UART_Init(). The code it
replaced routed the failure to Error_Handler(); the migration left the call
bare, so a UART that failed to initialise booted on silently with no
console. MISRA C:2012 Rule 17.7. The status is checked again and a failure
now halts, matching the previous behaviour.

The UART handle was a non-static global named UartHandle, but the extern
declaration that used to live in board_init.h went away with that header,
leaving an object with external linkage that nothing declared (Rule 8.4 and
8.7). It is now file-scope static console_uart. The redundant GPIOA and
USART2 clock enables are gone as well: HAL_UART_MspInit() already owns that
configuration, which is what "single UART owner" should mean. The const
cast required by HAL_UART_Transmit() is documented as a Rule 11.8
deviation.

bsp_board.c, bsp_led.c and bsp_console.c carry the AI Disclosure header
required by AGENTS.md for new files. They derive from board_init.c,
nucleo_bsp.c and console.c, each of which carried "Some portions generated
by Codex (GPT-5)"; the rename dropped the disclosure while keeping most of
the content, and git scores bsp_board.c as an 80% similarity rename.

That Codex work was Frederic Desbiens', done while editing another
contributor's PR under the AGENTS.md exception permitting edits to
incoming PRs. It reached the tree through 1bddd17, a GitHub squash-merge
of PR #45 whose git author is the PR author but whose trailers name the
real participants:

    Co-authored-by: Ammar Okla <ammargawkla@gmail.com>
    Co-authored-by: Frederic Desbiens <frederic.desbiens@eclipse-foundation.org>
    Co-authored-by: Codex <codex@openai.com>

The disclosure is therefore not a statement about this PR's author.

* Pinned the ARM toolchain to GCC 14 and refreshed the target documentation

The build-arm-nucleo job installed the distribution's gcc-arm-none-eabi,
which is GCC 13.2 on ubuntu-24.04, so the compiler drifted with the runner
image. AGENTS.md specifies GCC 14 and the sibling RISC-V job already pins
xPack GCC 14.2.0 by URL; the ARM job now pins Arm GNU Toolchain 14.2.Rel1
the same way. The toolchain bundles its own newlib, so the separate newlib
packages are no longer installed.

CMAKE_C_STANDARD moves from 11 to 99 per the AGENTS.md C99 requirement.
The target builds clean under both GCC 13.2.1 and the pinned GCC 14.2.1
with -Wall -Wshadow -Wdouble-promotion -Werror.

README.md moved with git mv but still described the pre-migration layout:
the build directory as STMicroelectronics/NUCLEO_F401RE, the HAL timebase
overrides as living in board_init.c, and the BSP as lib/nucleo_bsp/. All
three are corrected and the BSP entry now lists the four sources behind the
generic interfaces.

The Validation Record quoted 18244 B ROM and 5632 B RAM against a toolchain
the CI pipeline does not use. Re-measured against the newly pinned GCC
14.2.1: 20120 B ROM and 6000 B RAM. The hardware verification checklist is
left as the contributor recorded it.

---------

Co-authored-by: Ammar Okla <ammargawkla@gmail.com>
Co-authored-by: Frédéric Desbiens <frederic.desbiens@eclipse-foundation.org>
The ARM target had no runtime test. Its CI job checked only that the ELF
existed, so the clock configuration, the TIM2 HAL timebase, the console and
the heap bound were all unexercised. This brings it to parity with the
PolarFire target, which gates CI on a headless Renode run.

Renode ships no NUCLEO-F401RE board description, so renode/nucleo_f401re.repl
derives one from the generic STM32F4 CPU platform and corrects Flash to
512 KB and SRAM to 96 KB. The generic platform is sized for the larger
F407/F429 parts, and both the linker script's heap reservation and the
_sbrk() bound depend on those limits being right.

main.c gains seven startup self-tests that run before tx_kernel_enter(), so
a failure is reported even when the scheduler never starts. They cover the
_sbrk() allocate, release, underflow and over-limit paths, the invariant
that the heap reservation ends at or below the ThreadX byte pool, the
84 MHz SystemCoreClock, and that TIM2 still ticks after HAL_RCC_ClockConfig()
re-enters HAL_InitTick().

Test 4 is the regression guard for the heap bound fixed in #50. Requesting
32 KB fits inside the 96 KB SRAM but far exceeds the heap reservation.
Reinstating the old end-of-SRAM bound was verified to fail the suite:
newlib's first malloc() then took roughly 4 KB and put the break at
0x20002170, inside the ThreadX byte pool, which also broke self-tests 1
and 3. With the bound correct, _sbrk() refuses the oversized request,
newlib retries smaller, and malloc(64) succeeds using 72 bytes of the
512-byte reservation - so the reservation is adequate and no heap growth
is needed.

scripts/test_renode.py drives nucleo_f401re_ci.resc, which advances a fixed
span of virtual time and quits on its own rather than depending on wall
clock. Beyond the self-tests it asserts the boot banner, that the blink
thread and the 1 Hz application timer have both run (covering the LED path
and the timer service), and that the mutex, queue, event-flag and semaphore
counters are all non-zero. The suite was confirmed to exit 1 on a
reintroduced bug and 0 on the fixed tree.

The Robot Framework suite covers the same ground for renode-test, and the
new test-nucleo-renode CI job mirrors test-polarfire-renode.

main.c also drops a hardcoded 0x20018000 in favour of BSP_RAM_END now that
board_config.h is in scope, with the 4 KB main-stack margin named.
…works (#52)

templates/target/ documented a structure the repository does not have, and
following it produced a build that could not configure. Its app/CMakeLists.txt
compiled ../../../../apps/threadx_demo/main.c, but there is no apps/ directory
and never has been, so the first thing a new contributor hit was a CMake error
on a path that does not exist.

Of the three shared root directories the template declared governed, only one
was real:

  /bsp    real - both framework targets implement board.h, led.h, console.h
  /cmake  dead - cmake/utilities.cmake was referenced by nothing. All four
          targets carry their own copy, and the root file was byte-identical
          to the NUCLEO one apart from a license URL typo
  /apps   absent - referenced by the template, docs/architecture.md, and the
          template's own CMake, but never created

The template now says what the framework does: applications and toolchain
files live with their target. The claim that applications have no compile-time
dependency on vendor headers is retired rather than restated, because neither
existing demo satisfies it - both include their board_config.h for memory
sizing and vendor headers for board-specific startup self-tests. A portable
shared application layer stays documented as a goal, labelled as one.

templates/target/app/main.c is added because the template now points at it. It
depends only on <tx_api.h> and the <bsp/...> contracts and uses static thread
stacks, so it avoids needing the board's memory extents and will compile for
any target implementing the interfaces. It is a starting point to grow in
place, not a shared application.

Also removed the template's dead SHARED_APP_DIR and its guarded include of
${SHARED_CMAKE_DIR}/gcc-arm-none-eabi.cmake, a file that was never added, so
the include silently never fired. The template's BSP CMake now uses
SHARED_BSP_DIR instead of a five-level relative path, matching what both real
targets do, and CMAKE_C_STANDARD moves from 11 to 99 per AGENTS.md.

The deleted cmake/utilities.cmake is recoverable from history if the intent
was for it to become the shared location; nothing referenced it in this state,
and leaving a duplicate that looks authoritative invites edits that have no
effect.

Verified the NUCLEO target still builds clean (22068 B ROM, 6000 B RAM) and
its Renode suite still passes; neither target used the removed file.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
… pattern (#53)

The ARM job pinned 14.2.Rel1, chosen only because it was the newest release
at the time. eclipse-threadx/threadx already pins 14.3.rel1 in
ci_cortex_m.yml for the Cortex-M ports, so samplex was lagging the flagship
repository rather than choosing between two versions. Both satisfy the
AGENTS.md GCC 14 requirement; matching threadx means a toolchain bump is one
reviewable line in each repository instead of a per-repository decision.

The install steps now mirror ci_cortex_m.yml rather than paraphrasing it,
which fixes three weaknesses in the version this replaces:

  - The archive was fetched with no integrity check at all. It is now
    verified with sha256sum against the published .sha256asc.
  - Roughly 150 MB was downloaded on every run. actions/cache, keyed on the
    pinned version, now avoids that.
  - wget -q hid transfer detail. curl -fsSL fails the step on an HTTP error,
    and a new step reports the resulting compiler version so the log records
    what actually built the ELF.

Version and target move into job env vars (GCC_VERSION, GCC_TARGET) using the
same names threadx uses, so the two files stay diffable and a future bump is
a one-line change.

Re-measured the NUCLEO validation record under the newly pinned compiler:
22064 B ROM, down 4 bytes from 14.2.1; RAM unchanged at 6000 B. The figures
and the pin move together deliberately, since letting them drift is what left
a stale 15.2.1 measurement in this file before.

Verified under Arm GNU Toolchain 14.3.Rel1: clean build with -Wall -Wshadow
-Wdouble-promotion -Werror, no new diagnostics from the point release, and
the headless Renode suite still passes with all seven startup self-tests.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The PolarFire job pinned xPack riscv-none-elf-gcc 14.2.0-1 while the ARM job
now pins Arm GNU Toolchain 14.3.Rel1, so the two cross-compilers in this
pipeline were a minor release apart for no reason. xPack published v14.3.0-1
on 2025-10-23; moving to it puts both targets on GCC 14.3 and keeps the
AGENTS.md GCC 14 requirement satisfied.

The published SHA256 is updated alongside the version. The existing checksum
verification is kept as it was: this job already verified its download, which
is why it needed no other hardening.

Verified locally with xPack GNU RISC-V Embedded GCC 14.3.0:

  - Clean build of polarfire_icicle_demo.elf.
  - Headless Renode suite passes, exit 0, with all startup self-tests green:
    _sbrk bounds, HWTimer catch-up clamp, PLIC Hart 1 configuration, the queue
    round-trip, ThreadX ticks, the LM75 alarm, and the PLIC RX interrupt.

The linker warning about a LOAD segment with RWX permissions is unchanged by
this bump. It is emitted identically by 14.2.0 in the current pipeline, so it
predates this change and is left for separate treatment rather than folded in
here.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two problems in the pipeline, one of them mine.

The NUCLEO Renode job fetched renode-latest.linux-portable.tar.gz with no
version pin and no checksum, while the PolarFire job three jobs above it
already pinned Renode 1.16.1 and verified its SHA256. That pin landed in #49,
so it was present in this file when #51 added the NUCLEO job; the new job was
modelled on an older copy of the PolarFire step rather than the current one.
The result was a suite whose emulator could change under it on any Renode
release, with nothing verifying what was downloaded. The NUCLEO job now uses
the same pinned, checksum-verified step as PolarFire. The checksum was
recomputed from the published artefact rather than copied on trust.

Separately, every run re-downloaded roughly a gigabyte: the xPack RISC-V
toolchain at ~414 MB and Renode at ~52 MB in each of two jobs. All three are
now restored by actions/cache, keyed on the pinned version so a future bump
invalidates the cache instead of silently serving the old one. This completes
what #53 started for the Arm toolchain.

Caching only makes sense because these are now pinned. Caching an unpinned
"latest" artefact would have frozen CI on whichever build happened to be
fetched first, turning a reproducibility gap into an invisible one.

All four jobs now follow the same shape: cache, install only on a cache miss,
then put the tool on PATH as a separate step so it runs on hit and miss alike.
The PolarFire job also gains a version-reporting step, matching the Arm job, so
the log records which compiler produced the ELF.

Verified both constructed download URLs resolve, and that the Renode 1.16.1
checksum matches the published artefact.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…#56)

The framework's stated purpose is that application code can be built for
any board implementing the `bsp/` contracts. Neither demo could be,
because both `main.c` files reached past those contracts for two things
no portable application can see: the board's memory extents, and the
hardware specifics its startup self-tests assert on.

Neither was ever application logic. The self-tests test the BSP - linker
reservations, clock trees, interrupt controllers - so they belong in the
BSP. Two new contracts move both behind the boundary:

* `bsp/selftest.h` - `bsp_self_test()` runs the board's checks and
  reports each through an application-supplied callback, so message
  formatting (and therefore the choice between `printf()` and
  `bsp_console_write()`) stays with the application while the checks stay
  with the board.
* `bsp/memory.h` - `bsp_ram_region()` reports what RAM the application
  may claim, clamped against whatever the board reserves.

Both targets implement both, and both `main.c` files now include nothing
but the C standard headers, `tx_api.h` and `<bsp/...>`. The NUCLEO byte
pool comes out byte-identical at 88204 bytes.

Prototyping `bsp_ram_region()` on the PolarFire first surfaced a latent
bug there. Its `_sbrk()` bounded the heap at the end of DRAM, which is
the same mistake the NUCLEO shipped with before #50: harmless only
because nothing else claimed that memory. The moment `bsp_ram_region()`
promises it to an application, an oversized `malloc()` could take memory
holding thread stacks. The heap is now bounded against a documented 64 KB
reservation that `bsp_ram_region()` skips, and a new self-test guards the
bound the way the NUCLEO's test 4 does. Measured heap use is under 256
bytes, so the reservation has ample headroom.

The PolarFire tick-rate compile-time check moved to `hwtimer.c`, next to
the `TICK_CYCLES` constant it guards, since it needed ThreadX headers
that a portable `main.c` should not have to pull in.

Verified locally on both targets: clean builds plus `test_renode.py`
green on Cortex-M4 and RV64. Both suites were also confirmed to still
have teeth - weakening each target's `_sbrk()` bound back to the end of
RAM makes them exit 1 (three failures on the NUCLEO, one on the
PolarFire), so no assertion was lost in the move.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The framework documented a portable application layer it did not have: each
target owned its own demo under app/, so nothing checked that a demo could
actually move between boards. This adds apps/, moves the NUCLEO-F401RE demo
into apps/threadx_demo/main.c unchanged in behaviour, and has the PolarFire
SoC Icicle Kit build that same source as a second executable beside its LM75
monitor. CI runs it under Renode on 32-bit Cortex-M4 and 64-bit RISC-V and
asserts on the same console output from both, so the claim is now enforced
rather than stated.

apps/ holds portable demos; targets/<Vendor>/<BOARD>/app/ holds board-specific
ones, and a target's app/CMakeLists.txt picks either. The PolarFire LM75
monitor stays a target app because it genuinely models a sensor.

Linking a second executable first required removing a hard undefined reference:
polarfire_bsp's trap.c called console_rx_isr_callback(), a symbol only its own
demo defined, so any other application had to define a PolarFire-specific ISR
callback just to link. bsp/console.h gains a registration call in the shape
bsp_self_test() already established:

    typedef void (*bsp_console_rx_fn)(char c, void *context);
    void bsp_console_set_rx_handler(bsp_console_rx_fn handler, void *context);

The board stores a nullable pointer and checks it before dispatching, so bytes
arriving with no handler attached are dropped instead of faulting, and an
application that ignores console input defines nothing. The LM75 demo registers
its existing handler in main(); the NUCLEO-F401RE polls USART2 and so stores a
handler it never invokes, which keeps the contract uniform enough to register
against unconditionally. A weak symbol was rejected: it keeps the up-call and
is a GCC extension in a C99 codebase.

Building the demo for a second architecture found two real defects:

  - Every %lu in the demo was wrong on one target. ThreadX defines ULONG as
    unsigned long on the Cortex-M4 port and unsigned int on the RISC-V 64 one,
    so each value now casts to unsigned long, matching the existing idiom in
    the LM75 demo.
  - Both boards' _sbrk() underflow self-test asked for a fixed -128, which is
    an underflow only when nothing has allocated yet. The NUCLEO's passed by
    accident of a printf buffer malloc that failed against a small reservation;
    the PolarFire's failed outright once a demo reached printf first. Both now
    hand back one byte more than has ever been taken, which underflows whatever
    ran before them.

Thread stacks are sized in machine words rather than bytes, since every saved
register and the newlib printf() call chain double in width on a 64-bit hart:
1024 bytes on Cortex-M4 as before, 2048 on RISC-V, measured peak 1048.

Measured PolarFire heap use under printf, the first stdio on that board, is
2944 bytes; the run also completes with BSP_HEAP_RESERVE_BYTES cut to 4 KB, so
the 64 KB reservation holds unchanged.

Verified locally on both toolchains: three Renode suites green, and with the
_sbrk() bound deliberately widened the self-tests still fail - two checks on
the NUCLEO-F401RE, one on each PolarFire executable.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The per-edit disclosure named the product and model, so every tool and every
model version appended another line rather than recognising the one already
there. Deduplication was by exact string, which a version bump defeats.

Every file now carries exactly one line, fixed text naming no product:

    Portions of this file were generated with AI assistance.

written with the comment character that file already uses. Precise attribution
stays on the commit, where the Assisted-by trailer is dated and attached to the
diff it describes. A header line cannot keep that record honest, because the
code it names gets rewritten and the line stays.

Comment-only, 32 files: every removed line was a disclosure line, every added
line is the fixed text, and no file is left with zero or more than one. Nested
repositories were excluded, so no submodule content or gitlink is touched.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
samplex demonstrates Eclipse ThreadX and NetX Duo on Cortex-M4 and RISC-V, but had no
reference enablement for NXP's Cortex-M7 crossover parts, and no way to exercise the
network stack in CI without hardware.

This adds `targets/NXP/MIMXRT1064-EVK/`, built from `templates/target/` and implementing
the `bsp/include/bsp/` contract, with three demos: `threadx_basic` (scheduling, timers,
LED), `netx_echo` (ICMP, UDP and TCP echo on port 7) and `netx_trng_console` (on-chip
TRNG behind a single-session TCP shell on port 23, serving one connection at a time).
The ENET and KSZ8081 PHY drivers are vendored rather than tracked from
`getting-started`, which is on the same archive track as `iot-devkit`. Every SDK and
CMSIS download is pinned to a tag or commit and SHA256-verified. A Renode platform model
and a headless Python runner join the existing CI, reusing the pinned toolchain and
Renode steps from #53 and #55.

Renode 1.16.1, all three demos green: `threadx_basic` asserts the worker thread runs at
tick 100; `netx_echo` asserts link up, ARP, ICMP reply and 23-byte UDP and TCP echoes
across two emulated nodes; `netx_trng_console` asserts four distinct non-zero entropy
words, remote LED toggle, uptime telemetry, and that an over-long command line is
refused without dropping the session. Not run on hardware. The README records what the
emulator stubs: CCM and ANALOG are tag stubs whose registers read back fixed values, the
600 MHz banner is a compile-time constant, and Renode's TRNG model implements neither
the programming sequence nor the ENT15 block-consume semantics, so the driver's bring-up
is unverified on silicon.

Signed-off-by: Ali Eissa <ali.eissa.dev@gmail.com>
Assisted-by: Google DeepMind Antigravity (Gemini 3.8 Flash) <noreply@antigravity.google>
The NXP target and #69 were in flight at the same time, so the target landed
carrying the older per-edit disclosure that #69 replaced everywhere else. Eleven
files named the product and model inside the header's Contributors block, and
nine carried no disclosure at all.

All twenty now carry the same fixed line every other file in the repository uses,
written with the comment character already in the file. The Contributors blocks
are untouched: only the AI disclosure moves, and precise attribution stays on the
Assisted-by trailer where it is dated and attached to its diff.

Six vendored files are deliberately excluded. startup_mimxrt1064.S, the two
KSZ8081 PHY sources and the three NetX driver sources carry only their upstream
NXP or Microsoft copyright, with no Eclipse ThreadX line added, so they hold no
AI-generated work to disclose. tx_initialize_low_level.S and tx_user.h do carry
that line and are included.

Comment-only, 20 files: every removed line was a disclosure line and every added
line is the fixed text, with no file left holding zero or more than one. The
target builds clean with Arm GCC 14.3.Rel1.


Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
Signed-off-by: Frédéric Desbiens <frederic.desbiens@eclipse-foundation.org>
Clarified the boundary between Newlib system calls, the BSP, ThreadX, and the ThreadX POSIX compatibility layer.

Assisted-by: Codex (GPT-5) <noreply@openai.com>
The generic contributor guide omitted SampleX build, test, and submission
practices.

The guide now covers C99, board-specific toolchains and tests, the six
Linux CI jobs, Renode verification, attribution, and release practices.

Local Markdown links resolve and git diff --check passed. Build tests were
not run for this documentation change.

Assisted-by: Codex (GPT-6-Sol) <noreply@openai.com>
Added an STM32 NUCLEO-F429ZI sample

SampleX had no NUCLEO-F429ZI target. The NUCLEO-F767ZI one was the closest
thing, and a board this common being absent meant starting from a target for a
different family.

The new target is derived from the F767ZI one and keeps its shape: the same
tasks and the same IO, moved onto STM32F4 peripherals. It brings its own
startup, linker script and HAL configuration, a threadx_basic demo, the CMake
toolchain files, and build and SDK fetch scripts for Linux and Windows. The
F767ZI target picks up the small corrections that fell out of the comparison.

The STM32F429 SVD is fetched rather than carried. It is ST's file under
Apache-2.0 while this repository is MIT, and at 62106 lines it was most of the
change. fetch_sdk.sh now takes it from the CMSIS device family pack with the
pack version pinned and its digest checked, the way the toolchains are pinned,
and NOTICE.md declares it alongside the HAL, CMSIS Device and CMSIS Core that
were already fetched. Nothing is lost: the SVD is read only by the optional
Segger Ozone configuration and no build consumes it.

CI green. The fetch was run from a clean tree: the pack downloads, the digest
matches and tools/STM32F429.svd arrives at 2118587 bytes.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
Co-authored-by: Jacob Rosén <accounts.eclipse.org.script835@passmail.net>
Co-authored-by: Frédéric Desbiens <frederic.desbiens@eclipse-foundation.org>
* Fix CORE-V CLI host test mocks

* Added the licence header to the four new mock files

The mocks arrived without one. Every other file in this directory carries the
MIT header, and a source file entering an Eclipse project without a copyright
line is the kind of omission that is cheap now and awkward later.

The header is the plain MIT one rather than the AI disclosure variant the
neighbouring mocks carry, because nothing here establishes how these were
written and the disclosure is a statement about that.

Build clean and the CLI host tests pass.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

---------

Co-authored-by: divyanshisingh987456321 <sdivyanshi576@gmail.com>
@fdesbiens
fdesbiens merged commit 33d5f5a into main Oct 2, 2026
13 checks passed
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.

4 participants