From f55d1b50da2a81f6554f272cccba7b9281a8ddb7 Mon Sep 17 00:00:00 2001 From: Luca Burelli Date: Wed, 8 Jul 2026 16:19:31 +0200 Subject: [PATCH 1/9] get_board_details.sh: add proper escaping to input fields Escape double quotes and backslashes everywhere in the JSON output of get_board_details.sh to ensure that the output is a valid JSON. --- extra/get_board_details.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/get_board_details.sh b/extra/get_board_details.sh index cbe1a1178..a70d8b8b0 100755 --- a/extra/get_board_details.sh +++ b/extra/get_board_details.sh @@ -6,13 +6,13 @@ set -e get_boards() { - cat boards.txt | sed -e 's/\s*#.*//' | grep -E '^.*\.build\.variant=' | sed -e 's/\.build\.variant=.*//' + cat boards.txt | sed -e 's/\s*#.*//' | grep -E '^.*\.build\.variant=' | sed -e 's/\.build\.variant=.*//' -e 's/["\\]/\\&/g' } get_board_field() { board=$1 field=$2 - cat boards.txt | sed -e 's/\s*#.*//' | grep -E "^$board\\.$field=" | cut -d '=' -f2- | sed -e 's/"/\"/g' + cat boards.txt | sed -e 's/\s*#.*//' | grep -E "^$board\\.$field=" | cut -d '=' -f2- | sed -e 's/["\\]/\\&/g' } for BOARD in $(get_boards); do From 6da983f60c556b74e2d528a5e4cb755d8855d473 Mon Sep 17 00:00:00 2001 From: Luca Burelli Date: Wed, 8 Jul 2026 18:56:00 +0200 Subject: [PATCH 2/9] get_core_version.sh: export components to Zephyr VERSION file Rewrite get_core_version.sh to properly separate each version component during processing. Export a Zephyr-compatible VERSION file if a filename is provided as the first argument. --- extra/get_core_version.sh | 84 +++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 25 deletions(-) diff --git a/extra/get_core_version.sh b/extra/get_core_version.sh index 51171cee6..a75eddb1f 100755 --- a/extra/get_core_version.sh +++ b/extra/get_core_version.sh @@ -47,6 +47,9 @@ # environments the correct commit is used, even in pull requests where HEAD # might be a temporary detached commit. +# The computed version is also written to a file in the Zephyr-compatible +# VERSION format if a filename is provided as the first argument. + # Determine pre-release label based on build context if [ -z "$GITHUB_ACTIONS" ]; then # local build, highest precedence @@ -66,36 +69,67 @@ else exit 1 fi -VERSION=$(git describe --tags --exact-match --exclude '*/*' 2>/dev/null) -if [ -z "$VERSION" ]; then - DESCRIBE=$(git describe --tags --exclude '*/*' 2>/dev/null) - if [[ "$DESCRIBE" =~ ^([0-9]+\.[0-9]+\.)([0-9]+)(-.*)?-([0-9]+)-g ]]; then - maj_min="${BASH_REMATCH[1]}" - patch="${BASH_REMATCH[2]}" - prerel="${BASH_REMATCH[3]}" # optional - count="${BASH_REMATCH[4]}" -# echo "Git describe: maj_min='$maj_min' patch='$patch' prerel='$prerel' count='$count'" >&2 - if [ "$KIND" == "tag" ] ; then - # number of commits since tag is irrelevant for a tag push - label="${KIND}.${NAME}" - else - label="${KIND}.${NAME}.${count}" - fi - if [ -z "$prerel" ]; then - STEM="${maj_min}$((patch+1))-0.${label}" - else - STEM="${maj_min}${patch}${prerel}.${label}" - fi +# Get the current version from git describe. +exact_version=$(git describe --tags --exact-match --exclude '*/*' 2>/dev/null) +if [ -n "$exact_version" ] ; then + # must match ..(-) + if [[ $exact_version =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)? ]] ; then + v_maj="${BASH_REMATCH[1]}" + v_min="${BASH_REMATCH[2]}" + v_patch="${BASH_REMATCH[3]#-}" + v_extra="${BASH_REMATCH[4]}" # optional else - echo "Warning: unexpected git describe output '$DESCRIBE'" >&2 + echo "Error: unexpected tag '$exact_version'" >&2 + exit 1 fi - if [ -z "$STEM" ]; then - # no tags: build in a fork or locally, use a maxed out version with date+hash - STEM="9.9.9-$(date '+%Y%m%d-%H%M%S')" + + # no additional information + v_tweak="" +else + version_from_git=$(git describe --tags --long --exclude '*/*' 2>/dev/null) + # must match ..(-)--g + if [[ $version_from_git =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)?-([0-9]+)-g.* ]]; then + v_maj="${BASH_REMATCH[1]}" + v_min="${BASH_REMATCH[2]}" + v_patch="${BASH_REMATCH[3]#-}" + v_extra="${BASH_REMATCH[4]}" # optional + count="${BASH_REMATCH[5]}" + else + echo "Error: unexpected git describe output '$version_from_git'" >&2 + exit 1 + fi + + if [ -z "${v_extra}" ]; then + v_patch=$((v_patch+1)) + v_extra="-0" + fi + + if [ "$KIND" == "tag" ] ; then + # number of commits since tag is irrelevant for a tag push + v_extra="${v_extra}.${KIND}.${NAME}" + else + v_extra="${v_extra}.${KIND}.${NAME}.${count}" fi # If HEAD_REF is not set, we're not in CI but in a local clone. Use the # implicit HEAD and include --dirty to test for uncommitted changes. - VERSION="${STEM}+$(git describe --always ${HEAD_REF:---dirty})" + v_tweak="+$(git describe --always ${HEAD_REF:---dirty})" fi + +VERSION="${v_maj}.${v_min}.${v_patch}${v_extra}${v_tweak}" echo $VERSION + +if [ -n "$1" ]; then + # Write the version components to a file in Zephyr-compatible format, + # removing leading separators from EXTRAVERSION and VERSION_TWEAK. + cat > $1 << EOF +# This file is auto-generated by get_core_version.sh. Do not edit. +# Generated from version string: ${VERSION} + +VERSION_MAJOR = ${v_maj} +VERSION_MINOR = ${v_min} +PATCHLEVEL = ${v_patch} +EXTRAVERSION = ${v_extra#-} +VERSION_TWEAK = ${v_tweak#+} +EOF +fi From 1fb6e8b59552f0b51566d8df37cce1e23b2caff0 Mon Sep 17 00:00:00 2001 From: Luca Burelli Date: Wed, 8 Jul 2026 19:01:07 +0200 Subject: [PATCH 3/9] build.sh: support writing to platform.local.txt Add the possibility of writing to platform.local.txt instead of boards.local.txt when the board variable is not set. This allows for global configuration settings that are not specific to any particular board to be saved. --- .gitignore | 1 + extra/build.sh | 45 ++++++++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 53a2144bc..2c2e2269f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /firmwares/* /venv/ /boards.local.txt +/platform.local.txt llext-edk/ cflags.txt diff --git a/extra/build.sh b/extra/build.sh index 72396c924..ab084a1f2 100755 --- a/extra/build.sh +++ b/extra/build.sh @@ -138,41 +138,48 @@ update_local_field() { local field=$1 local value="$2" local comment="$3" - local full_field_name="${board}.${field}" + + if [ -z "$board" ]; then + local file="platform.local.txt" + local full_field_name="${field}" + else + local file="boards.local.txt" + local full_field_name="${board}.${field}" + fi local match_regexp="${full_field_name//./\\.}" # escape dots + if [ ! -f $file ] ; then + cat << EOF > $file +######################################################################################### +# +# AUTO GENERATED FILE - DO NOT EDIT +# This file is manipulated by extra/build.sh; manual changes may be overwritten. +# +######################################################################################### + +EOF + fi + if [ -n "$comment" ]; then # if there's a comment, add/update it as a commented-out line above the actual field - if grep -qE "^# ${match_regexp}:" boards.local.txt; then - sed -i -e "s/^# ${match_regexp}:.*/# ${full_field_name}: ${comment}/" boards.local.txt + if grep -qE "^# ${match_regexp}:" $file; then + sed -i -e "s/^# ${match_regexp}:.*/# ${full_field_name}: ${comment}/" $file else - echo "# ${full_field_name}: ${comment}" >> boards.local.txt + echo "# ${full_field_name}: ${comment}" >> $file fi fi # update the actual field line - if grep -qE "^${match_regexp}" boards.local.txt; then - sed -i -e "s/^${match_regexp}=.*/${full_field_name}=${value}/" boards.local.txt + if grep -qE "^${match_regexp}" $file; then + sed -i -e "s/^${match_regexp}=.*/${full_field_name}=${value}/" $file else - echo "${full_field_name}=${value}" >> boards.local.txt + echo "${full_field_name}=${value}" >> $file fi } # update properties on boards.local.txt from the generated files if [ ! -z "$board" ]; then - if [ ! -f boards.local.txt ] ; then - cat << EOF > boards.local.txt -######################################################################################### -# -# AUTO GENERATED FILE - DO NOT EDIT -# This file is manipulated by extra/build.sh; manual changes may be overwritten. -# -######################################################################################### - -EOF - fi - # sketch load address: start of sketch partition, hex (exact) CODE_ADDR=$(get_value_from_text_file variants/${variant}/syms-static.ld '_sketch_start') if [ -z "$upload_offset" ] ; then From 71e4f3e2f3c239ebaaaf9cf06d03c6d031fa2f04 Mon Sep 17 00:00:00 2001 From: Luca Burelli Date: Wed, 8 Jul 2026 19:04:57 +0200 Subject: [PATCH 4/9] build.sh: store per-board and global build version The build.sh script now saves the build version to loader/VERSION (so it is picked up by the Zephyr build system) and updates the version field in both platform.local.txt and boards.local.txt. To ensure multiple boards share the same version, the build_all.sh script exports a global PINNED_CORE_VERSION environment variable so that get_core_version.sh can use it instead of generating a new version for each run. --- extra/build.sh | 11 +++++ extra/build_all.sh | 3 ++ extra/get_core_version.sh | 88 +++++++++++++++++++++++---------------- extra/package_core.sh | 2 + 4 files changed, 69 insertions(+), 35 deletions(-) diff --git a/extra/build.sh b/extra/build.sh index ab084a1f2..3ab542027 100755 --- a/extra/build.sh +++ b/extra/build.sh @@ -68,7 +68,11 @@ else fi fi +# Save the build version to loader/VERSION and for later use in local files +build_version=$(extra/get_core_version.sh loader/VERSION) + echo +echo "Build version: $build_version" echo "Build target: $target $args" # Get the variant name (NORMALIZED_BOARD_TARGET in Zephyr) @@ -180,6 +184,13 @@ EOF # update properties on boards.local.txt from the generated files if [ ! -z "$board" ]; then + # save version to both platform.local.txt and boards.local.txt: + # - the platform one is reported by the IDE as _the_ core version; + # - the board-specific one is used by the auto-update-loader feature + # (when developing, each board build should be tracked separately). + board="" update_local_field "version" "$build_version" + update_local_field "version" "$build_version" + # sketch load address: start of sketch partition, hex (exact) CODE_ADDR=$(get_value_from_text_file variants/${variant}/syms-static.ld '_sketch_start') if [ -z "$upload_offset" ] ; then diff --git a/extra/build_all.sh b/extra/build_all.sh index 38c4174e1..70ddf5c1f 100755 --- a/extra/build_all.sh +++ b/extra/build_all.sh @@ -27,6 +27,9 @@ if [ ! -z "$GITHUB_STEP_SUMMARY" ] ; then echo "### Variant build results:" >> "$GITHUB_STEP_SUMMARY" fi +# This will force all boards to share the same build version +export PINNED_CORE_VERSION=$(extra/get_core_version.sh) + final_result=0 while read -r item; do board=$(jq -cr '.board' <<< "$item") diff --git a/extra/get_core_version.sh b/extra/get_core_version.sh index a75eddb1f..5c2b2e913 100755 --- a/extra/get_core_version.sh +++ b/extra/get_core_version.sh @@ -69,51 +69,69 @@ else exit 1 fi -# Get the current version from git describe. -exact_version=$(git describe --tags --exact-match --exclude '*/*' 2>/dev/null) -if [ -n "$exact_version" ] ; then - # must match ..(-) - if [[ $exact_version =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)? ]] ; then +if [ -n "$PINNED_CORE_VERSION" ] ; then + # If PINNED_CORE_VERSION is set, use it as the version (extract fields) + # must match ..(-)(+) + pattern='^([0-9]+)\.([0-9]+)\.([0-9]+)(-[^+]*)?(\+.*)?' + if [[ $PINNED_CORE_VERSION =~ $pattern ]]; then v_maj="${BASH_REMATCH[1]}" v_min="${BASH_REMATCH[2]}" - v_patch="${BASH_REMATCH[3]#-}" - v_extra="${BASH_REMATCH[4]}" # optional + v_patch="${BASH_REMATCH[3]}" + v_extra="${BASH_REMATCH[4]}" # optional, lead - + v_tweak="${BASH_REMATCH[5]}" # optional, lead + else - echo "Error: unexpected tag '$exact_version'" >&2 + echo "Error: unexpected pinned ver '$PINNED_CORE_VERSION'" >&2 exit 1 fi - - # no additional information - v_tweak="" else - version_from_git=$(git describe --tags --long --exclude '*/*' 2>/dev/null) - # must match ..(-)--g - if [[ $version_from_git =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)?-([0-9]+)-g.* ]]; then - v_maj="${BASH_REMATCH[1]}" - v_min="${BASH_REMATCH[2]}" - v_patch="${BASH_REMATCH[3]#-}" - v_extra="${BASH_REMATCH[4]}" # optional - count="${BASH_REMATCH[5]}" + exact_version=$(git describe --tags --exact-match --exclude '*/*' 2>/dev/null) + if [ -n "$exact_version" ] ; then + # this is a tagged build, extract the version components from the tag + # must match ..(-) + pattern='^([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)?' + if [[ $exact_version =~ $pattern ]]; then + v_maj="${BASH_REMATCH[1]}" + v_min="${BASH_REMATCH[2]}" + v_patch="${BASH_REMATCH[3]}" + v_extra="${BASH_REMATCH[4]}" # optional, lead - + else + echo "Error: unexpected tag '$exact_version'" >&2 + exit 1 + fi + + # no additional information + v_tweak="" else - echo "Error: unexpected git describe output '$version_from_git'" >&2 - exit 1 - fi + version_from_git=$(git describe --tags --long --exclude '*/*' 2>/dev/null) + # must match ..(-)--g + pattern='^([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)?-([0-9]+)-g.*' + if [[ $version_from_git =~ $pattern ]]; then + v_maj="${BASH_REMATCH[1]}" + v_min="${BASH_REMATCH[2]}" + v_patch="${BASH_REMATCH[3]}" + v_extra="${BASH_REMATCH[4]}" # optional, lead - + count="${BASH_REMATCH[5]}" + else + echo "Error: unexpected git describe output '$version_from_git'" >&2 + exit 1 + fi - if [ -z "${v_extra}" ]; then - v_patch=$((v_patch+1)) - v_extra="-0" - fi + if [ -z "${v_extra}" ]; then + v_patch=$((v_patch+1)) + v_extra="-0" + fi - if [ "$KIND" == "tag" ] ; then - # number of commits since tag is irrelevant for a tag push - v_extra="${v_extra}.${KIND}.${NAME}" - else - v_extra="${v_extra}.${KIND}.${NAME}.${count}" - fi + if [ "$KIND" == "tag" ] ; then + # number of commits since tag is irrelevant for a tag push + v_extra="${v_extra}.${KIND}.${NAME}" + else + v_extra="${v_extra}.${KIND}.${NAME}.${count}" + fi - # If HEAD_REF is not set, we're not in CI but in a local clone. Use the - # implicit HEAD and include --dirty to test for uncommitted changes. - v_tweak="+$(git describe --always ${HEAD_REF:---dirty})" + # If HEAD_REF is not set, we're not in CI but in a local clone. Use the + # implicit HEAD and include --dirty to test for uncommitted changes. + v_tweak="+$(git describe --always ${HEAD_REF:---dirty})" + fi fi VERSION="${v_maj}.${v_min}.${v_patch}${v_extra}${v_tweak}" diff --git a/extra/package_core.sh b/extra/package_core.sh index d1c4179d8..77b431480 100755 --- a/extra/package_core.sh +++ b/extra/package_core.sh @@ -54,6 +54,8 @@ for board in $EXCLUDED_BOARDS ; do # remove (even commented) lines for excluded boards sed -i "/^\(\s*#\s*\)\?${board}\./d" $TEMP_BOARDS done +# remove any per-board version lines +sed -i '/^\S*\.version=/d' $TEMP_BOARDS # remove multiple empty lines sed -i '/^$/N;/^\n$/D' $TEMP_BOARDS From 75c3d65fbf12bb443ebf56724c563cbf85cc4836 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 17 Apr 2026 17:17:55 +0200 Subject: [PATCH 5/9] usb: pass Zephyr core version in USB descriptor as iConfig This is step 1. for automatic flash of the loader if the onboard version is different from the one shipped with the core: passing the current build version in the USB descriptor as iConfig, so that it is available on the host side to check if the loader needs to be reflashed. For the legacy USB stack, the version is passed to USB drivers directly via the Kconfig option CONFIG_USB_CONFIGURATION_STRING_DESC. Boards using the new USB stack, like the Portenta C33, set this value via the USBD_DESC_CONFIG_DEFINE macro instead. --- cores/arduino/usb_device_descriptor.c | 5 +++-- loader/Kconfig | 3 +++ loader/prj.conf | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cores/arduino/usb_device_descriptor.c b/cores/arduino/usb_device_descriptor.c index 646bd84d7..0643c69ca 100644 --- a/cores/arduino/usb_device_descriptor.c +++ b/cores/arduino/usb_device_descriptor.c @@ -11,6 +11,7 @@ #include #include +#include #ifdef CONFIG_USB_DEVICE_STACK_NEXT @@ -32,8 +33,8 @@ USBD_DESC_PRODUCT_DEFINE(sample_product, CONFIG_USB_DEVICE_PRODUCT); USBD_DESC_SERIAL_NUMBER_DEFINE(sample_sn); /* doc string instantiation end */ -USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration"); -USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration"); +USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, APP_VERSION_EXTENDED_STRING); +USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, APP_VERSION_EXTENDED_STRING); /* doc configuration instantiation start */ static const uint8_t attributes = 0; diff --git a/loader/Kconfig b/loader/Kconfig index 216d0d959..9965f3757 100644 --- a/loader/Kconfig +++ b/loader/Kconfig @@ -7,6 +7,9 @@ config PINCTRL select PINCTRL_NON_STATIC +config USB_CONFIGURATION_STRING_DESC + default "$(APP_VERSION_EXTENDED_STRING)" + source "Kconfig.zephyr" config LLEXT_HEAP_REGION diff --git a/loader/prj.conf b/loader/prj.conf index 0b48b8ca1..ce28c9433 100644 --- a/loader/prj.conf +++ b/loader/prj.conf @@ -51,3 +51,5 @@ CONFIG_RTC_UPDATE=y CONFIG_RTC_CALIBRATION=y CONFIG_DEVICE_DEPS=y + +CONFIG_USB_CONFIGURATION_STRING_DESC_ENABLE=y From ed1e9a22a6afa1d79701b7a73a6cc1d13f6bb90b Mon Sep 17 00:00:00 2001 From: Luca Burelli Date: Thu, 9 Jul 2026 11:02:55 +0200 Subject: [PATCH 6/9] platform: add upload.artifacts variables, cleanup Add generic 'upload.artifacts.loader' and 'upload.artifacts.sketch' variables to platform.txt to allow for cleaner upload patterns and explicit file dependencies. Remove 'stm32flash' and 'adb' upload patterns, as they are not used by any of the supported boards. Also remove any '.erase' patterns, as all of them were empty anyway. --- platform.txt | 88 +++++++++++++++++----------------------------------- 1 file changed, 29 insertions(+), 59 deletions(-) diff --git a/platform.txt b/platform.txt index 9c541554c..b066724ea 100644 --- a/platform.txt +++ b/platform.txt @@ -164,64 +164,51 @@ pluggable_monitor.required.serial=builtin:serial-monitor # Uploader tools # -------------- +# No erase command is needed for Zephyr targets +erase.pattern= + +# Generated files used at upload time +upload.artifacts.loader={runtime.platform.path}/firmwares/{bootloader.file} +upload.artifacts.sketch={build.path}/{build.project_name}.{upload.extension} + + # # OpenOCD # + tools.openocd.path={runtime.tools.openocd-0.12.0-arduino1-static.path} tools.openocd.cmd=bin/openocd tools.openocd.cmd.windows=bin/openocd.exe tools.openocd.upload.params.verbose=-d2 tools.openocd.upload.params.quiet=-d0 -tools.openocd.upload.pattern="{path}/{cmd}" -s "{path}/share/openocd/scripts/" {upload.programmer} {upload.config} -c "set filename0 "{runtime.platform.path}/firmwares/{bootloader.file}"" -c "set filename1 "{build.path}/{build.project_name}.{upload.extension}"" -c "set offset {upload.address}" -f "{build.variant.path}/{openocd_cfg}" "{upload.verbose}" +tools.openocd.upload.pattern="{path}/{cmd}" -s "{path}/share/openocd/scripts/" {upload.programmer} {upload.config} -c "set filename0 "{upload.artifacts.loader}"" -c "set filename1 "{upload.artifacts.sketch}"" -c "set offset {upload.address}" -f "{build.variant.path}/{openocd_cfg}" "{upload.verbose}" tools.openocd.program.params.verbose=-d2 tools.openocd.program.params.quiet=-d0 -tools.openocd.upload.pattern="{path}/{cmd}" -s "{path}/share/openocd/scripts/" {upload.programmer} {upload.config} -c "set filename0 "{runtime.platform.path}/firmwares/{bootloader.file}"" -c "set filename1 "{build.path}/{build.project_name}.{upload.extension}"" -c "set offset {upload.address}" -f "{build.variant.path}/{openocd_cfg}" "{upload.verbose}" - -tools.openocd.erase.params.verbose=-d2 -tools.openocd.erase.params.quiet=-d0 -tools.openocd.erase.pattern= +tools.openocd.program.pattern="{path}/{cmd}" -s "{path}/share/openocd/scripts/" {upload.programmer} {upload.config} -c "set filename0 "{upload.artifacts.loader}"" -c "set filename1 "{upload.artifacts.sketch}"" -c "set offset {upload.address}" -f "{build.variant.path}/{openocd_cfg}" "{upload.verbose}" tools.openocd.bootloader.params.verbose=-d2 tools.openocd.bootloader.params.quiet=-d0 -tools.openocd.bootloader.pattern="{path}/{cmd}" -s "{path}/share/openocd/scripts/" {upload.programmer} {upload.config} -c "set filename0 "{runtime.platform.path}/firmwares/{bootloader.file}"" -f "{build.variant.path}/flash_bootloader.cfg" "{upload.verbose}" +tools.openocd.bootloader.pattern="{path}/{cmd}" -s "{path}/share/openocd/scripts/" {upload.programmer} {upload.config} -c "set filename0 "{upload.artifacts.loader}"" -f "{build.variant.path}/flash_bootloader.cfg" "{upload.verbose}" + # # BOSSA # + tools.bossac.path={runtime.tools.bossac-1.9.1-arduino2.path} tools.bossac.cmd=bossac tools.bossac.cmd.windows=bossac.exe tools.bossac.upload.params.verbose=-d tools.bossac.upload.params.quiet= -tools.bossac.upload.pattern="{path}/{cmd}" {upload.verbose} --port={serial.port.file} -U -i -e -w "{build.path}/{build.project_name}.{upload.extension}" --offset {upload.address} -R +tools.bossac.upload.pattern="{path}/{cmd}" {upload.verbose} --port={serial.port.file} -U -i -e -w "{upload.artifacts.sketch}" --offset {upload.address} -R tools.bossac.bootloader.params.verbose=-d tools.bossac.bootloader.params.quiet= -tools.bossac.bootloader.pattern="{path}/{cmd}" {upload.verbose} --port={serial.port.file} -U -i -e -w "{runtime.platform.path}/firmwares/{bootloader.file}" --offset {bootloader.address} -R - -tools.bossac.erase.params.verbose= -tools.bossac.erase.params.quiet= -tools.bossac.erase.pattern= +tools.bossac.bootloader.pattern="{path}/{cmd}" {upload.verbose} --port={serial.port.file} -U -i -e -w "{upload.artifacts.loader}" --offset {bootloader.address} -R -# -# STM32FLASH -# -tools.stm32flash.cmd=stm32flash -tools.stm32flash.path={runtime.platform.path}/tools/windows -tools.stm32flash.path.macosx={runtime.platform.path}/tools/macosx -tools.stm32flash.path.linux={runtime.platform.path}/tools/linux -tools.stm32flash.upload.params.verbose=-v -tools.stm32flash.upload.params.quiet= -tools.stm32flash.upload.pattern="{path}/{cmd}" {serial.port} -e 1024 -b 2400 -w "{build.path}/{build.project_name}.bin" -tools.stm32flash.program.params.verbose=-v -tools.stm32flash.program.params.quiet= -tools.stm32flash.program.pattern="{path}/{cmd}" {serial.port} -e 1024 -b 2400 -w "{build.path}/{build.project_name}.bin" -tools.stm32flash.erase.params.verbose=-v -tools.stm32flash.erase.params.quiet= -tools.stm32flash.erase.pattern="{path}/{cmd}" {serial.port} -e 1024 -b 2400 # # DFU_UTIL @@ -234,32 +221,27 @@ tools.dfu-util.path={runtime.tools.dfu-util.path} tools.dfu-util.cmd=dfu-util tools.dfu-util.upload.params.verbose=-d tools.dfu-util.upload.params.quiet= -tools.dfu-util.upload.pattern="{path}/{cmd}" --device ,{upload.vid}:{upload.pid} -D "{build.path}/{build.project_name}.{upload.extension}" -a{upload.interface} {upload.dfuse} +tools.dfu-util.upload.pattern="{path}/{cmd}" --device ,{upload.vid}:{upload.pid} -D "{upload.artifacts.sketch}" -a{upload.interface} {upload.dfuse} tools.dfu-util.bootloader.params.verbose=-d tools.dfu-util.bootloader.params.quiet= -tools.dfu-util.bootloader.pattern="{path}/{cmd}" --device ,{bootloader.vid}:{bootloader.pid} -D "{runtime.platform.path}/firmwares/{bootloader.file}" -a{bootloader.interface} {bootloader.dfuse} +tools.dfu-util.bootloader.pattern="{path}/{cmd}" --device ,{bootloader.vid}:{bootloader.pid} -D "{upload.artifacts.loader}" -a{bootloader.interface} {bootloader.dfuse} -tools.dfu-util.erase.params.verbose= -tools.dfu-util.erase.params.quiet= -tools.dfu-util.erase.pattern= # # PICOTOOL # + tools.picotool.path={runtime.tools.rp2040tools.path} tools.picotool.cmd=rp2040load tools.picotool.upload.params.verbose=-v tools.picotool.upload.params.quiet= -tools.picotool.upload.pattern="{path}/{cmd}" {upload.verbose} -D "{build.path}/{build.project_name}" - -tools.picotool.erase.params.verbose= -tools.picotool.erase.params.quiet= -tools.picotool.erase.pattern= +tools.picotool.upload.pattern="{path}/{cmd}" {upload.verbose} -D "{upload.artifacts.sketch}" tools.picotool.bootloader.params.verbose=-v tools.picotool.bootloader.params.quiet= -tools.picotool.bootloader.pattern="{path}/{cmd}" {upload.verbose} -D "{runtime.platform.path}/firmwares/{bootloader.file}" +tools.picotool.bootloader.pattern="{path}/{cmd}" {upload.verbose} -D "{upload.artifacts.loader}" + # # IMGTOOL @@ -269,29 +251,20 @@ tools.imgtool.path={runtime.tools.imgtool.path} tools.imgtool.cmd=imgtool tools.imgtool.flags=sign --key "{build.keys.keychain}/{build.keys.sign_key}" --encrypt "{build.keys.keychain}/{build.keys.encrypt_key}" "{build.path}/{build.project_name}.bin" "{build.path}/{build.project_name}.bin" --align {build.alignment} --max-align {build.alignment} --version {build.version} --header-size {build.header_size} --pad-header --slot-size {build.slot_size} -# -# ADB WRAPPER -# - -tools.adb.path={runtime.tools.adb.path} -tools.adb.cmd=adb -tools.adb.upload.params.verbose=-v -tools.adb.upload.params.quiet= -tools.adb.upload.pattern="{path}/{cmd}" push "{build.path}/{build.project_name}.elf" "/tmp/arduino/m4-user-sketch.elf" # # REMOTEOCD # + tools.remoteocd.path={runtime.tools.remoteocd.path} tools.remoteocd.cmd=remoteocd tools.remoteocd.upload.params.verbose=--verbose tools.remoteocd.upload.params.quiet= -tools.remoteocd.upload.pattern="{path}/{cmd}" upload --adb-path "{runtime.tools.adb.path}/adb" -s "{upload.port.properties.serialNumber}" -f "{build.variant.path}/{openocd_cfg}" "{upload.verbose}" "{runtime.platform.path}/firmwares/{bootloader.file}" "{build.path}/{build.project_name}.{upload.extension}" +tools.remoteocd.upload.pattern="{path}/{cmd}" upload --adb-path "{runtime.tools.adb.path}/adb" -s "{upload.port.properties.serialNumber}" -f "{build.variant.path}/{openocd_cfg}" "{upload.verbose}" "{upload.artifacts.loader}" "{upload.artifacts.sketch}" tools.remoteocd.bootloader.params.verbose=--verbose tools.remoteocd.bootloader.params.quiet= -tools.remoteocd.erase.pattern= -tools.remoteocd.bootloader.pattern="{path}/{cmd}" upload --adb-path "{runtime.tools.adb.path}/adb" -s "{upload.port.properties.serialNumber}" -f "{build.variant.path}/flash_bootloader.cfg" "{upload.verbose}" "{runtime.platform.path}/firmwares/{bootloader.file}" +tools.remoteocd.bootloader.pattern="{path}/{cmd}" upload --adb-path "{runtime.tools.adb.path}/adb" -s "{upload.port.properties.serialNumber}" -f "{build.variant.path}/flash_bootloader.cfg" "{upload.verbose}" "{upload.artifacts.loader}" tools.remoteocd_network.upload.protocol=network tools.remoteocd_network.upload.field.password=Password @@ -300,7 +273,8 @@ tools.remoteocd_network.path={runtime.tools.remoteocd.path} tools.remoteocd_network.cmd=remoteocd tools.remoteocd_network.upload.params.verbose=--verbose tools.remoteocd_network.upload.params.quiet= -tools.remoteocd_network.upload.pattern="{path}/{cmd}" upload -a "{upload.port.address}" --password "{upload.field.password}" -f "{build.variant.path}/{openocd_cfg}" "{upload.verbose}" "{runtime.platform.path}/firmwares/{bootloader.file}" "{build.path}/{build.project_name}.{upload.extension}" +tools.remoteocd_network.upload.pattern="{path}/{cmd}" upload -a "{upload.port.address}" --password "{upload.field.password}" -f "{build.variant.path}/{openocd_cfg}" "{upload.verbose}" "{upload.artifacts.loader}" "{upload.artifacts.sketch}" + # # PYOCD WRAPPER @@ -312,15 +286,11 @@ tools.pyocd.path= tools.pyocd.cmd=pyocd tools.pyocd.upload.params.verbose= tools.pyocd.upload.params.quiet= -tools.pyocd.upload.pattern="{cmd}" load --target {upload.target} {build.path}/{build.project_name}.{upload.extension}@{upload.address} +tools.pyocd.upload.pattern="{cmd}" load --target {upload.target} {upload.artifacts.sketch}@{upload.address} tools.pyocd.bootloader.params.verbose= tools.pyocd.bootloader.params.quiet= -tools.pyocd.bootloader.pattern="{cmd}" load --target {bootloader.target} "{runtime.platform.path}/firmwares/{bootloader.file}" - -tools.pyocd.erase.params.verbose= -tools.pyocd.erase.params.quiet= -tools.pyocd.erase.pattern= +tools.pyocd.bootloader.pattern="{cmd}" load --target {bootloader.target} "{upload.artifacts.loader}" # From a40dfc9d97a5e13e062137dbd5dc255d546c2bf3 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 15 Jul 2026 12:38:19 +0200 Subject: [PATCH 7/9] boards: c33: update bootloader binary tracks https://github.com/arduino/arduino-renesas-bootloader/tree/zephyr_alt , allows consecutive uploads (loader+sketch) --- loader/blobs/c33_bl.bin | Bin 17724 -> 18435 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/loader/blobs/c33_bl.bin b/loader/blobs/c33_bl.bin index 40688c78d19ce05fbddbafabfddeb6e92fa9867b..f93f1a5658b3011776a3c6f60975081b88a46c4e 100755 GIT binary patch literal 18435 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 From 28c0dfa1714a7946e198e6ca7651d54cf5b357fb Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 8 Jul 2026 15:53:18 +0200 Subject: [PATCH 8/9] platform: automatic reflash of the bootloader for USB boards For boards that have soft USB, use the new 'scripting-tools' tool to check the USB configuration string reported by the board. If it does not match the expected version, reflash the bootloader before uploading the sketch. Refactor loader and sketch commands so they can be reused in both the bootloader and upload patterns. Note that {loader.cmd} should leave the target waiting for the sketch upload, so {bootloader.pattern} has some extra flags to make it exit after flashing the bootloader. Supported tools: - bossac - dfu-util - picotool --- boards.txt | 8 ++++++-- platform.txt | 35 +++++++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/boards.txt b/boards.txt index e7b6325a1..8368217d6 100644 --- a/boards.txt +++ b/boards.txt @@ -484,14 +484,18 @@ portentac33.upload.interface=1 portentac33.upload.use_1200bps_touch=true portentac33.upload.wait_for_upload_port=false portentac33.upload.native_usb=true -portentac33.upload.dfuse=-Q -w +portentac33.upload.dfuse=-w portentac33.bootloader.tool=dfu-util portentac33.bootloader.tool.default=dfu-util portentac33.bootloader.file=zephyr-{build.variant}.bin portentac33.bootloader.interface=0 portentac33.bootloader.address=0x10000 -portentac33.bootloader.dfuse=-Q +portentac33.bootloader.dfuse= + +# the space before -Q is important +portentac33.empty= +portentac33.leave.dfuse={empty} -Q ########################################################################################## diff --git a/platform.txt b/platform.txt index b066724ea..5773d2a74 100644 --- a/platform.txt +++ b/platform.txt @@ -171,11 +171,17 @@ erase.pattern= upload.artifacts.loader={runtime.platform.path}/firmwares/{bootloader.file} upload.artifacts.sketch={build.path}/{build.project_name}.{upload.extension} +# Expects a {loader.cmd} and a {sketch.cmd} to be defined in the tool's +# section. The loader is flashed if it does not match the current (maybe +# board-specific) core version. The sketch is then flashed in any case. +tools.patterns.upload-loader-and-sketch="{runtime.tools.scripting-tools.path}/scripting-tools" if not eq "v{upload.port.properties.configuration}" "v{version}" {loader.cmd} :: run {sketch.cmd} # # OpenOCD # +# Special handling of loader and sketch via cfg file + tools.openocd.path={runtime.tools.openocd-0.12.0-arduino1-static.path} tools.openocd.cmd=bin/openocd tools.openocd.cmd.windows=bin/openocd.exe @@ -201,46 +207,57 @@ tools.bossac.path={runtime.tools.bossac-1.9.1-arduino2.path} tools.bossac.cmd=bossac tools.bossac.cmd.windows=bossac.exe +tools.bossac.sketch.cmd="{path}/{cmd}" {upload.verbose} --port={serial.port.file} -U -i -e -w "{upload.artifacts.sketch}" --offset {upload.address} -R +tools.bossac.loader.cmd="{path}/{cmd}" {upload.verbose} --port={serial.port.file} -U -i -e -w "{upload.artifacts.loader}" --offset {bootloader.address} + tools.bossac.upload.params.verbose=-d tools.bossac.upload.params.quiet= -tools.bossac.upload.pattern="{path}/{cmd}" {upload.verbose} --port={serial.port.file} -U -i -e -w "{upload.artifacts.sketch}" --offset {upload.address} -R +tools.bossac.upload.pattern={tools.patterns.upload-loader-and-sketch} tools.bossac.bootloader.params.verbose=-d tools.bossac.bootloader.params.quiet= -tools.bossac.bootloader.pattern="{path}/{cmd}" {upload.verbose} --port={serial.port.file} -U -i -e -w "{upload.artifacts.loader}" --offset {bootloader.address} -R +tools.bossac.bootloader.pattern={loader.cmd} -R # # DFU_UTIL # -upload.dfuse=--dfuse-address={upload.address}:leave -bootloader.dfuse=--dfuse-address={bootloader.address}:leave +# overridable by boards.txt +leave.dfuse=:leave +upload.dfuse=--dfuse-address={upload.address} +bootloader.dfuse=--dfuse-address={bootloader.address} tools.dfu-util.path={runtime.tools.dfu-util.path} tools.dfu-util.cmd=dfu-util +tools.dfu-util.sketch.cmd="{path}/{cmd}" --device ,{upload.vid}:{upload.pid} -D "{upload.artifacts.sketch}" -a{upload.interface} {upload.dfuse}{leave.dfuse} +tools.dfu-util.loader.cmd="{path}/{cmd}" --device ,{bootloader.vid}:{bootloader.pid} -D "{upload.artifacts.loader}" -a{bootloader.interface} {bootloader.dfuse} + tools.dfu-util.upload.params.verbose=-d tools.dfu-util.upload.params.quiet= -tools.dfu-util.upload.pattern="{path}/{cmd}" --device ,{upload.vid}:{upload.pid} -D "{upload.artifacts.sketch}" -a{upload.interface} {upload.dfuse} +tools.dfu-util.upload.pattern={tools.patterns.upload-loader-and-sketch} tools.dfu-util.bootloader.params.verbose=-d tools.dfu-util.bootloader.params.quiet= -tools.dfu-util.bootloader.pattern="{path}/{cmd}" --device ,{bootloader.vid}:{bootloader.pid} -D "{upload.artifacts.loader}" -a{bootloader.interface} {bootloader.dfuse} +tools.dfu-util.bootloader.pattern={loader.cmd}{leave.dfuse} # # PICOTOOL # +tools.picotool.sketch.cmd="{path}/{cmd}" {upload.verbose} -D "{upload.artifacts.sketch}" +tools.picotool.loader.cmd="{path}/{cmd}" {upload.verbose} -D "{upload.artifacts.loader}" + tools.picotool.path={runtime.tools.rp2040tools.path} tools.picotool.cmd=rp2040load tools.picotool.upload.params.verbose=-v tools.picotool.upload.params.quiet= -tools.picotool.upload.pattern="{path}/{cmd}" {upload.verbose} -D "{upload.artifacts.sketch}" +tools.picotool.upload.pattern={tools.patterns.upload-loader-and-sketch} tools.picotool.bootloader.params.verbose=-v tools.picotool.bootloader.params.quiet= -tools.picotool.bootloader.pattern="{path}/{cmd}" {upload.verbose} -D "{upload.artifacts.loader}" +tools.picotool.bootloader.pattern={loader.cmd} # @@ -256,6 +273,8 @@ tools.imgtool.flags=sign --key "{build.keys.keychain}/{build.keys.sign_key}" --e # REMOTEOCD # +# Special handling of loader and sketch via upload command + tools.remoteocd.path={runtime.tools.remoteocd.path} tools.remoteocd.cmd=remoteocd tools.remoteocd.upload.params.verbose=--verbose From 52eb2def62f332a8910f278b804329c64ca147d7 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 15 Jul 2026 16:37:03 +0200 Subject: [PATCH 9/9] boards: c33: add feedback on bootloader reflash ok Switching to this core requires an updated bootloader. Since it will be flashed only if needed, the procedure is now: * Double click reset to enter bootloader mode * Burn bootloader * Wait until the blue led starts flashing * Double click reset to enter bootloader mode again * Upload sketch --- loader/fixups.c | 8 ++++++++ .../arduino_portenta_c33_r7fa6m5bh3cfc.conf | 2 ++ 2 files changed, 10 insertions(+) diff --git a/loader/fixups.c b/loader/fixups.c index 5903c20e7..a9335ae6f 100644 --- a/loader/fixups.c +++ b/loader/fixups.c @@ -6,6 +6,7 @@ #include #include +#include #ifndef CONFIG_CPP void __cxa_pure_virtual() { @@ -189,6 +190,13 @@ int maybe_flash_bootloader(void) { printk("Failed to write flash area, rc %d\n", rc); return rc; } + struct led_dt_spec ledb = LED_DT_SPEC_GET(DT_NODELABEL(led3)); + while (1) { + led_on_dt(&ledb); + k_msleep(100); + led_off_dt(&ledb); + k_msleep(100); + } } return 0; } diff --git a/variants/arduino_portenta_c33_r7fa6m5bh3cfc/arduino_portenta_c33_r7fa6m5bh3cfc.conf b/variants/arduino_portenta_c33_r7fa6m5bh3cfc/arduino_portenta_c33_r7fa6m5bh3cfc.conf index c8dd6b230..248bf700e 100644 --- a/variants/arduino_portenta_c33_r7fa6m5bh3cfc/arduino_portenta_c33_r7fa6m5bh3cfc.conf +++ b/variants/arduino_portenta_c33_r7fa6m5bh3cfc/arduino_portenta_c33_r7fa6m5bh3cfc.conf @@ -140,3 +140,5 @@ CONFIG_FS_FATFS_CUSTOM_MOUNT_POINT_COUNT=3 CONFIG_FS_FATFS_CUSTOM_MOUNT_POINTS="wlan,ota,SD" CONFIG_DYNAMIC_INTERRUPTS=n + +CONFIG_LED=y \ No newline at end of file