Compare commits
2 commits
feature/us
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 01c5e0930e | |||
| cd27639e44 |
4 changed files with 151 additions and 8 deletions
13
README.md
13
README.md
|
|
@ -55,6 +55,19 @@ cd bootloader && pio run -e versapad_bootloader --target upload # einmalig
|
|||
pio run -e versapad_usb --target upload # danach jedes App-Update
|
||||
```
|
||||
|
||||
`bootloader/` ist ein eigenständiges PlatformIO-Projekt (eigene
|
||||
`platformio.ini`, kein Arduino-Framework). Für die PlatformIO-IDE-Buttons in
|
||||
VS Code müsste der Ordner separat als eigener Workspace geöffnet werden; über
|
||||
die Kommandozeile reicht `cd bootloader && pio run ...` im selben Fenster.
|
||||
|
||||
Sobald der Bootloader installiert ist, **verweigert `versapad --target
|
||||
upload` den normalen Upload** — dieser Weg schreibt ab `0x0000` und würde den
|
||||
Bootloader sonst kommentarlos überschreiben (`upload_openocd.py` prüft das
|
||||
vorher automatisch). Bauen und Debuggen über `versapad` bleiben uneingeschränkt
|
||||
möglich, nur der Upload ist betroffen. Für App-Updates danach `versapad_usb`
|
||||
verwenden; bewusst zurück zu reinem SWD-Betrieb geht über
|
||||
`pio run -e versapad -t erase-bootloader-and-flash`.
|
||||
|
||||
Details, Speicherlayout und die Bootloader-Aktivierung (kein physischer
|
||||
Reset-Taster auf dieser Platine) stehen in
|
||||
[10_usb_bootloader.md](doc/10_usb_bootloader.md).
|
||||
|
|
|
|||
5
bootloader/.gitignore
vendored
Normal file
5
bootloader/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
|
|
@ -56,6 +56,30 @@ erkennt den Schreibzugriff und springt selbständig in die neue App.
|
|||
`upload_uf2.py` sucht das Laufwerk aktuell nur über die Windows-API
|
||||
(`GetVolumeInformationW`) — keine macOS/Linux-Unterstützung.
|
||||
|
||||
## Schutz gegen versehentliches Überschreiben (env:versapad)
|
||||
|
||||
`env:versapad` (SWD, `boards/versapad_nobl.json`) schreibt die App-Firmware
|
||||
ab `0x0000` und überschreibt damit einen installierten Bootloader
|
||||
kommentarlos — genau das ist am 2026-08-05 während der Entwicklung passiert
|
||||
(zweimal). `upload_openocd.py` prüft das seither vor jedem `versapad`-Upload:
|
||||
|
||||
- vergleicht per `verify_image` gegen die lokal gebaute
|
||||
`bootloader/.pio/build/versapad_bootloader/firmware.bin`
|
||||
- Bootloader erkannt → Upload wird verweigert, mit Hinweis auf
|
||||
`versapad_usb` oder den expliziten Override
|
||||
- lässt sich die Prüfung nicht eindeutig durchführen (z. B. `bootloader/`
|
||||
noch nicht gebaut, oder die SWD-Verbindung antwortet nicht) → wird
|
||||
sicherheitshalber ebenfalls verweigert, nicht durchgelassen
|
||||
- gilt nur für `env:versapad` — `env:versapad_bootloader` nutzt dasselbe
|
||||
Skript (`extra_scripts = ../upload_openocd.py`) und schreibt bewusst immer
|
||||
auf `0x0000`, ungeprüft
|
||||
|
||||
Bewusstes Überschreiben (zurück zu reinem SWD-Betrieb ohne Bootloader):
|
||||
|
||||
```bash
|
||||
pio run -e versapad -t erase-bootloader-and-flash
|
||||
```
|
||||
|
||||
## Herkunft und Details
|
||||
|
||||
Der Bootloader ist abgeleitet und stark eingekürzt aus
|
||||
|
|
@ -87,6 +111,12 @@ stehen in `bootloader/README.md` unter "Hardwaretest"; kurz zusammengefasst:
|
|||
immer `.bin` mit expliziter Adresse und getrennte OpenOCD-Aufrufe
|
||||
verwenden — Details in `bootloader/README.md`, "Achtung bei manuellem
|
||||
SWD-Flashen".
|
||||
- **`env:versapad` überschreibt den Bootloader kommentarlos.** Ein normaler
|
||||
`pio run -e versapad --target upload` (der alte, gewohnte SWD-Weg für
|
||||
App-Updates) schreibt ab `0x0000` und hat den Bootloader dabei zweimal
|
||||
ohne jede Warnung zerstört. Fix: automatischer Presence-Check in
|
||||
`upload_openocd.py`, siehe "Schutz gegen versehentliches Überschreiben"
|
||||
oben.
|
||||
|
||||
## Bekannte Einschränkungen
|
||||
|
||||
|
|
|
|||
|
|
@ -2,23 +2,118 @@ Import("env")
|
|||
import os
|
||||
import subprocess
|
||||
|
||||
def upload_via_openocd(source, target, env):
|
||||
pkg_dir = env.PioPlatform().get_package_dir("tool-openocd")
|
||||
openocd = os.path.join(pkg_dir, "bin", "openocd.exe")
|
||||
scripts = os.path.join(pkg_dir, "scripts")
|
||||
firmware = str(source[0]) # .elf path
|
||||
# Real, already-built bootloader image used as the reference for the presence
|
||||
# check below -- a raw synthetic probe blob turned out unreliable with
|
||||
# verify_image (silent no-op on tiny files), whereas verify_image against a
|
||||
# real firmware .bin has been solid throughout this project's bring-up.
|
||||
BOOTLOADER_BIN = os.path.join(
|
||||
"bootloader", ".pio", "build", "versapad_bootloader", "firmware.bin"
|
||||
)
|
||||
|
||||
|
||||
def _openocd_paths(env):
|
||||
pkg_dir = env.PioPlatform().get_package_dir("tool-openocd")
|
||||
return (
|
||||
os.path.join(pkg_dir, "bin", "openocd.exe"),
|
||||
os.path.join(pkg_dir, "scripts"),
|
||||
)
|
||||
|
||||
|
||||
def _run_openocd(env, extra_cmd, capture=False):
|
||||
openocd, scripts = _openocd_paths(env)
|
||||
cmd = [
|
||||
openocd,
|
||||
"-s", scripts,
|
||||
"-f", "interface/cmsis-dap.cfg",
|
||||
"-f", "target/at91samdXX.cfg",
|
||||
"-c", 'program "{}" verify reset; shutdown'.format(firmware.replace("\\", "/"))
|
||||
"-c", extra_cmd,
|
||||
]
|
||||
|
||||
print(" ".join(cmd))
|
||||
result = subprocess.run(cmd)
|
||||
if capture:
|
||||
return subprocess.run(cmd, capture_output=True, text=True)
|
||||
return subprocess.run(cmd)
|
||||
|
||||
|
||||
def _bootloader_present(env):
|
||||
bootloader_bin = env.subst(
|
||||
os.path.join("$PROJECT_DIR", BOOTLOADER_BIN)
|
||||
)
|
||||
if not os.path.isfile(bootloader_bin):
|
||||
print("WARNING: {} not found (build it with".format(BOOTLOADER_BIN))
|
||||
print("'cd bootloader && pio run -e versapad_bootloader') -- can't check")
|
||||
print("for an installed bootloader, refusing to flash as a precaution.")
|
||||
print("Use 'pio run -e versapad -t erase-bootloader-and-flash' to override.")
|
||||
return True
|
||||
|
||||
result = _run_openocd(
|
||||
env,
|
||||
'init; reset halt; verify_image "{}" 0x0; shutdown'.format(
|
||||
bootloader_bin.replace("\\", "/")
|
||||
),
|
||||
capture=True,
|
||||
)
|
||||
output = result.stdout + result.stderr
|
||||
if "checksum mismatch" in output or "diff " in output:
|
||||
return False # something else is at 0x0000 -- not this bootloader
|
||||
if "halted due to debug-request" in output and "Error" not in output:
|
||||
return True # verify_image is silent on a match; absence of a
|
||||
# mismatch/error after a successful connect means it matched
|
||||
print("WARNING: could not read flash to check for an installed bootloader")
|
||||
print("(inconclusive) -- refusing to flash as a precaution.")
|
||||
print("Use 'pio run -e versapad -t erase-bootloader-and-flash' to override.")
|
||||
return True
|
||||
|
||||
|
||||
def _flash(env, firmware):
|
||||
result = _run_openocd(
|
||||
env, 'program "{}" verify reset; shutdown'.format(firmware.replace("\\", "/"))
|
||||
)
|
||||
if result.returncode != 0:
|
||||
env.Exit(1)
|
||||
|
||||
|
||||
def upload_via_openocd(source, target, env):
|
||||
firmware = str(source[0]) # .elf path
|
||||
|
||||
# This script is shared with bootloader/platformio.ini's own upload
|
||||
# (env:versapad_bootloader), which legitimately writes 0x0000 every time
|
||||
# -- the guard below only makes sense for the app-without-bootloader
|
||||
# target (env:versapad).
|
||||
if env["PIOENV"] == "versapad" and _bootloader_present(env):
|
||||
print("=" * 78)
|
||||
print("REFUSING TO FLASH: a UF2 bootloader looks like it's installed at 0x0000.")
|
||||
print("")
|
||||
print("This target (env:versapad) writes the app starting at 0x0000 and")
|
||||
print("would silently overwrite it -- the board would lose its USB flashing")
|
||||
print("path (see doc/10_usb_bootloader.md).")
|
||||
print("")
|
||||
print("Use instead:")
|
||||
print(" pio run -e versapad_usb --target upload # flash over USB, keeps the bootloader")
|
||||
print("or, if you deliberately want to erase the bootloader and go back to")
|
||||
print("standalone SWD-only operation:")
|
||||
print(" pio run -e versapad -t erase-bootloader-and-flash")
|
||||
print("=" * 78)
|
||||
env.Exit(1)
|
||||
|
||||
_flash(env, firmware)
|
||||
|
||||
|
||||
env.Replace(UPLOADCMD=upload_via_openocd)
|
||||
|
||||
|
||||
def erase_bootloader_and_flash(*_args, **_kwargs):
|
||||
firmware = env.subst(os.path.join("$BUILD_DIR", "${PROGNAME}.elf"))
|
||||
print("Overwriting 0x0000..0x1FAFF -- any installed UF2 bootloader will be erased.")
|
||||
_flash(env, firmware)
|
||||
|
||||
|
||||
env.AddCustomTarget(
|
||||
name="erase-bootloader-and-flash",
|
||||
dependencies=["buildprog"],
|
||||
actions=[erase_bootloader_and_flash],
|
||||
title="Erase bootloader + flash (standalone SWD)",
|
||||
description=(
|
||||
"Unconditionally overwrites 0x0000..0x1FAFF, wiping any installed UF2 "
|
||||
"bootloader. Use only to return to standalone SWD-only operation."
|
||||
),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue