Repository navigation
PSoC 6 parity and CY8CEVAL-062S2 support, with two core power cut fixes - #975
aidangarske wants to merge 4 commits into
Conversation
b3b43f3 to
800e122
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Incomplete image-header validation can treat an unknown BOOT version as 0, misclassifying a resumed forward update as a rollback and committing SUCCESS without confirmation.
2 open issues
What changed in this PR
This PR improves wolfBoot’s power-cut recovery and expands the PSoC 6 port with CY8CEVAL-062S2 support, updated drivers, and additional boot configurations.
Changes:
- Revises rollback detection and shared write-once trailer handling to preserve recovery state.
- Updates PSoC 6 flash drivers, board configuration, console, timing, and CM4 application handoff.
- Adds power-cut tests, PSoC 6 HAL tests, RAM-code checks, and CI coverage.
| File | Description |
|---|---|
| tools/unit-tests/unit-update-flash.c | Adds power-cut, rollback, and trailer recovery tests. |
| tools/unit-tests/unit-psoc6-qspi.c | Tests QSPI operations and erase geometry. |
| tools/unit-tests/unit-psoc6-flash.c | Tests internal flash operations and console output. |
| tools/unit-tests/unit-psoc6-boot.c | Tests bank mapping, vectors, timer, and CM4 handoff. |
| tools/unit-tests/unit-mock-flash.c | Adds torn-operation and flash-failure simulation. |
| tools/unit-tests/Makefile | Registers new flash variants and PSoC 6 tests. |
| tools/unit-tests/cmsis_stub/core_cm0plus.h | Provides host-side CMSIS register and intrinsic mocks. |
| tools/scripts/check-ramcode.py | Checks reachable self-update code and data placement. |
| tools/config.mk | Persists new PSoC 6 configuration options. |
| tools/ci/fetch-psoc6-pdl.sh | Fetches pinned Infineon libraries. |
| test-app/startup_arm.c | Preserves the ROM flash NMI handler on CM0+. |
| test-app/Makefile | Separates application HAL and PDL objects. |
| test-app/ARM-psoc6.ld | Expands application SRAM allocation. |
| test-app/ARM-psoc6-cm4.ld | Defines a separate CM4 application memory layout. |
| test-app/app_psoc6.c | Reports firmware version and confirms updates. |
| src/update_flash.c | Revises rollback detection and final-erase recovery. |
| src/libwolfboot.c | Preserves shared trailer records during rewrites. |
| src/boot_arm.c | Adds remote-core boot handoff. |
| include/image.h | Declares shared-trailer state operations. |
| include/hal.h | Declares the remote-core boot hook. |
| hal/psoc6.ld | Places flash dependencies and vectors in SRAM. |
| hal/psoc6.h | Defines board pins and hardware constants. |
| hal/psoc6.c | Updates flash, QSPI, console, timer, and boot support. |
| docs/Targets.md | Documents PSoC 6 configuration and update procedures. |
| config/examples/cypsoc6.config | Updates the internal-flash configuration and layout. |
| config/examples/cypsoc6-qspi.config | Adds an external-QSPI update configuration. |
| arch.mk | Integrates updated PDL libraries and target requirements. |
| .gitignore | Excludes fetched libraries and new test binaries. |
| .github/workflows/test-units.yml | Enables PSoC 6 host tests in CI. |
| .github/workflows/test-configs.yml | Adds PSoC 6 build variants. |
| .github/workflows/test-build-psoc6.yml | Fetches pinned libraries and runs optional RAM-code checks. |
Review effort: Balanced
76851ee to
41aa841
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #975
Scan targets checked: wolfboot-src, wolfboot-bugs
Not reviewed (no scan target covers them): .github/workflows/test-build-psoc6.yml, .github/workflows/test-configs.yml, .github/workflows/test-units.yml, .gitignore, arch.mk, config/examples/cypsoc6-qspi.config, config/examples/cypsoc6.config, config/examples/stm32c0-rsa2048.config, docs/Targets.md, hal/psoc6.h, hal/psoc6.ld, test-app/ARM-psoc6-cm4.ld, test-app/ARM-psoc6.ld, test-app/Makefile, tools/ci/fetch-psoc6-pdl.sh, tools/config.mk, tools/scripts/check-ramcode.py, tools/scripts/sim-update-powerfail-resume.sh, tools/unit-tests/Makefile, tools/unit-tests/cmsis_stub/core_cm0plus.h
Coverage: 10 of 13 in-scope changed file(s) opened by the reviewer; not opened: tools/unit-tests/unit-psoc6-boot.c, tools/unit-tests/unit-psoc6-flash.c, tools/unit-tests/unit-psoc6-qspi.c
Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Reported findings require changes before merge.
Review tier: Max
41aa841 to
650a380
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #975
Scan targets checked: wolfboot-src, wolfboot-bugs
Not reviewed (no scan target covers them): .github/workflows/test-build-psoc6.yml, .github/workflows/test-configs.yml, .github/workflows/test-units.yml, .gitignore, arch.mk, config/examples/cypsoc6-qspi.config, config/examples/cypsoc6.config, config/examples/stm32c0-rsa2048.config, docs/Targets.md, hal/psoc6.h, hal/psoc6.ld, test-app/ARM-psoc6-cm4.ld, test-app/ARM-psoc6.ld, test-app/Makefile, tools/ci/fetch-psoc6-pdl.sh, tools/config.mk, tools/scripts/check-ramcode.py, tools/scripts/sim-update-powerfail-resume.sh, tools/unit-tests/Makefile, tools/unit-tests/cmsis_stub/core_cm0plus.h
Coverage: 2 of 2 in-scope changed file(s) opened by the reviewer
Fenrir result: Approved ✅
No new issues found in the changed files.
Advisory only — this automated result does not count as a GitHub approval.
Review tier: Lite
Fenrir's latest completed scan found no issues; clearing the prior automated change request.
dgarske
left a comment
There was a problem hiding this comment.
Verdict
The PR works on hardware. Every documented flow passed, and the core power-cut fix held under 47 hardware resets swept across an update and a rollback. No correctness bug found. The gaps are in the documentation and the test app, plus a few low-severity code notes.
Hardware results
| Test | Result |
|---|---|
| Docs flow, internal flash: fetch PDL, build, flash, boot v1 | Pass, output matches docs |
| Docs update to v2, reset, second update to v3 (documented trigger after a confirmed update) | Pass |
cypsoc6-qspi.config: v1 internal, v2 staged in QSPI, documented FLAGS_HOME trigger |
Pass |
PSOC6_CM4_APP=1 PSOC6_CRYPTO=1 BOOT_BENCHMARK=1: boot v1 on CM4, update, CM4 calls wolfBoot_success() |
Pass, ECDSA verify 180-192 ms on the crypto block |
DUALBANK_SWAP=1: v2 written at 0x10120000, no trigger |
Pass, bank swap and confirm |
RAM_CODE=1 bootloader self-update |
Pass once both bootloaders set WOLFBOOT_VERSION (see gap 1) |
| Reset swept across the rollback (26 cuts, 40 ms steps) | 26/26: always settles on v1, unconfirmed image never boots again |
| Reset swept across the update (21 cuts, 80 ms steps) | 21/21: settles on v1 after the unconfirmed image fails to confirm |
Host results
- Unit suites touched by the PR all pass: unit-psoc6-flash, -qspi, -boot, unit-update-flash and its int-invert, invert, flagshome, delta, enc, custom-trailer, hwswap and self-update variants.
check-ramcode.py --selftestand the CI invocation on the RAM_CODE build pass (59 functions reachable, all in RAM).- Build matrix: all documented options build. Removing
NO_MPU,FLAGS_INVERTorNVM_FLASH_WRITEONCEfails with a clear message, andPSOC6_CM4_APP=1without the 1 KB header fails with a clear#error.
Gaps found acting as a new user
- Self-update silently discards the image. With the config default
WOLFBOOT_VERSION=0no define is passed, so the bootloader takesLIBWOLFBOOT_VERSION_HEX(0x020A0000). A self-update signed with any small version is erased with no console message and the old bootloader keeps running. The docs line forRAM_CODE=1needs the full recipe: build the running bootloader withWOLFBOOT_VERSION=N, build the new one with a higher value, sign with that version, write to UPDATE, set the trigger. This is upstream behavior, not a PR regression, but a log line on that erase would save the next user an hour. - The documented rollback cannot be exercised with the shipped test app. It confirms every version except 1, so "if version 2 does not call
wolfBoot_success(), the next reset rolls back" needs a code edit to try. A make knob or a version convention (for example, versions at or above some value never confirm) would make rollback testable. - A missing PDL fails with a raw compiler error.
cy_device_headers.h: No such file or directory, with no pointer totools/ci/fetch-psoc6-pdl.sh. A wildcard check inarch.mknext to the three existing$(error ...)lines would fix it. - Loading commands only show the internal layout. A QSPI user must work out from the layout paragraph that v1 goes to 0x10040000.
- No migration note. Partition addresses change, the default signature goes from ED25519 to ECC256,
CYPRESS_TARGET_LIBbecomesCYPRESS_TEMPLATE_LIB, andPSOC6_CRYPTOwas documented as on by default and is now off. A device in the field on the old layout cannot take an update from the new one. - Only pyOCD is documented. The OpenOCD/ModusToolbox instructions and the one-step
factory.binflash were removed. Most Infineon users have ModusToolbox, so one line for its OpenOCD or Programmer would help. - Only the CY8CEVAL-062S2 is listed as tested, yet the config default is the CY8CKIT-062S2-43012. Worth asking whether the default board was run on hardware.
- The QSPI trigger wording is hard to follow. "8 bytes below the BOOT flags" plus an inline python one-liner works, but a sentence on what the last 13 bytes are would help.
- Harness note, not a bug.
pyocd flashresets and runs the target when it finishes, so the trigger write starts the update immediately. Pressing reset right after it interrupts the first boot of the new image, which wolfBoot correctly treats as a failed confirm and rolls back. The docs could say "wait for the confirm line before resetting".
Code review notes (no high-severity issue)
The earlier Copilot and Fenrir comments are all addressed at 650a380: register moves are tracked in check-ramcode.py, the SMIF clock comes from CLK_PATH1/8, and the short-read test now reaches wolfBoot_swap_backup_version.
- Equal versions decide differently on a resume.
src/update_flash.c:664returns rollback forver == curon a first run but not on a resume. A cut in the final erase of an equal-version rollback would then write TESTING and ping-pong again. Equal versions are refused by a normal update, so this needs debugger staging orALLOW_DOWNGRADE. Low. ALLOW_DOWNGRADEandDELTA_UPDATESkeep the old behavior. The PR states that scope, but the description's "in every layout" overstates it. Low, wording only.- One trailer read in the delta branch runs with flash locked.
src/update_flash.c:1758aftertrailer_read_lock()at :1685. Pre-existing. Low. - "Other builds compile unchanged" is not quite true. The new
nvm_select_fresh_sectorpreference (src/libwolfboot.c:421-438) is also compiled forFLAGS_HOME && NVM_FLASH_WRITEONCEbuilds without a shared trailer, for example the enc flagshome unit variant. No failure found. Low. from_swapassumes the header fits in one sector (src/update_flash.c:609-627). Only matters for very small sectors with large headers. Low.- The test-app still rebuilds
hal/psoc6.owith CM4 flags. An incremental top-levelmakeafter it fails to link instead of producing a bad image. Pre-existing pattern. Low.

