fbt: initial blackmagic support (#1362)

* fbt: added separate script for Windows env setup; moved flash targets from firmware.scons to SConstruct; added Blackmagic support with automatic probe port resolution; added apps.c rebuild on any manifest.fam changes; fixed simultaneous flash & debug ops
* fbt: added networked BlackmagicResolver mode; added `get_blackmagic` target for IDE integration
* fbt: cleanup
* fbt: docs update; fixed blackmagic lookup on certain usb hubs
* fbt: removed explicit python serial port import
* fbt: cleanup
* fbt: raising exception on multiple serial blackmagic probes
This commit is contained in:
hedger
2022-07-04 19:53:04 +03:00
committed by GitHub
parent 793501d62d
commit 6b6ea44802
12 changed files with 220 additions and 50 deletions

View File

@@ -0,0 +1,74 @@
from SCons.Errors import StopError
class BlackmagicResolver:
BLACKMAGIC_HOSTNAME = "blackmagic.local"
def __init__(self, env):
self.env = env
# On Win:
# 'location': '1-5:x.0', 'name': 'COM4',
# 'location': '1-5:x.2', 'name': 'COM13',
# On Linux:
# 'location': '1-1.2:1.0', 'name': 'ttyACM0',
# 'location': '1-1.2:1.2', 'name': 'ttyACM1',
# On MacOS:
# 'location': '0-1.3', 'name': 'cu.usbmodemblackmagic1',
# 'location': '0-1.3', 'name': 'cu.usbmodemblackmagic3',
def _find_probe(self):
import serial.tools.list_ports as list_ports
ports = list(list_ports.grep("blackmagic"))
if len(ports) == 0:
# Blackmagic probe serial port not found, will be handled later
pass
elif len(ports) > 2:
raise StopError("More than one Blackmagic probe found")
else:
# If you're getting any issues with auto lookup, uncomment this
# print("\n".join([f"{p.device} {vars(p)}" for p in ports]))
return sorted(ports, key=lambda p: f"{p.location}_{p.name}")[0]
# Look up blackmagic probe hostname with dns
def _resolve_hostname(self):
import socket
try:
return socket.gethostbyname(self.BLACKMAGIC_HOSTNAME)
except socket.gaierror:
print("Failed to resolve Blackmagic hostname")
return None
def get_serial(self):
if not (probe := self._find_probe()):
return None
# print(f"Found Blackmagic probe on {probe.device}")
if self.env.subst("$PLATFORM") == "win32":
return f"\\\\.\\{probe.device}"
return probe.device
def get_networked(self):
if not (probe := self._resolve_hostname()):
return None
return f"tcp:{probe}:2345"
def __str__(self):
# print("distenv blackmagic", self.env.subst("$BLACKMAGIC"))
if (blackmagic := self.env.subst("$BLACKMAGIC")) != "auto":
return blackmagic
# print("Looking for Blackmagic...")
if probe := self.get_serial() or self.get_networked():
return probe
raise Exception("Please specify BLACKMAGIC=...")
def generate(env):
env.SetDefault(BLACKMAGIC_ADDR=BlackmagicResolver(env))
def exists(env):
return True

View File

@@ -54,20 +54,20 @@ def AddFwProject(env, base_env, fw_type, fw_env_key):
return project_env
def AddDebugTarget(env, alias, targetenv, force_flash=True):
debug_target = env.PhonyTarget(
alias,
"$GDBPYCOM",
source=targetenv["FW_ELF"],
GDBPYOPTS='-ex "source debug/FreeRTOS/FreeRTOS.py" '
'-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" '
'-ex "svd_load ${SVD_FILE}" '
'-ex "compare-sections"',
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"),
**kw,
)
if force_flash:
env.Depends(debug_target, targetenv["FW_FLASH"])
return debug_target
env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), openocd_target)
return openocd_target
def DistCommand(env, name, source, **kw):
@@ -85,8 +85,9 @@ def DistCommand(env, name, source, **kw):
def generate(env):
env.AddMethod(AddFwProject)
env.AddMethod(AddDebugTarget)
env.AddMethod(DistCommand)
env.AddMethod(AddOpenOCDFlashTarget)
env.SetDefault(
COPRO_MCU_FAMILY="STM32WB5x",
)

View File

@@ -34,9 +34,9 @@ def PhonyTarget(env, name, action, source=None, **kw):
source = []
phony_name = "phony_" + name
env.Pseudo(phony_name)
return env.AlwaysBuild(
env.Alias(name, env.Command(phony_name, source, action, **kw))
)
command = env.Command(phony_name, source, action, **kw)
env.AlwaysBuild(env.Alias(name, command))
return command
def generate(env):