diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b9eb4d70..bd85858a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: - name: 'Bundle scripts' if: ${{ !github.event.pull_request.head.repo.fork }} run: | - tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts + tar czpf "artifacts/flipper-z-any-scripts-${SUFFIX}.tgz" scripts - name: 'Build the firmware' run: | @@ -73,6 +73,8 @@ jobs: ./fbt TARGET_HW=$TARGET_HW fap_dist tar czpf "artifacts/flipper-z-${TARGET}-debugapps-${SUFFIX}.tgz" \ -C dist/${TARGET}-*/apps/Debug . + tar czpf "artifacts/flipper-z-${TARGET}-appsymbols-${SUFFIX}.tgz" \ + -C dist/${TARGET}-*/debug_elf . done - name: "Check for uncommitted changes" diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 9c6c6b2d..2e6336c0 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -31,7 +31,7 @@ jobs: if: success() timeout-minutes: 10 run: | - ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 + ./fbt flash SWD_TRANSPORT_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 - name: 'Wait for flipper and format ext' id: format_ext @@ -64,4 +64,4 @@ jobs: - name: 'Check GDB output' if: failure() run: | - ./fbt gdb_trace_all OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 + ./fbt gdb_trace_all SWD_TRANSPORT_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml index 27a181c4..f4064e1c 100644 --- a/.github/workflows/updater_test.yml +++ b/.github/workflows/updater_test.yml @@ -64,7 +64,7 @@ jobs: - name: 'Flash last release' if: failure() run: | - ./fbt flash OPENOCD_ADAPTER_SERIAL=${{steps.device.outputs.stlink}} FORCE=1 + ./fbt flash SWD_TRANSPORT_SERIAL=${{steps.device.outputs.stlink}} FORCE=1 - name: 'Wait for flipper and format ext' if: failure() diff --git a/.vscode/example/tasks.json b/.vscode/example/tasks.json index 3c01506a..e36a1fe4 100644 --- a/.vscode/example/tasks.json +++ b/.vscode/example/tasks.json @@ -16,29 +16,17 @@ "command": "./fbt" }, { - "label": "[Release] Flash (ST-Link)", + "label": "[Release] Flash (SWD)", "group": "build", "type": "shell", "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash" }, { - "label": "[Debug] Flash (ST-Link)", + "label": "[Debug] Flash (SWD)", "group": "build", "type": "shell", "command": "./fbt FORCE=1 flash" }, - { - "label": "[Release] Flash (blackmagic)", - "group": "build", - "type": "shell", - "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_blackmagic" - }, - { - "label": "[Debug] Flash (blackmagic)", - "group": "build", - "type": "shell", - "command": "./fbt FORCE=1 flash_blackmagic" - }, { "label": "[Release] Flash (JLink)", "group": "build", diff --git a/SConstruct b/SConstruct index 04cd057f..44ee7746 100644 --- a/SConstruct +++ b/SConstruct @@ -185,27 +185,15 @@ copro_dist = distenv.CoproBuilder( distenv.AlwaysBuild(copro_dist) distenv.Alias("copro_dist", copro_dist) -firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env) + +firmware_flash = distenv.AddFwFlashTarget(firmware_env) distenv.Alias("flash", firmware_flash) +# To be implemented in fwflash.py firmware_jflash = distenv.AddJFlashTarget(firmware_env) distenv.Alias("jflash", firmware_jflash) -firmware_bm_flash = distenv.PhonyTarget( - "flash_blackmagic", - "$GDB $GDBOPTS $SOURCES $GDBFLASH", - source=firmware_env["FW_ELF"], - GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", - GDBREMOTE="${BLACKMAGIC_ADDR}", - GDBFLASH=[ - "-ex", - "load", - "-ex", - "quit", - ], -) - -gdb_backtrace_all_threads = distenv.PhonyTarget( +distenv.PhonyTarget( "gdb_trace_all", "$GDB $GDBOPTS $SOURCES $GDBFLASH", source=firmware_env["FW_ELF"], diff --git a/documentation/fbt.md b/documentation/fbt.md index af588382..02de2949 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -66,7 +66,7 @@ To use language servers other than the default VS Code C/C++ language server, us - `fap_dist` - build external plugins & publish to the `dist` folder. - `updater_package`, `updater_minpackage` - build a self-update package. The minimal version only includes the firmware's DFU file; the full version also includes a radio stack & resources for the SD card. - `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper. -- `flash` - flash the attached device with OpenOCD over ST-Link. +- `flash` - flash the attached device over SWD interface with supported probes. Probe is detected automatically; you can override it with `SWD_TRANSPORT=...` variable. If multiple probes are attached, you can specify the serial number of the probe to use with `SWD_TRANSPORT_SERIAL=...`. - `flash_usb`, `flash_usb_full` - build, upload and install the update package to the device over USB. See details on `updater_package` and `updater_minpackage`. - `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded. - `debug_other`, `debug_other_blackmagic` - attach GDB without loading any `.elf`. It will allow you to manually add external `.elf` files with `add-symbol-file` in GDB. @@ -75,7 +75,7 @@ To use language servers other than the default VS Code C/C++ language server, us - `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board). - `openocd` - just start OpenOCD. - `get_blackmagic` - output the blackmagic address in the GDB remote format. Useful for IDE integration. -- `get_stlink` - output serial numbers for attached STLink probes. Used for specifying an adapter with `OPENOCD_ADAPTER_SERIAL=...`. +- `get_stlink` - output serial numbers for attached STLink probes. Used for specifying an adapter with `SWD_TRANSPORT_SERIAL=...`. - `lint`, `format` - run clang-format on the C source code to check and reformat it according to the `.clang-format` specs. - `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on the Python source code, build system files & application manifests. - `firmware_pvs` - generate a PVS Studio report for the firmware. Requires PVS Studio to be available on your system's `PATH`. @@ -88,9 +88,8 @@ To use language servers other than the default VS Code C/C++ language server, us - `fap_snake_game`, etc. - build single app as `.fap` by its application ID. - Check out [`--extra-ext-apps`](#command-line-parameters) for force adding extra apps to external build. - `fap_snake_game_list`, etc - generate source + assembler listing for app's `.fap`. -- `flash`, `firmware_flash` - flash the current version to the attached device with OpenOCD over ST-Link. +- `flash`, `firmware_flash` - flash the current version to the attached device over SWD. - `jflash` - flash the current version to the attached device with JFlash using a J-Link probe. The JFlash executable must be on your `$PATH`. -- `flash_blackmagic` - flash the current version to the attached device with a Blackmagic probe. - `firmware_all`, `updater_all` - build a basic set of binaries. - `firmware_list`, `updater_list` - generate source + assembler listing. - `firmware_cdb`, `updater_cdb` - generate a `compilation_database.json` file for external tools and IDEs. It can be created without actually building the firmware. diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index eab140d5..d935ee79 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1038,18 +1038,18 @@ Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer Function,+,furi_hal_crypto_ctr,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" +Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" +Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool" Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*" Function,+,furi_hal_crypto_gcm_encrypt_and_tag,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*" Function,-,furi_hal_crypto_init,void, Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*" -Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" -Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" -Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t Function,+,furi_hal_crypto_unload_key,_Bool, -Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" -Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, Function,+,furi_hal_debug_is_gdb_session_active,_Bool, @@ -1471,7 +1471,6 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* -Function,-,gui_active_view_port_count,size_t,"Gui*, GuiLayer" Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 8efe980a..d6c54e39 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1109,18 +1109,18 @@ Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer Function,+,furi_hal_crypto_ctr,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" +Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" +Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool" Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*" Function,+,furi_hal_crypto_gcm_encrypt_and_tag,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*" Function,-,furi_hal_crypto_init,void, Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*" -Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" -Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" -Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t Function,+,furi_hal_crypto_unload_key,_Bool, -Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" -Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, Function,+,furi_hal_debug_is_gdb_session_active,_Bool, @@ -1642,7 +1642,6 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* -Function,-,gui_active_view_port_count,size_t,"Gui*, GuiLayer" Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 7df3eb09..bea7c123 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -1,3 +1,4 @@ +#include "storage/storage.h" #include #include "elf_file.h" #include "elf_file_i.h" @@ -57,6 +58,13 @@ static void address_cache_put(AddressCache_t cache, int symEntry, Elf32_Addr sym /********************************************** ELF ***********************************************/ /**************************************************************************************************/ +static void elf_file_maybe_release_fd(ELFFile* elf) { + if(elf->fd) { + storage_file_free(elf->fd); + elf->fd = NULL; + } +} + static ELFSection* elf_file_get_section(ELFFile* elf, const char* name) { return ELFSectionDict_get(elf->sections, name); } @@ -764,7 +772,7 @@ void elf_file_free(ELFFile* elf) { free(elf->debug_link_info.debug_link); } - storage_file_free(elf->fd); + elf_file_maybe_release_fd(elf); free(elf); } @@ -855,6 +863,7 @@ ElfProcessSectionResult elf_process_section( } ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { + furi_check(elf->fd != NULL); ELFFileLoadStatus status = ELFFileLoadStatusSuccess; ELFSectionDict_it_t it; @@ -895,6 +904,7 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { FURI_LOG_I(TAG, "Total size of loaded sections: %zu", total_size); } + elf_file_maybe_release_fd(elf); return status; } diff --git a/scripts/fbt/sdk/cache.py b/scripts/fbt/sdk/cache.py index b6f6edbe..074cac6b 100644 --- a/scripts/fbt/sdk/cache.py +++ b/scripts/fbt/sdk/cache.py @@ -237,6 +237,7 @@ class SdkCache: removed_entries = known_set - new_set if removed_entries: print(f"Removed: {removed_entries}") + self.loaded_dirty_version = True known_set -= removed_entries # If any of removed entries was a part of active API, that's a major bump if update_version and any( diff --git a/scripts/fbt_tools/fbt_debugopts.py b/scripts/fbt_tools/fbt_debugopts.py index d46ecd8f..392465a5 100644 --- a/scripts/fbt_tools/fbt_debugopts.py +++ b/scripts/fbt_tools/fbt_debugopts.py @@ -21,7 +21,7 @@ def generate(env, **kw): FBT_DEBUG_DIR="${FBT_SCRIPT_DIR}/debug", ) - if (adapter_serial := env.subst("$OPENOCD_ADAPTER_SERIAL")) != "auto": + if (adapter_serial := env.subst("$SWD_TRANSPORT_SERIAL")) != "auto": env.Append( OPENOCD_OPTS=[ "-c", diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index e47898bd..fdf66c0a 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -52,22 +52,16 @@ def AddFwProject(env, base_env, fw_type, fw_env_key): return project_env -def AddOpenOCDFlashTarget(env, targetenv, **kw): - openocd_target = env.OpenOCDFlash( - "#build/oocd-${BUILD_CFG}-flash.flag", - targetenv["FW_BIN"], - OPENOCD_COMMAND=[ - "-c", - "program ${SOURCE.posix} reset exit ${BASE_ADDRESS}", - ], - BUILD_CFG=targetenv.subst("$FIRMWARE_BUILD_CFG"), - BASE_ADDRESS=targetenv.subst("$IMAGE_BASE_ADDRESS"), +def AddFwFlashTarget(env, targetenv, **kw): + fwflash_target = env.FwFlash( + "#build/flash.flag", + targetenv["FW_ELF"], **kw, ) - env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), openocd_target) + env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), fwflash_target) if env["FORCE"]: - env.AlwaysBuild(openocd_target) - return openocd_target + env.AlwaysBuild(fwflash_target) + return fwflash_target def AddJFlashTarget(env, targetenv, **kw): @@ -115,7 +109,7 @@ def generate(env): env.SetDefault(COPROCOMSTR="\tCOPRO\t${TARGET}") env.AddMethod(AddFwProject) env.AddMethod(DistCommand) - env.AddMethod(AddOpenOCDFlashTarget) + env.AddMethod(AddFwFlashTarget) env.AddMethod(GetProjetDirName) env.AddMethod(AddJFlashTarget) env.AddMethod(AddUsbFlashTarget) @@ -125,30 +119,53 @@ def generate(env): SELFUPDATE_SCRIPT="${FBT_SCRIPT_DIR}/selfupdate.py", DIST_SCRIPT="${FBT_SCRIPT_DIR}/sconsdist.py", COPRO_ASSETS_SCRIPT="${FBT_SCRIPT_DIR}/assets.py", + FW_FLASH_SCRIPT="${FBT_SCRIPT_DIR}/fwflash.py", ) env.Append( BUILDERS={ + "FwFlash": Builder( + action=[ + [ + "${PYTHON3}", + "${FW_FLASH_SCRIPT}", + "-d" if env["VERBOSE"] else "", + "--interface=${SWD_TRANSPORT}", + "--serial=${SWD_TRANSPORT_SERIAL}", + "${SOURCE}", + ], + Touch("${TARGET}"), + ] + ), "UsbInstall": Builder( action=[ - Action( - '${PYTHON3} "${SELFUPDATE_SCRIPT}" -p ${FLIP_PORT} ${UPDATE_BUNDLE_DIR}/update.fuf' - ), + [ + "${PYTHON3}", + "${SELFUPDATE_SCRIPT}", + "-p", + "${FLIP_PORT}", + "${UPDATE_BUNDLE_DIR}/update.fuf", + ], Touch("${TARGET}"), ] ), "CoproBuilder": Builder( action=Action( [ - '${PYTHON3} "${COPRO_ASSETS_SCRIPT}" ' - "copro ${COPRO_CUBE_DIR} " - "${TARGET} ${COPRO_MCU_FAMILY} " - "--cube_ver=${COPRO_CUBE_VERSION} " - "--stack_type=${COPRO_STACK_TYPE} " - '--stack_file="${COPRO_STACK_BIN}" ' - "--stack_addr=${COPRO_STACK_ADDR} ", + [ + "${PYTHON3}", + "${COPRO_ASSETS_SCRIPT}", + "copro", + "${COPRO_CUBE_DIR}", + "${TARGET}", + "${COPRO_MCU_FAMILY}", + "--cube_ver=${COPRO_CUBE_VERSION}", + "--stack_type=${COPRO_STACK_TYPE}", + "--stack_file=${COPRO_STACK_BIN}", + "--stack_addr=${COPRO_STACK_ADDR}", + ] ], - "$COPROCOMSTR", + "${COPROCOMSTR}", ) ), } diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 642c1c98..6059628f 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -53,6 +53,11 @@ class AppBuilder: FAP_SRC_DIR=self.app._appdir, FAP_WORK_DIR=self.app_work_dir, ) + self.app_env.Append( + CPPDEFINES=[ + ("FAP_VERSION", f'"{".".join(map(str, self.app.fap_version))}"') + ], + ) self.app_env.VariantDir(self.app_work_dir, self.app._appdir, duplicate=False) def _build_external_files(self): diff --git a/scripts/fbt_tools/fbt_help.py b/scripts/fbt_tools/fbt_help.py index 68fc2aaf..dcdce934 100644 --- a/scripts/fbt_tools/fbt_help.py +++ b/scripts/fbt_tools/fbt_help.py @@ -16,8 +16,8 @@ Firmware & apps: Flashing & debugging: - flash, flash_blackmagic, jflash: - Flash firmware to target using debug probe + flash, jflash: + Flash firmware to target using SWD probe. See also SWD_TRANSPORT, SWD_TRANSPORT_SERIAL flash_usb, flash_usb_full: Install firmware using self-update package debug, debug_other, blackmagic: diff --git a/scripts/program.py b/scripts/fwflash.py similarity index 71% rename from scripts/program.py rename to scripts/fwflash.py index f3e7e3e2..2b1ad543 100755 --- a/scripts/program.py +++ b/scripts/fwflash.py @@ -6,14 +6,17 @@ import subprocess import time import typing from abc import ABC, abstractmethod -from dataclasses import dataclass +from dataclasses import dataclass, field from flipper.app import App +# When adding an interface, also add it to SWD_TRANSPORT in fbt options + + class Programmer(ABC): @abstractmethod - def flash(self, bin: str) -> bool: + def flash(self, file_path: str, do_verify: bool) -> bool: pass @abstractmethod @@ -32,9 +35,9 @@ class Programmer(ABC): @dataclass class OpenOCDInterface: name: str - file: str + config_file: str serial_cmd: str - additional_args: typing.Optional[list[str]] = None + additional_args: typing.Optional[list[str]] = field(default_factory=list) class OpenOCDProgrammer(Programmer): @@ -44,12 +47,10 @@ class OpenOCDProgrammer(Programmer): self.serial: typing.Optional[str] = None def _add_file(self, params: list[str], file: str): - params.append("-f") - params.append(file) + params += ["-f", file] def _add_command(self, params: list[str], command: str): - params.append("-c") - params.append(command) + params += ["-c", command] def _add_serial(self, params: list[str], serial: str): self._add_command(params, f"{self.interface.serial_cmd} {serial}") @@ -57,22 +58,27 @@ class OpenOCDProgrammer(Programmer): def set_serial(self, serial: str): self.serial = serial - def flash(self, bin: str) -> bool: - i = self.interface - + def flash(self, file_path: str, do_verify: bool) -> bool: if os.altsep: - bin = bin.replace(os.sep, os.altsep) + file_path = file_path.replace(os.sep, os.altsep) openocd_launch_params = ["openocd"] - self._add_file(openocd_launch_params, i.file) + self._add_file(openocd_launch_params, self.interface.config_file) if self.serial: self._add_serial(openocd_launch_params, self.serial) - if i.additional_args: - for a in i.additional_args: - self._add_command(openocd_launch_params, a) + for additional_arg in self.interface.additional_args: + self._add_command(openocd_launch_params, additional_arg) self._add_file(openocd_launch_params, "target/stm32wbx.cfg") self._add_command(openocd_launch_params, "init") - self._add_command(openocd_launch_params, f"program {bin} reset exit 0x8000000") + program_params = [ + "program", + f'"{file_path}"', + "verify" if do_verify else "", + "reset", + "exit", + "0x8000000" if file_path.endswith(".bin") else "", + ] + self._add_command(openocd_launch_params, " ".join(program_params)) # join the list of parameters into a string, but add quote if there are spaces openocd_launch_params_string = " ".join( @@ -105,7 +111,7 @@ class OpenOCDProgrammer(Programmer): i = self.interface openocd_launch_params = ["openocd"] - self._add_file(openocd_launch_params, i.file) + self._add_file(openocd_launch_params, i.config_file) if self.serial: self._add_serial(openocd_launch_params, self.serial) if i.additional_args: @@ -187,7 +193,7 @@ def _resolve_hostname(hostname): def blackmagic_find_networked(serial: str): - if not serial: + if not serial or serial == "auto": serial = "blackmagic.local" # remove the tcp: prefix if it's there @@ -234,7 +240,7 @@ class BlackmagicProgrammer(Programmer): else: self.port = serial - def flash(self, bin: str) -> bool: + def flash(self, file_path: str, do_verify: bool) -> bool: if not self.port: if not self.probe(): return False @@ -242,12 +248,14 @@ class BlackmagicProgrammer(Programmer): # We can convert .bin to .elf with objcopy: # arm-none-eabi-objcopy -I binary -O elf32-littlearm --change-section-address=.data=0x8000000 -B arm -S app.bin app.elf # But I choose to use the .elf file directly because we are flashing our own firmware and it always has an elf predecessor. - elf = bin.replace(".bin", ".elf") - if not os.path.exists(elf): - self.logger.error( - f"Sorry, but Blackmagic can't flash .bin file, and {elf} doesn't exist" - ) - return False + + if file_path.endswith(".bin"): + file_path = file_path[:-4] + ".elf" + if not os.path.exists(file_path): + self.logger.error( + f"Sorry, but Blackmagic can't flash .bin file, and {file_path} doesn't exist" + ) + return False # arm-none-eabi-gdb build/f7-firmware-D/firmware.bin # -ex 'set pagination off' @@ -260,7 +268,7 @@ class BlackmagicProgrammer(Programmer): # -ex 'compare-sections' # -ex 'quit' - gdb_launch_params = ["arm-none-eabi-gdb", elf] + gdb_launch_params = ["arm-none-eabi-gdb", file_path] self._add_command(gdb_launch_params, f"target extended-remote {self.port}") self._add_command(gdb_launch_params, "set pagination off") self._add_command(gdb_launch_params, "set confirm off") @@ -268,7 +276,8 @@ class BlackmagicProgrammer(Programmer): self._add_command(gdb_launch_params, "attach 1") self._add_command(gdb_launch_params, "set mem inaccessible-by-default off") self._add_command(gdb_launch_params, "load") - self._add_command(gdb_launch_params, "compare-sections") + if do_verify: + self._add_command(gdb_launch_params, "compare-sections") self._add_command(gdb_launch_params, "quit") self.logger.debug(f"Launching: {' '.join(gdb_launch_params)}") @@ -314,7 +323,9 @@ class BlackmagicProgrammer(Programmer): return self.name -programmers: list[Programmer] = [ +#################### + +local_flash_interfaces: list[Programmer] = [ OpenOCDProgrammer( OpenOCDInterface( "cmsis-dap", @@ -325,47 +336,64 @@ programmers: list[Programmer] = [ ), OpenOCDProgrammer( OpenOCDInterface( - "stlink", "interface/stlink.cfg", "hla_serial", ["transport select hla_swd"] + "stlink", + "interface/stlink.cfg", + "hla_serial", + ["transport select hla_swd"], ), ), BlackmagicProgrammer(blackmagic_find_serial, "blackmagic_usb"), ] -network_programmers = [ +network_flash_interfaces: list[Programmer] = [ BlackmagicProgrammer(blackmagic_find_networked, "blackmagic_wifi") ] +all_flash_interfaces = [*local_flash_interfaces, *network_flash_interfaces] + +#################### + class Main(App): + AUTO_INTERFACE = "auto" + def init(self): - self.subparsers = self.parser.add_subparsers(help="sub-command help") - self.parser_flash = self.subparsers.add_parser("flash", help="Flash a binary") - self.parser_flash.add_argument( - "bin", + self.parser.add_argument( + "filename", type=str, - help="Binary to flash", + help="File to flash", ) - interfaces = [i.get_name() for i in programmers] - interfaces.extend([i.get_name() for i in network_programmers]) - self.parser_flash.add_argument( + self.parser.add_argument( + "--verify", + "-v", + action="store_true", + help="Verify flash after programming", + default=False, + ) + self.parser.add_argument( "--interface", - choices=interfaces, + choices=( + self.AUTO_INTERFACE, + *[i.get_name() for i in all_flash_interfaces], + ), type=str, + default=self.AUTO_INTERFACE, help="Interface to use", ) - self.parser_flash.add_argument( + self.parser.add_argument( "--serial", type=str, + default=self.AUTO_INTERFACE, help="Serial number or port of the programmer", ) - self.parser_flash.set_defaults(func=self.flash) + self.parser.set_defaults(func=self.flash) - def _search_interface(self, serial: typing.Optional[str]) -> list[Programmer]: + def _search_interface(self, interface_list: list[Programmer]) -> list[Programmer]: found_programmers = [] - for p in programmers: + for p in interface_list: name = p.get_name() - if serial: + if (serial := self.args.serial) != self.AUTO_INTERFACE: p.set_serial(serial) self.logger.debug(f"Trying {name} with {serial}") else: @@ -373,29 +401,7 @@ class Main(App): if p.probe(): self.logger.debug(f"Found {name}") - found_programmers += [p] - else: - self.logger.debug(f"Failed to probe {name}") - - return found_programmers - - def _search_network_interface( - self, serial: typing.Optional[str] - ) -> list[Programmer]: - found_programmers = [] - - for p in network_programmers: - name = p.get_name() - - if serial: - p.set_serial(serial) - self.logger.debug(f"Trying {name} with {serial}") - else: - self.logger.debug(f"Trying {name}") - - if p.probe(): - self.logger.debug(f"Found {name}") - found_programmers += [p] + found_programmers.append(p) else: self.logger.debug(f"Failed to probe {name}") @@ -403,55 +409,60 @@ class Main(App): def flash(self): start_time = time.time() - bin_path = os.path.abspath(self.args.bin) + file_path = os.path.abspath(self.args.filename) - if not os.path.exists(bin_path): - self.logger.error(f"Binary file not found: {bin_path}") + if not os.path.exists(file_path): + self.logger.error(f"Binary file not found: {file_path}") return 1 - if self.args.interface: - i_name = self.args.interface - interfaces = [p for p in programmers if p.get_name() == i_name] - if len(interfaces) == 0: - interfaces = [p for p in network_programmers if p.get_name() == i_name] - else: - self.logger.info("Probing for interfaces...") - interfaces = self._search_interface(self.args.serial) + if self.args.interface != self.AUTO_INTERFACE: + available_interfaces = list( + filter( + lambda p: p.get_name() == self.args.interface, + all_flash_interfaces, + ) + ) - if len(interfaces) == 0: + else: + self.logger.info("Probing for local interfaces...") + available_interfaces = self._search_interface(local_flash_interfaces) + + if not available_interfaces: # Probe network blackmagic self.logger.info("Probing for network interfaces...") - interfaces = self._search_network_interface(self.args.serial) + available_interfaces = self._search_interface(network_flash_interfaces) - if len(interfaces) == 0: + if not available_interfaces: self.logger.error("No interface found") return 1 - - if len(interfaces) > 1: + elif len(available_interfaces) > 1: self.logger.error("Multiple interfaces found: ") self.logger.error( - f"Please specify '--interface={[i.get_name() for i in interfaces]}'" + f"Please specify '--interface={[i.get_name() for i in available_interfaces]}'" ) return 1 - interface = interfaces[0] + interface = available_interfaces.pop(0) - if self.args.serial: + if self.args.serial != self.AUTO_INTERFACE: interface.set_serial(self.args.serial) self.logger.info( - f"Flashing {bin_path} via {interface.get_name()} with {self.args.serial}" + f"Flashing {file_path} via {interface.get_name()} with {self.args.serial}" ) else: - self.logger.info(f"Flashing {bin_path} via {interface.get_name()}") + self.logger.info(f"Flashing {file_path} via {interface.get_name()}") - if not interface.flash(bin_path): + if not interface.flash(file_path, self.args.verify): self.logger.error(f"Failed to flash via {interface.get_name()}") return 1 flash_time = time.time() - start_time - bin_size = os.path.getsize(bin_path) self.logger.info(f"Flashed successfully in {flash_time:.2f}s") - self.logger.info(f"Effective speed: {bin_size / flash_time / 1024:.2f} KiB/s") + if file_path.endswith(".bin"): + bin_size = os.path.getsize(file_path) + self.logger.info( + f"Effective speed: {bin_size / flash_time / 1024:.2f} KiB/s" + ) return 0 diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 1c2f2bdf..1630135c 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -144,24 +144,20 @@ dist_env = env.Clone( ], ) -openocd_target = dist_env.OpenOCDFlash( +flash_target = dist_env.FwFlash( dist_env["UFBT_STATE_DIR"].File("flash"), - dist_env["FW_BIN"], - OPENOCD_COMMAND=[ - "-c", - "program ${SOURCE.posix} reset exit 0x08000000", - ], + dist_env["FW_ELF"], ) -dist_env.Alias("firmware_flash", openocd_target) -dist_env.Alias("flash", openocd_target) +dist_env.Alias("firmware_flash", flash_target) +dist_env.Alias("flash", flash_target) if env["FORCE"]: - env.AlwaysBuild(openocd_target) + env.AlwaysBuild(flash_target) firmware_jflash = dist_env.JFlash( dist_env["UFBT_STATE_DIR"].File("jflash"), dist_env["FW_BIN"], - JFLASHADDR="0x20000000", + JFLASHADDR="0x08000000", ) dist_env.Alias("firmware_jflash", firmware_jflash) dist_env.Alias("jflash", firmware_jflash) @@ -213,21 +209,6 @@ dist_env.PhonyTarget( GDBPYOPTS=debug_other_opts, ) - -dist_env.PhonyTarget( - "flash_blackmagic", - "$GDB $GDBOPTS $SOURCES $GDBFLASH", - source=dist_env["FW_ELF"], - GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", - GDBREMOTE="${BLACKMAGIC_ADDR}", - GDBFLASH=[ - "-ex", - "load", - "-ex", - "quit", - ], -) - flash_usb_full = dist_env.UsbInstall( dist_env["UFBT_STATE_DIR"].File("usbinstall"), [], diff --git a/scripts/ufbt/commandline.scons b/scripts/ufbt/commandline.scons index 349b4ef2..99c34c35 100644 --- a/scripts/ufbt/commandline.scons +++ b/scripts/ufbt/commandline.scons @@ -55,9 +55,21 @@ vars.AddVariables( "Blackmagic probe location", "auto", ), + EnumVariable( + "SWD_TRANSPORT", + help="SWD interface adapter type", + default="auto", + allowed_values=[ + "auto", + "cmsis-dap", + "stlink", + "blackmagic_usb", + "blackmagic_wifi", + ], + ), ( - "OPENOCD_ADAPTER_SERIAL", - "OpenOCD adapter serial number", + "SWD_TRANSPORT_SERIAL", + "SWD interface adapter serial number", "auto", ), ( diff --git a/scripts/ufbt/site_tools/ufbt_help.py b/scripts/ufbt/site_tools/ufbt_help.py index 3d7f6f00..1df6a059 100644 --- a/scripts/ufbt/site_tools/ufbt_help.py +++ b/scripts/ufbt/site_tools/ufbt_help.py @@ -20,8 +20,8 @@ Building: Build FAP app with appid={APPID}; upload & start it over USB Flashing & debugging: - flash, flash_blackmagic, *jflash: - Flash firmware to target using debug probe + flash, *jflash: + Flash firmware to target using SWD probe. See also SWD_TRANSPORT, SWD_TRANSPORT_SERIAL flash_usb, flash_usb_full: Install firmware using self-update package debug, debug_other, blackmagic: diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 096cbd54..f75d5e0e 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -175,9 +175,21 @@ vars.AddVariables( "Blackmagic probe location", "auto", ), + EnumVariable( + "SWD_TRANSPORT", + help="SWD interface adapter type", + default="auto", + allowed_values=[ + "auto", + "cmsis-dap", + "stlink", + "blackmagic_usb", + "blackmagic_wifi", + ], + ), ( - "OPENOCD_ADAPTER_SERIAL", - "OpenOCD adapter serial number", + "SWD_TRANSPORT_SERIAL", + "SWD interface adapter serial number", "auto", ), ( diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 5c6f18d6..97b7ac09 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -23,18 +23,14 @@ appenv.Replace( appenv.AppendUnique( CCFLAGS=[ - "-ggdb3", "-mword-relocations", "-mlong-calls", "-fno-common", "-nostdlib", - "-fvisibility=hidden", ], LINKFLAGS=[ "-Ur", "-Wl,-Ur", - # "-Wl,--orphan-handling=error", - "-Bsymbolic", "-nostartfiles", "-mlong-calls", "-fno-common",