Four commits, meant to be reviewed one at a time. They're all in the same PR because I needed them for the PSoC 6 changes. The first two fix core wolfBoot (
src/update_flash.c,src/libwolfboot.c) for every target; the PSoC 6 port needs both becausecypsoc6-qspi.configusesFLAGS_HOMEwith write-once flash.1. Keep a rollback on the original image across a power cut (core, all targets)
wolfBoot_update()chose rollback by comparing BOOT and UPDATE versions before the swap, so a resumed swap read half swapped headers and ended the restored image in TESTING. A resumed final erase also always wrote TESTING, andwolfBoot_start()then fell back again from its pre-resume state.NVM_FLASH_ECCan erased backup is not read.wolfBoot_start()re-reads the BOOT state after a resumed final erase, also when only its cleanup erase failed. Trailer reads inwolfBoot_update()and the final erase unlock the flash, aswolfBoot_start()already does.CUSTOM_PARTITION_TRAILERkeeps its recorded direction (IMG_STATE_ROLLBACK) andEXT_ENCRYPTEDkeeps the pre-swap comparison.ALLOW_DOWNGRADEandDELTA_UPDATESkeep TESTING for a cut during the final erase.unit-mock-flash.cgains a power-cut hook (clean, half or all-but-last-word cuts), write and erase failure countdowns, and erase polarity for inverted flags. New variantunit-update-flash-int-invertcovers write-once trailers in internal flash.tools/scripts/sim-update-powerfail-resume.shexpected the bug: its last step ("make sure it can double revert") relied on a reset in the fallback leaving v1 in TESTING, so the next boots swapped the unconfirmed v2 back in with no trigger. It now checks that v1 stays booted through the same resets. Encrypted, delta andALLOW_DOWNGRADEbuilds, which keep the old decision, keep the old check. On master the new check fails for every other configuration that runs it.2. Keep the FLAGS_HOME final swap resumable across a power cut (core, FLAGS_HOME with write-once flash)
FLAGS_HOMEandNVM_FLASH_WRITEONCEthe UPDATE flags live in the BOOT trailer, andwolfBoot_swap_and_final_erase()erased that trailer,FINAL_FLAGSincluded, before writing TESTING. A power cut there left the new image booting as if confirmed, never rolling back.wolfBoot_flags_home_reset_boot()now clears only the BOOT magic and state and keepsFINAL_FLAGS. After the restore,wolfBoot_flags_home_set_boot()writes the BOOT state and clears the UPDATE flags in one rewrite: TESTING after an update, SUCCESS after a rollback.nvm_select_fresh_sector()now keeps a copy holding a whole BOOT record over one without the BOOT magic, unless that one holdsFINAL_FLAGS, so a torn erase of the old copy cannot hide the resume record.sim-nvm-writeonce-flags-home*.configand the PSoC 6 QSPI configuration.unit-update-flash-flagshome(whole-sector writes) andunit-update-flash-invert(512-byte chunks) run the suite, including the power-cut sweeps. New tests cover the update trigger cut at every operation, a torn erase that keeps the old BOOT record, and write or erase failures in both rewrites.3. Add CY8CEVAL-062S2 support and bring the PSoC 6 port to parity
EXT_FLASHandDUALBANK_SWAPbuilds did not compile on master.tools/ci/fetch-psoc6-pdl.shfetches pinned releases intolib/.cypsoc6.configcovers the CY8CKIT-062S2-43012 (the default) and the CY8CEVAL-062S2 (PSOC6_BOARD=CY8CEVAL_062S2). The newcypsoc6-qspi.configputs UPDATE in QSPI; dual-bank is make arguments.BOOT_BENCHMARK, a test-app that confirms updates, andPSOC6_CM4_APP=1, which boots the verified image on the CM4.NO_MPU=1(the ROM flash NMI locked up under the wolfBoot MPU),FLAGS_INVERT=1(flash erases to 0x00) andNVM_FLASH_WRITEONCE=1(rewriting a row erases it first).ProgramRowfor erased rows andWriteRowotherwise, skipping unchanged rows (an update went from 38 s to 6 s); write errors return -1; the vector table and the PDL flash/SMIF code run from SRAM forRAM_CODEself-update.WOLFBOOT_BOOT_REMOTE_COREhook indo_boot(), and the test-app builds private PDL and string objects, so a CM4 build never leaks into the bootloader.tools/scripts/check-ramcode.pywalks the call graph fromwolfBoot_self_updatethrough calls, literal pools, register-built constants and function-pointer data. It fails if reachable code is outside RAM or reads the flash image; the entry may only write at the image base. Its--selftestand theRAM_CODECI jobs run it.unit-psoc6-flash,unit-psoc6-qspiandunit-psoc6-bootbuild against the real PDL headers with only CMSIS mocked; each of 36 HAL mutations fails a test.4. Clock the PSoC 6 QSPI interface from the PLL
hal_ext_flash_init()fed the SMIF clockCLK_HF2fromCLK_PATH0divided by 4. wolfBoot never configures that path; it only sets up the PLL onCLK_PATH1.CLK_PATH0is the FLL, left as the last loader set it. After pyOCD programming it was enabled but unlocked (CLK_FLL_STATUS0x6), so the SMIF clock switched between the FLL and the 8 MHz IMO.Cy_SMIF_MemRead()then returnedCY_SMIF_CMD_FIFO_FULL, a correct update image failed its hash, and SFDP init needed up to four 500 ms retries.CLK_HF2now comes from the PLL divided by 8, which is 12.5 MHz, the rate the locked FLL gave, so QSPI timing is unchanged.unit-psoc6-qspichecks the source and the divider; reverting either fails the test.