mm-iot-sdk/builder/main.py
Morse Micro 4f4eb7fc7a Morse Micro MM-IoT-SDK release 2.9.7
This commit introduces version 2.9.7 of the Morse Micro MM-IoT-SDK
for ARM microcontroller base platforms.
2025-10-04 02:04:28 +10:00

366 lines
12 KiB
Python

# Copyright 2014-present PlatformIO <contact@platformio.org>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
from os import makedirs
from os.path import basename, isdir, join
from platform import system
from SCons.Script import (
ARGUMENTS,
COMMAND_LINE_TARGETS,
AlwaysBuild,
Builder,
Default,
DefaultEnvironment,
)
from platformio.public import list_serial_ports
def BeforeUpload(_target, _source, env): # pylint: disable=W0613,W0621 # noqa: N802
env.AutodetectUploadPort()
upload_options = {}
if "BOARD" in env:
upload_options = env.BoardConfig().get("upload", {})
if not bool(upload_options.get("disable_flushing", False)):
env.FlushSerialBuffer("$UPLOAD_PORT")
before_ports = list_serial_ports()
if bool(upload_options.get("use_1200bps_touch", False)):
env.TouchSerialPort("$UPLOAD_PORT", 1200)
if bool(upload_options.get("wait_for_upload_port", False)):
env.Replace(UPLOAD_PORT=env.WaitForNewSerialPort(before_ports))
env = DefaultEnvironment()
platform = env.PioPlatform()
board = env.BoardConfig()
SIZE_PROG_SECTIONS = [
".text", ".data", ".rodata", ".text.align", ".ARM.exidx",
".isr_vector", ".ARM", ".init_array", ".fini_array",
]
SIZE_DATA_SECTIONS = [
".data", ".bss", ".noinit", "._user_heap_stack",
]
def make_size_reg_exp(sections):
r"""
Make a regular expression in the following form given a list of sections:
^(?:\.data|\.bss|\.noinit|\._user_heap_stack)\s+(\d+).*
"""
escaped_sections = [x.replace(".", r"\.") for x in sections]
return rf"^(?:{'|'.join(escaped_sections)})\s+(\d+).*"
env.Replace(
AR="arm-none-eabi-gcc-ar",
AS="arm-none-eabi-as",
CC="arm-none-eabi-gcc",
CXX="arm-none-eabi-g++",
GDB="arm-none-eabi-gdb",
OBJCOPY="arm-none-eabi-objcopy",
RANLIB="arm-none-eabi-gcc-ranlib",
SIZETOOL="arm-none-eabi-size",
ARFLAGS=["rc"],
SIZEPROGREGEXP=make_size_reg_exp(SIZE_PROG_SECTIONS),
SIZEDATAREGEXP=make_size_reg_exp(SIZE_DATA_SECTIONS),
SIZECHECKCMD="$SIZETOOL -A -d $SOURCES",
SIZEPRINTCMD="$SIZETOOL -B -d $SOURCES",
PROGSUFFIX=".elf"
)
# Allow user to override via pre:script
if env.get("PROGNAME", "program") == "program":
env.Replace(PROGNAME="firmware")
env.Append(
BUILDERS={
"ElfToBin": Builder(
action=env.VerboseAction("$OBJCOPY -O binary $SOURCES $TARGET", "Building $TARGET"),
suffix=".bin"
),
"ElfToHex": Builder(
action=env.VerboseAction(
"$OBJCOPY -O ihex -R .eeprom $SOURCES $TARGET", "Building $TARGET"),
suffix=".hex"
)
}
)
if not env.get("PIOFRAMEWORK"):
env.SConscript("frameworks/_bare.py")
#
# Target: Build executable and linkable firmware
#
frameworks = env.get("PIOFRAMEWORK", [])
if "zephyr" in frameworks:
env.SConscript(
join(platform.get_package_dir(
"framework-zephyr"), "scripts", "platformio", "platformio-build-pre.py"),
exports={"env": env}
)
target_elf = None
if "nobuild" in COMMAND_LINE_TARGETS:
target_elf = join("$BUILD_DIR", "${PROGNAME}.elf")
target_firm = join("$BUILD_DIR", "${PROGNAME}.bin")
else:
target_elf = env.BuildProgram()
target_firm = env.ElfToBin(join("$BUILD_DIR", "${PROGNAME}"), target_elf)
env.Depends(target_firm, "checkprogsize")
AlwaysBuild(env.Alias("nobuild", target_firm))
target_buildprog = env.Alias("buildprog", target_firm, target_firm)
#
# Target: Print binary size
#
target_size = env.Alias(
"size", target_elf,
env.VerboseAction("$SIZEPRINTCMD", "Calculating size $SOURCE"))
AlwaysBuild(target_size)
#
# Target: Upload by default .bin file
#
upload_protocol = env.subst("$UPLOAD_PROTOCOL")
debug_tools = board.get("debug.tools", {})
upload_source = target_firm
upload_actions = []
if upload_protocol == "mbed":
upload_actions = [
env.VerboseAction(env.AutodetectUploadPort, "Looking for upload disk..."),
env.VerboseAction(env.UploadToDisk, "Uploading $SOURCE")
]
elif upload_protocol.startswith("blackmagic"):
env.Replace(
UPLOADER="$GDB",
UPLOADERFLAGS=[
"-nx",
"--batch",
"-ex", "target extended-remote $UPLOAD_PORT",
"-ex", "monitor %s_scan" %
("jtag" if upload_protocol == "blackmagic-jtag" else "swdp"),
"-ex", "attach 1",
"-ex", "load",
"-ex", "compare-sections",
"-ex", "kill"
],
UPLOADCMD="$UPLOADER $UPLOADERFLAGS $SOURCE"
)
upload_source = target_elf
upload_actions = [
env.VerboseAction(env.AutodetectUploadPort, "Looking for BlackMagic port..."),
env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")
]
elif upload_protocol.startswith("jlink"):
def _jlink_cmd_script(env, source):
build_dir = env.subst("$BUILD_DIR")
if not isdir(build_dir):
makedirs(build_dir, exist_ok=True)
script_path = join(build_dir, "upload.jlink")
commands = [
"h",
"loadbin {}, {}".format(source, board.get(
"upload.offset_address", "0x08000000")),
"r",
"q"
]
with open(script_path, "w") as fp:
fp.write("\n".join(commands))
return script_path
env.Replace(
__jlink_cmd_script=_jlink_cmd_script,
UPLOADER="JLink.exe" if system() == "Windows" else "JLinkExe",
UPLOADERFLAGS=[
"-device", board.get("debug", {}).get("jlink_device"),
"-speed", env.GetProjectOption("debug_speed", "4000"),
"-if", ("jtag" if upload_protocol == "jlink-jtag" else "swd"),
"-autoconnect", "1",
"-NoGui", "1"
],
UPLOADCMD='$UPLOADER $UPLOADERFLAGS -CommanderScript '
'"${__jlink_cmd_script(__env__, SOURCE)}"'
)
upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
elif upload_protocol == "dfu":
hwids = board.get("build.hwids", [["0x0483", "0xDF11"]])
vid = hwids[0][0]
pid = hwids[0][1]
# default tool for all boards with embedded DFU bootloader over USB
_upload_tool = '"{}"'.format(join(platform.get_package_dir(
"tool-dfuutil") or "", "bin", "dfu-util"))
_upload_flags = [
"-d", ",".join([f"{hwid[0]}:{hwid[1]}" for hwid in hwids]),
"-a", "0", "-s",
"{}:leave".format(board.get("upload.offset_address", "0x08000000")), "-D"
]
upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
if "arduino" in frameworks:
if env.subst("$BOARD").startswith(("portenta", "opta")):
upload_actions.insert(
0,
env.VerboseAction(BeforeUpload, "Looking for upload port...")
)
elif board.get("build.mcu").startswith("stm32f103"):
# F103 series doesn't have embedded DFU over USB
# stm32duino bootloader (v1, v2) is used instead
def __configure_upload_port(env):
return basename(env.subst("$UPLOAD_PORT"))
_upload_tool = "maple_upload"
_upload_flags = [
"${__configure_upload_port(__env__)}",
board.get("upload.boot_version", 2),
f"{vid[2:]}:{pid[2:]}"
]
env.Replace(__configure_upload_port=__configure_upload_port)
upload_actions.insert(
0, env.VerboseAction(env.AutodetectUploadPort,
"Looking for upload port..."))
if "dfu-util" in _upload_tool:
# Add special DFU header to the binary image
env.AddPostAction(
join("$BUILD_DIR", "${PROGNAME}.bin"),
env.VerboseAction(
" ".join([
'"{}"'.format(join(platform.get_package_dir("tool-dfuutil") or "",
"bin", "dfu-suffix")),
f"-v {vid}",
f"-p {pid}",
"-d 0xffff", "-a", "$TARGET"
]), "Adding dfu suffix to ${PROGNAME}.bin"))
env.Replace(
UPLOADER=_upload_tool,
UPLOADERFLAGS=_upload_flags,
UPLOADCMD='$UPLOADER $UPLOADERFLAGS "${SOURCE.get_abspath()}"')
upload_source = target_firm
elif upload_protocol == "serial":
def __configure_upload_port(env):
return env.subst("$UPLOAD_PORT")
env.Replace(
__configure_upload_port=__configure_upload_port,
UPLOADER=join(
'"{}"'.format(platform.get_package_dir("tool-stm32duino")) or "",
"stm32flash", "stm32flash"),
UPLOADERFLAGS=[
"-g", board.get("upload.offset_address", "0x08000000"),
"-b", env.subst("$UPLOAD_SPEED") or "115200", "-w"
],
UPLOADCMD='$UPLOADER $UPLOADERFLAGS "$SOURCE" "${__configure_upload_port(__env__)}"'
)
upload_actions = [
env.VerboseAction(env.AutodetectUploadPort, "Looking for upload port..."),
env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")
]
elif upload_protocol == "hid":
def __configure_upload_port(env):
return basename(env.subst("$UPLOAD_PORT"))
env.Replace(
__configure_upload_port=__configure_upload_port,
UPLOADER="hid-flash",
UPLOADCMD='$UPLOADER "$SOURCES" "${__configure_upload_port(__env__)}"'
)
upload_actions = [
env.VerboseAction(env.AutodetectUploadPort, "Looking for upload port..."),
env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")
]
elif upload_protocol in debug_tools:
openocd_args = [
"-d%d" % (2 if int(ARGUMENTS.get("PIOVERBOSE", 0)) else 1)
]
openocd_args.extend(
debug_tools.get(upload_protocol).get("server").get("arguments", []))
if env.GetProjectOption("debug_speed", ""):
openocd_args.extend(
["-c", "adapter speed {}".format(env.GetProjectOption("debug_speed"))]
)
openocd_args.extend([
"-c",
"program {{$SOURCE}} {} verify reset; shutdown;".
format(board.get("upload.offset_address", ""))
])
openocd_args = [
f.replace("$PACKAGE_DIR",
platform.get_package_dir("tool-openocd") or "")
for f in openocd_args
]
env.Replace(
UPLOADER="openocd",
UPLOADERFLAGS=openocd_args,
UPLOADCMD="$UPLOADER $UPLOADERFLAGS")
if not board.get("upload").get("offset_address"):
upload_source = target_elf
upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
# custom upload tool
elif upload_protocol == "custom":
upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
else:
sys.stderr.write(f"Warning! Unknown upload protocol {upload_protocol}\n")
AlwaysBuild(env.Alias("upload", upload_source, upload_actions))
#
# Information about obsolete method of specifying linker scripts
#
if any("-Wl,-T" in f for f in env.get("LINKFLAGS", [])):
print("Warning! '-Wl,-T' option for specifying linker scripts is deprecated. "
"Please use 'board_build.ldscript' option in your 'platformio.ini' file.")
#
# Default targets
#
Default([target_buildprog, target_size])