Merge branch 'dev' of https://github.com/DarkFlippers/unleashed-firmware into xfw-dev --nobuild

This commit is contained in:
Willy-JL
2023-08-13 02:26:24 +02:00
95 changed files with 1850 additions and 299 deletions
+2 -1
View File
@@ -2,6 +2,7 @@ import os
import re
from dataclasses import dataclass, field
from enum import Enum
from fbt.util import resolve_real_dir_node
from typing import Callable, ClassVar, List, Optional, Tuple, Union
@@ -147,7 +148,7 @@ class AppManager:
FlipperApplication(
*args,
**kw,
_appdir=app_dir_node,
_appdir=resolve_real_dir_node(app_dir_node),
_apppath=os.path.dirname(app_manifest_path),
_appmanager=self,
),
+2 -10
View File
@@ -45,7 +45,7 @@ def single_quote(arg_list):
return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list)
def extract_abs_dir(node):
def resolve_real_dir_node(node):
if isinstance(node, SCons.Node.FS.EntryProxy):
node = node.get()
@@ -53,15 +53,7 @@ def extract_abs_dir(node):
if os.path.exists(repo_dir.abspath):
return repo_dir
def extract_abs_dir_path(node):
abs_dir_node = extract_abs_dir(node)
if abs_dir_node is None:
raise StopError(f"Can't find absolute path for {node.name}")
# Don't return abspath attribute (type is str), it will break in
# OverrideEnvironment.subst_list() by splitting path on spaces
return abs_dir_node
raise StopError(f"Can't find absolute path for {node.name} ({node})")
def path_as_posix(path):
+8 -10
View File
@@ -8,11 +8,14 @@ from SCons.Errors import StopError
def icons_emitter(target, source, env):
icons_src = env.GlobRecursive("*.png", env["ICON_SRC_DIR"])
icons_src += env.GlobRecursive("frame_rate", env["ICON_SRC_DIR"])
target = [
target[0].File(env.subst("${ICON_FILE_NAME}.c")),
target[0].File(env.subst("${ICON_FILE_NAME}.h")),
]
return target, source
return target, icons_src
def proto_emitter(target, source, env):
@@ -104,21 +107,16 @@ def proto_ver_generator(target, source, env):
def CompileIcons(env, target_dir, source_dir, *, icon_bundle_name="assets_icons"):
# Gathering icons sources
try:
os.mkdir(str(source_dir))
except FileExistsError:
pass
icons_src = env.GlobRecursive("*.png", source_dir)
icons_src += env.GlobRecursive("frame_rate", source_dir)
icons = env.IconBuilder(
return env.IconBuilder(
target_dir,
source_dir,
None,
ICON_SRC_DIR=source_dir,
ICON_FILE_NAME=icon_bundle_name,
)
env.Depends(icons, icons_src)
return icons
def generate(env):
@@ -141,7 +139,7 @@ def generate(env):
BUILDERS={
"IconBuilder": Builder(
action=Action(
'${PYTHON3} ${ASSETS_COMPILER} icons ${ABSPATHGETTERFUNC(SOURCE)} ${TARGET.dir} --filename "${ICON_FILE_NAME}"',
'${PYTHON3} ${ASSETS_COMPILER} icons ${ICON_SRC_DIR} ${TARGET.dir} --filename "${ICON_FILE_NAME}"',
"${ICONSCOMSTR}",
),
emitter=icons_emitter,
+5 -4
View File
@@ -11,7 +11,7 @@ from fbt.appmanifest import FlipperApplication, FlipperAppType, FlipperManifestE
from fbt.elfmanifest import assemble_manifest_data
from fbt.fapassets import FileBundler
from fbt.sdk.cache import SdkCache
from fbt.util import extract_abs_dir_path
from fbt.util import resolve_real_dir_node
from SCons.Action import Action
from SCons.Builder import Builder
from SCons.Errors import UserError
@@ -50,7 +50,8 @@ class AppBuilder:
def _setup_app_env(self):
self.app_env = self.fw_env.Clone(
FAP_SRC_DIR=self.app._appdir, FAP_WORK_DIR=self.app_work_dir
FAP_SRC_DIR=self.app._appdir,
FAP_WORK_DIR=self.app_work_dir,
)
self.app_env.VariantDir(self.app_work_dir, self.app._appdir, duplicate=False)
@@ -119,7 +120,7 @@ class AppBuilder:
CPPDEFINES=lib_def.cdefines,
CPPPATH=list(
map(
lambda cpath: extract_abs_dir_path(self.app._appdir.Dir(cpath)),
lambda cpath: resolve_real_dir_node(self.app._appdir.Dir(cpath)),
lib_def.cincludes,
)
),
@@ -133,7 +134,7 @@ class AppBuilder:
def _build_app(self):
self.app_env.Append(
LIBS=[*self.app.fap_libs, *self.private_libs],
CPPPATH=self.app_env.Dir(self.app_work_dir),
CPPPATH=[self.app_env.Dir(self.app_work_dir), self.app._appdir],
)
app_sources = list(
+4
View File
@@ -26,6 +26,10 @@ class Programmer(ABC):
def option_bytes_set(self, file_path: str) -> bool:
pass
@abstractmethod
def option_bytes_recover(self) -> bool:
pass
@abstractmethod
def otp_write(self, address: int, file_path: str) -> bool:
pass
+32 -23
View File
@@ -44,11 +44,11 @@ class OpenOCDProgrammer(Programmer):
self.logger = logging.getLogger()
def reset(self, mode: Programmer.RunMode = Programmer.RunMode.Run) -> bool:
stm32 = STM32WB55()
stm32 = STM32WB55(self.openocd)
if mode == Programmer.RunMode.Run:
stm32.reset(self.openocd, stm32.RunMode.Run)
stm32.reset(stm32.RunMode.Run)
elif mode == Programmer.RunMode.Stop:
stm32.reset(self.openocd, stm32.RunMode.Init)
stm32.reset(stm32.RunMode.Init)
else:
raise Exception("Unknown mode")
@@ -96,11 +96,11 @@ class OpenOCDProgrammer(Programmer):
def option_bytes_validate(self, file_path: str) -> bool:
# Registers
stm32 = STM32WB55()
stm32 = STM32WB55(self.openocd)
# OpenOCD
self.openocd.start()
stm32.reset(self.openocd, stm32.RunMode.Init)
stm32.reset(stm32.RunMode.Init)
# Generate Option Bytes data
ob_data = OptionBytesData(file_path)
@@ -133,7 +133,7 @@ class OpenOCDProgrammer(Programmer):
self._ob_print_diff_table(ob_reference, ob_compare, self.logger.error)
# Stop OpenOCD
stm32.reset(self.openocd, stm32.RunMode.Run)
stm32.reset(stm32.RunMode.Run)
self.openocd.stop()
return return_code
@@ -143,11 +143,11 @@ class OpenOCDProgrammer(Programmer):
def option_bytes_set(self, file_path: str) -> bool:
# Registers
stm32 = STM32WB55()
stm32 = STM32WB55(self.openocd)
# OpenOCD
self.openocd.start()
stm32.reset(self.openocd, stm32.RunMode.Init)
stm32.reset(stm32.RunMode.Init)
# Generate Option Bytes data
ob_data = OptionBytesData(file_path)
@@ -159,11 +159,11 @@ class OpenOCDProgrammer(Programmer):
ob_dwords = int(ob_length / 8)
# Clear flash errors
stm32.clear_flash_errors(self.openocd)
stm32.clear_flash_errors()
# Unlock Flash and Option Bytes
stm32.flash_unlock(self.openocd)
stm32.option_bytes_unlock(self.openocd)
stm32.flash_unlock()
stm32.option_bytes_unlock()
ob_need_to_apply = False
@@ -194,16 +194,16 @@ class OpenOCDProgrammer(Programmer):
self.openocd.write_32(device_reg_addr, ob_value)
if ob_need_to_apply:
stm32.option_bytes_apply(self.openocd)
stm32.option_bytes_apply()
else:
self.logger.info("Option Bytes are already correct")
# Load Option Bytes
# That will reset and also lock the Option Bytes and the Flash
stm32.option_bytes_load(self.openocd)
stm32.option_bytes_load()
# Stop OpenOCD
stm32.reset(self.openocd, stm32.RunMode.Run)
stm32.reset(stm32.RunMode.Run)
self.openocd.stop()
return True
@@ -233,11 +233,10 @@ class OpenOCDProgrammer(Programmer):
self.logger.debug(f"Data: {data.hex().upper()}")
# Start OpenOCD
oocd = self.openocd
oocd.start()
self.openocd.start()
# Registers
stm32 = STM32WB55()
stm32 = STM32WB55(self.openocd)
try:
# Check that OTP is empty for the given address
@@ -245,7 +244,7 @@ class OpenOCDProgrammer(Programmer):
already_written = True
for i in range(0, data_size, 4):
file_word = int.from_bytes(data[i : i + 4], "little")
device_word = oocd.read_32(address + i)
device_word = self.openocd.read_32(address + i)
if device_word != 0xFFFFFFFF and device_word != file_word:
self.logger.error(
f"OTP memory at {address + i:08X} is not empty: {device_word:08X}"
@@ -260,7 +259,7 @@ class OpenOCDProgrammer(Programmer):
return OpenOCDProgrammerResult.Success
self.reset(self.RunMode.Stop)
stm32.clear_flash_errors(oocd)
stm32.clear_flash_errors()
# Write OTP memory by 8 bytes
for i in range(0, data_size, 8):
@@ -269,14 +268,14 @@ class OpenOCDProgrammer(Programmer):
self.logger.debug(
f"Writing {word_1:08X} {word_2:08X} to {address + i:08X}"
)
stm32.write_flash_64(oocd, address + i, word_1, word_2)
stm32.write_flash_64(address + i, word_1, word_2)
# Validate OTP memory
validation_result = True
for i in range(0, data_size, 4):
file_word = int.from_bytes(data[i : i + 4], "little")
device_word = oocd.read_32(address + i)
device_word = self.openocd.read_32(address + i)
if file_word != device_word:
self.logger.error(
f"Validation failed: {file_word:08X} != {device_word:08X} at {address + i:08X}"
@@ -284,11 +283,21 @@ class OpenOCDProgrammer(Programmer):
validation_result = False
finally:
# Stop OpenOCD
stm32.reset(oocd, stm32.RunMode.Run)
oocd.stop()
stm32.reset(stm32.RunMode.Run)
self.openocd.stop()
return (
OpenOCDProgrammerResult.Success
if validation_result
else OpenOCDProgrammerResult.ErrorValidation
)
def option_bytes_recover(self) -> bool:
try:
self.openocd.start()
stm32 = STM32WB55(self.openocd)
stm32.reset(stm32.RunMode.Halt)
stm32.option_bytes_recover()
return True
finally:
self.openocd.stop()
+13 -4
View File
@@ -16,6 +16,7 @@ class Register32:
self.names = [definition.name for definition in definition_list] # typecheck
self.address = address
self.definition_list = definition_list
self.openocd = None
# Validate that the definitions are not overlapping
for i in range(len(definition_list)):
@@ -76,6 +77,14 @@ class Register32:
def __dir__(self):
return self.names
def set_openocd(self, openocd: OpenOCD):
self.openocd = openocd
def get_openocd(self) -> OpenOCD:
if self.openocd is None:
raise RuntimeError("OpenOCD is not installed")
return self.openocd
def set(self, value: int):
for definition in self.definition_list:
definition.value = (value >> definition.offset) & (
@@ -88,8 +97,8 @@ class Register32:
value |= definition.value << definition.offset
return value
def load(self, openocd: OpenOCD):
self.set(openocd.read_32(self.address))
def load(self):
self.set(self.get_openocd().read_32(self.address))
def store(self, openocd: OpenOCD):
openocd.write_32(self.address, self.get())
def store(self):
self.get_openocd().write_32(self.address, self.get())
+77 -56
View File
@@ -108,23 +108,27 @@ class STM32WB55:
15: None, # Core 2 Options
}
def __init__(self):
def __init__(self, openocd: OpenOCD):
self.openocd = openocd
self.logger = logging.getLogger("STM32WB55")
self.FLASH_CR.set_openocd(self.openocd)
self.FLASH_SR.set_openocd(self.openocd)
class RunMode(Enum):
Init = "init"
Run = "run"
Halt = "halt"
def reset(self, oocd: OpenOCD, mode: RunMode):
def reset(self, mode: RunMode):
self.logger.debug("Resetting device")
oocd.send_tcl(f"reset {mode.value}")
self.openocd.send_tcl(f"reset {mode.value}")
def clear_flash_errors(self, oocd: OpenOCD):
def clear_flash_errors(self):
# Errata 2.2.9: Flash OPTVERR flag is always set after system reset
# And also clear all other flash error flags
self.logger.debug("Resetting flash errors")
self.FLASH_SR.load(oocd)
self.FLASH_SR.load()
self.FLASH_SR.OP_ERR = 1
self.FLASH_SR.PROG_ERR = 1
self.FLASH_SR.WRP_ERR = 1
@@ -135,51 +139,51 @@ class STM32WB55:
self.FLASH_SR.FAST_ERR = 1
self.FLASH_SR.RD_ERR = 1
self.FLASH_SR.OPTV_ERR = 1
self.FLASH_SR.store(oocd)
self.FLASH_SR.store()
def flash_unlock(self, oocd: OpenOCD):
def flash_unlock(self):
# Check if flash is already unlocked
self.FLASH_CR.load(oocd)
self.FLASH_CR.load()
if self.FLASH_CR.LOCK == 0:
self.logger.debug("Flash is already unlocked")
return
# Unlock flash
self.logger.debug("Unlocking Flash")
oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1)
oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2)
self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1)
self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2)
# Check if flash is unlocked
self.FLASH_CR.load(oocd)
self.FLASH_CR.load()
if self.FLASH_CR.LOCK == 0:
self.logger.debug("Flash unlocked")
else:
self.logger.error("Flash unlock failed")
raise Exception("Flash unlock failed")
def option_bytes_unlock(self, oocd: OpenOCD):
def option_bytes_unlock(self):
# Check if options is already unlocked
self.FLASH_CR.load(oocd)
self.FLASH_CR.load()
if self.FLASH_CR.OPT_LOCK == 0:
self.logger.debug("Options is already unlocked")
return
# Unlock options
self.logger.debug("Unlocking Options")
oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1)
oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2)
self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1)
self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2)
# Check if options is unlocked
self.FLASH_CR.load(oocd)
self.FLASH_CR.load()
if self.FLASH_CR.OPT_LOCK == 0:
self.logger.debug("Options unlocked")
else:
self.logger.error("Options unlock failed")
raise Exception("Options unlock failed")
def option_bytes_lock(self, oocd: OpenOCD):
def option_bytes_lock(self):
# Check if options is already locked
self.FLASH_CR.load(oocd)
self.FLASH_CR.load()
if self.FLASH_CR.OPT_LOCK == 1:
self.logger.debug("Options is already locked")
return
@@ -187,19 +191,19 @@ class STM32WB55:
# Lock options
self.logger.debug("Locking Options")
self.FLASH_CR.OPT_LOCK = 1
self.FLASH_CR.store(oocd)
self.FLASH_CR.store()
# Check if options is locked
self.FLASH_CR.load(oocd)
self.FLASH_CR.load()
if self.FLASH_CR.OPT_LOCK == 1:
self.logger.debug("Options locked")
else:
self.logger.error("Options lock failed")
raise Exception("Options lock failed")
def flash_lock(self, oocd: OpenOCD):
def flash_lock(self):
# Check if flash is already locked
self.FLASH_CR.load(oocd)
self.FLASH_CR.load()
if self.FLASH_CR.LOCK == 1:
self.logger.debug("Flash is already locked")
return
@@ -207,31 +211,31 @@ class STM32WB55:
# Lock flash
self.logger.debug("Locking Flash")
self.FLASH_CR.LOCK = 1
self.FLASH_CR.store(oocd)
self.FLASH_CR.store()
# Check if flash is locked
self.FLASH_CR.load(oocd)
self.FLASH_CR.load()
if self.FLASH_CR.LOCK == 1:
self.logger.debug("Flash locked")
else:
self.logger.error("Flash lock failed")
raise Exception("Flash lock failed")
def option_bytes_apply(self, oocd: OpenOCD):
def option_bytes_apply(self):
self.logger.debug("Applying Option Bytes")
self.FLASH_CR.load(oocd)
self.FLASH_CR.load()
self.FLASH_CR.OPT_STRT = 1
self.FLASH_CR.store(oocd)
self.FLASH_CR.store()
# Wait for Option Bytes to be applied
self.flash_wait_for_operation(oocd)
self.flash_wait_for_operation()
def option_bytes_load(self, oocd: OpenOCD):
def option_bytes_load(self):
self.logger.debug("Loading Option Bytes")
self.FLASH_CR.load(oocd)
self.FLASH_CR.load()
self.FLASH_CR.OBL_LAUNCH = 1
self.FLASH_CR.store(oocd)
self.FLASH_CR.store()
def option_bytes_id_to_address(self, id: int) -> int:
# Check if this option byte (dword) is mapped to a register
@@ -241,16 +245,16 @@ class STM32WB55:
return device_reg_addr
def flash_wait_for_operation(self, oocd: OpenOCD):
def flash_wait_for_operation(self):
# Wait for flash operation to complete
# TODO: timeout
while True:
self.FLASH_SR.load(oocd)
self.FLASH_SR.load()
if self.FLASH_SR.BSY == 0:
break
def flash_dump_status_register(self, oocd: OpenOCD):
self.FLASH_SR.load(oocd)
def flash_dump_status_register(self):
self.FLASH_SR.load()
self.logger.info(f"FLASH_SR: {self.FLASH_SR.get():08x}")
if self.FLASH_SR.EOP:
self.logger.info(" End of operation")
@@ -283,70 +287,87 @@ class STM32WB55:
if self.FLASH_SR.PESD:
self.logger.info(" Programming / erase operation suspended.")
def write_flash_64(self, oocd: OpenOCD, address: int, word_1: int, word_2: int):
def write_flash_64(self, address: int, word_1: int, word_2: int):
self.logger.debug(f"Writing flash at address {address:08x}")
if address % 8 != 0:
self.logger.error("Address must be aligned to 8 bytes")
raise Exception("Address must be aligned to 8 bytes")
if word_1 == oocd.read_32(address) and word_2 == oocd.read_32(address + 4):
if word_1 == self.openocd.read_32(address) and word_2 == self.openocd.read_32(
address + 4
):
self.logger.debug("Data is already programmed")
return
self.flash_unlock(oocd)
self.flash_unlock()
# Check that no flash main memory operation is ongoing by checking the BSY bit
self.FLASH_SR.load(oocd)
self.FLASH_SR.load()
if self.FLASH_SR.BSY:
self.logger.error("Flash is busy")
self.flash_dump_status_register(oocd)
self.flash_dump_status_register()
raise Exception("Flash is busy")
# Enable end of operation interrupts and error interrupts
self.FLASH_CR.load(oocd)
self.FLASH_CR.load()
self.FLASH_CR.EOPIE = 1
self.FLASH_CR.ERRIE = 1
self.FLASH_CR.store(oocd)
self.FLASH_CR.store()
# Check that flash memory program and erase operations are allowed
if self.FLASH_SR.PESD:
self.logger.error("Flash operations are not allowed")
self.flash_dump_status_register(oocd)
self.flash_dump_status_register()
raise Exception("Flash operations are not allowed")
# Check and clear all error programming flags due to a previous programming.
self.clear_flash_errors(oocd)
self.clear_flash_errors()
# Set the PG bit in the Flash memory control register (FLASH_CR)
self.FLASH_CR.load(oocd)
self.FLASH_CR.load()
self.FLASH_CR.PG = 1
self.FLASH_CR.store(oocd)
self.FLASH_CR.store()
# Perform the data write operation at the desired memory address, only double word (64 bits) can be programmed.
# Write the first word
oocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}")
self.openocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}")
# Write the second word
oocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}")
self.openocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}")
# Wait for the BSY bit to be cleared
self.flash_wait_for_operation(oocd)
self.flash_wait_for_operation()
# Check that EOP flag is set in the FLASH_SR register
self.FLASH_SR.load(oocd)
self.FLASH_SR.load()
if not self.FLASH_SR.EOP:
self.logger.error("Flash operation failed")
self.flash_dump_status_register(oocd)
self.flash_dump_status_register()
raise Exception("Flash operation failed")
# Clear the EOP flag
self.FLASH_SR.load(oocd)
self.FLASH_SR.load()
self.FLASH_SR.EOP = 1
self.FLASH_SR.store(oocd)
self.FLASH_SR.store()
# Clear the PG bit in the FLASH_CR register
self.FLASH_CR.load(oocd)
self.FLASH_CR.load()
self.FLASH_CR.PG = 0
self.FLASH_CR.store(oocd)
self.FLASH_CR.store()
self.flash_lock(oocd)
self.flash_lock()
def option_bytes_recover(self):
self.openocd.send_tcl("mww 0x58004010 0x8000") # set OPTVERR to reset
# Replace flash_unlock and option_bytes_unlock with the following lines, if this does not work
# self.openocd.send_tcl("mww 0x58004008 0x45670123") # unlock FLASH
# self.openocd.send_tcl("mww 0x58004008 0xCDEF89AB")
# self.openocd.send_tcl("mww 0x5800400c 0x08192A3B") # unlock OB
# self.openocd.send_tcl("mww 0x5800400c 0x4C5D6E7F")
self.flash_unlock()
self.option_bytes_unlock()
self.openocd.send_tcl("mmw 0x58004020 0x3ffff1aa 0xffffffff") # Reset OB
self.openocd.send_tcl("mww 0x5800402c 0xff") # Reset WRP1AR
self.openocd.send_tcl("mww 0x58004030 0xff") # Reset WRP1BR
self.openocd.send_tcl("mmw 0x58004014 0x00020000 0") # OPTSTRT
self.openocd.send_tcl("mmw 0x58004014 0x08000000 0") # OBL_LAUNCH
+20
View File
@@ -22,6 +22,12 @@ class Main(App):
self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
self._add_args(self.parser_set)
self.parser_set.set_defaults(func=self.set)
# Set command
self.parser_recover = self.subparsers.add_parser(
"recover", help="Recover Option Bytes"
)
self._add_args(self.parser_recover)
self.parser_recover.set_defaults(func=self.recover)
def _add_args(self, parser):
parser.add_argument(
@@ -75,6 +81,20 @@ class Main(App):
return return_code
def recover(self):
self.logger.info("Setting Option Bytes")
# OpenOCD
openocd = OpenOCDProgrammer(
self.args.interface,
self.args.port_base,
self.args.serial,
)
openocd.option_bytes_recover()
return 0
if __name__ == "__main__":
Main()()
+18 -17
View File
@@ -1,12 +1,11 @@
from SCons.Platform import TempFileMunge
from SCons.Node import FS
from SCons.Errors import UserError
import os
import multiprocessing
import os
import pathlib
from SCons.Errors import UserError
from SCons.Node import FS
from SCons.Platform import TempFileMunge
SetOption("num_jobs", multiprocessing.cpu_count())
SetOption("max_drift", 1)
# SetOption("silent", False)
@@ -67,16 +66,15 @@ core_env.Append(CPPDEFINES=GetOption("extra_defines"))
# Now we can import stuff bundled with SDK - it was added to sys.path by ufbt_state
from fbt.util import (
tempfile_arg_esc_func,
single_quote,
extract_abs_dir,
extract_abs_dir_path,
wrap_tempfile,
path_as_posix,
)
from fbt.appmanifest import FlipperAppType, FlipperApplication
from fbt.appmanifest import FlipperApplication, FlipperAppType
from fbt.sdk.cache import SdkCache
from fbt.util import (
path_as_posix,
resolve_real_dir_node,
single_quote,
tempfile_arg_esc_func,
wrap_tempfile,
)
# Base environment with all tools loaded from SDK
env = core_env.Clone(
@@ -107,7 +105,7 @@ env = core_env.Clone(
PROGSUFFIX=".elf",
TEMPFILEARGESCFUNC=tempfile_arg_esc_func,
SINGLEQUOTEFUNC=single_quote,
ABSPATHGETTERFUNC=extract_abs_dir_path,
ABSPATHGETTERFUNC=resolve_real_dir_node,
APPS=[],
UFBT_API_VERSION=SdkCache(
core_env.subst("$SDK_DEFINITION"), load_version_only=True
@@ -277,7 +275,7 @@ for app in known_extapps:
continue
app_artifacts = appenv.BuildAppElf(app)
app_src_dir = extract_abs_dir(app_artifacts.app._appdir)
app_src_dir = resolve_real_dir_node(app_artifacts.app._appdir)
app_artifacts.installer = [
appenv.Install(app_src_dir.Dir("dist"), app_artifacts.compact),
appenv.Install(app_src_dir.Dir("dist").Dir("debug"), app_artifacts.debug),
@@ -348,6 +346,9 @@ appenv.PhonyTarget(
'${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py" -p ${FLIP_PORT}',
)
# Update WiFi devboard firmware
dist_env.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py")
# Linter
dist_env.PhonyTarget(
+2
View File
@@ -26,6 +26,8 @@ Flashing & debugging:
Install firmware using self-update package
debug, debug_other, blackmagic:
Start GDB
devboard_flash:
Update WiFi dev board with the latest firmware
Other:
cli: