Document Si4684 blob procurement and enforce CI policy.

Expand local-only firmware README, add check_si4684_blobs.py to verify gitignore and clean history, and gate main with a new si4684-blobs workflow job.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-07 07:38:04 +02:00
co-authored by Cursor
parent 8d826b4d73
commit 9b46fe9465
7 changed files with 201 additions and 43 deletions
+12
View File
@@ -44,3 +44,15 @@ jobs:
- uses: actions/checkout@v4
- name: Check manual class sync
run: python3 tools/check-manual-sync.py --src components --manual docs/manual
si4684-blobs:
runs-on: ubuntu-24.04
defaults:
run:
working-directory: Software
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check Si4684 blob policy
run: python3 tools/check_si4684_blobs.py
+1 -1
View File
@@ -91,7 +91,7 @@ Vertical slices landed on `main` (newest first):
| **0.50.6** | ADAU1701 runtime EQ/mixer, Si4684 tuning & DAB service list, BT1035 driver |
| **0.30.4** | Secure store, Wi-Fi provisioning, companion-chip boot, walking skeleton |
**Next up** ([`Software/docs/TODO.md`](Software/docs/TODO.md)): Si4684 blob procurement docs (T7), NVS/flash encryption (T8).
**Next up** ([`Software/docs/TODO.md`](Software/docs/TODO.md)): NVS/flash encryption (T8).
---
+10 -4
View File
@@ -9,10 +9,16 @@ Binary and SigmaStudio exports loaded by the ESP32 at every boot.
## Si4684 blobs
`rom_patch_016.bin` and `dab_firmware.bin` are extracted from the
[PE5PVB SI4684-DAB-Receiver](https://github.com/PE5PVB/SI4684-DAB-Receiver)
project. `fm_firmware.bin` comes from the Skyworks Si4684 eval firmware pack
(see `tools/fetch_si4684_firmware.py --si46xx-dir`).
Proprietary Skyworks images — **local only**, gitignored (`*.bin`). See
[`Si4684-Firmware/README.md`](Si4684-Firmware/README.md) for procurement and
legal notes. Populate with:
```bash
cd Software
python3 tools/fetch_si4684_firmware.py --dab-only
python3 tools/fetch_si4684_firmware.py --si46xx-dir /path/to/si46xx_firmware
python3 tools/check_si4684_blobs.py
```
## ADAU1701 export
+72 -26
View File
@@ -1,43 +1,89 @@
# Si4684 firmware images
# Si4684 firmware images (local only — not in git)
Skyworks Si4684 application firmware is **proprietary**. This directory holds
the three binary blobs your build embeds via `HOST_LOAD`; they must **never**
be committed to the public repository.
| File | Size (typ.) | Role |
|------|-------------|------|
| `rom_patch_016.bin` | 5796 | ROM patch / bootloader helper (HOST_LOAD before main image) |
| `rom_patch_016.bin` | 5796 B | ROM patch / bootloader helper (HOST_LOAD before main image) |
| `dab_firmware.bin` | ~517 KB | DAB+ application image (PE5PVB / Skyworks BIF) |
| `fm_firmware.bin` | ~530 KB | FM application image (Skyworks `fm_radio_5_1_0.bin`, AN649 A10) |
Boot sequence: AN649 POWER_UP → LOAD_INIT → HOST_LOAD(patch) → LOAD_INIT →
HOST_LOAD(image) → BOOT. DAB and FM use the same patch; only the application
Boot sequence (AN649): POWER_UP → LOAD_INIT → HOST_LOAD(patch) → LOAD_INIT →
HOST_LOAD(image) → BOOT. DAB and FM share the same patch; only the application
image differs.
## Refresh blobs
## Legal / redistribution
- **Do not** commit `*.bin` files here, attach them to GitHub releases, or
redistribute them with this open-source tree.
- DAB images extracted from [PE5PVB/SI4684-DAB-Receiver](https://github.com/PE5PVB/SI4684-DAB-Receiver) are community-sourced; FM images come from the
Skyworks evaluation package or equivalent local sources (see below).
- Keep blobs on your machine only. CI verifies they are gitignored and absent
from history (`python3 tools/check_si4684_blobs.py`).
## Before you build
From `Software/`:
```bash
# DAB + patch from PE5PVB GitHub (always safe to re-run):
python3 tools/fetch_si4684_firmware.py --dab-only
# FM from Skyworks eval / dabpi si46xx_firmware folder:
python3 tools/fetch_si4684_firmware.py --si46xx-dir /path/to/si46xx_firmware
# FM extracted from uGreen DABBoard radio_cli (Files_v16.zip from ugreen.eu/downloads):
python3 tools/fetch_si4684_firmware.py --from-ugreen-radio-cli ~/Downloads/Files_v16.zip
# Patch + DAB + FM entirely from uGreen radio_cli (overrides PE5PVB DAB):
python3 tools/fetch_si4684_firmware.py --from-ugreen-radio-cli ~/Downloads/Files_v16.zip --ugreen-all
# FM extracted from a full SPI flash dump (TechniSat layout, dirb.me tech wiki):
python3 tools/fetch_si4684_firmware.py --flash-dump technisat_spi.bin
python3 tools/fetch_si4684_firmware.py --dab-only # patch + DAB
python3 tools/fetch_si4684_firmware.py --si46xx-dir /path # add FM (see below)
ls -la Firmware/Si4684-Firmware/*.bin # expect 3 files
```
FM images are **not** redistributed with PE5PVB (DAB-only project). Obtain
`fm_radio_5_1_0.bin` from the Si4684 evaluation package (AN649 table 1),
community `si46xx_firmware/` folders used by [teknoid/dabpi](https://github.com/teknoid/dabpi),
or extract from a uGreen [DABBoard](https://ugreen.eu/downloads/) `radio_cli`
binary (proprietary — keep local, do not commit to public git).
`idf.py build` fails at link/embed time if blobs are missing.
Low-level conversion helper:
## Obtain images locally
### Patch + DAB (automated, network)
```bash
python3 tools/extract_si4684_blob.py --copy /path/to/fm_radio_5_1_0.bin \\
cd Software
python3 tools/fetch_si4684_firmware.py --dab-only
```
Clones PE5PVB/SI4684-DAB-Receiver and writes `rom_patch_016.bin` and
`dab_firmware.bin`.
### FM image (manual source — pick one)
FM is **not** shipped with PE5PVB. Supply one of:
| Source | Command |
|--------|---------|
| Skyworks eval / dabpi `si46xx_firmware/` | `python3 tools/fetch_si4684_firmware.py --si46xx-dir /path/to/si46xx_firmware` |
| uGreen DABBoard `Files_v16.zip` | `python3 tools/fetch_si4684_firmware.py --from-ugreen-radio-cli ~/Downloads/Files_v16.zip` |
| Full SPI flash dump (TechniSat layout) | `python3 tools/fetch_si4684_firmware.py --flash-dump technisat_spi.bin` |
Candidate FM filenames (AN649 / community): `fm_radio_5_1_0.bin`,
`fm_radio_5_0_9.bin`, `fmhd_radio_5_1_0.bin`, `fmhd_radio_5_0_4.bin`.
Manual copy helper:
```bash
python3 tools/extract_si4684_blob.py --copy /path/to/fm_radio_5_1_0.bin \
--out Firmware/Si4684-Firmware/fm_firmware.bin
```
### All three from uGreen radio_cli
```bash
python3 tools/fetch_si4684_firmware.py --from-ugreen-radio-cli ~/Downloads/Files_v16.zip --ugreen-all
```
## Verify your checkout
```bash
cd Software
python3 tools/check_si4684_blobs.py # must exit 0
git ls-files 'Firmware/Si4684-Firmware/*.bin' # must print nothing
```
## References
- Skyworks **AN649** — Si4684 programming API and boot flow
- `tools/fetch_si4684_firmware.py` — fetch/extract orchestration
- `tools/extract_si4684_blob.py` — header/array → raw `.bin`
- `docs/manual/ch-si4684.tex` — driver boot integration in DigiRadio
+1
View File
@@ -31,6 +31,7 @@ Documentation gates (must exit 0 before merging; also enforced in CI):
```bash
doxygen Doxyfile
python3 tools/check-manual-sync.py
python3 tools/check_si4684_blobs.py
python3 tools/gzip-www.sh # after editing components/net/www/index.html
```
+4 -12
View File
@@ -69,18 +69,10 @@ embedded gzip blob. No debug routes in `SetupWebServer`.
## P3 — Procurement & hardening
### T7. Si4684 firmware blob strategy (legal)
**Why:** the Si4684 images are proprietary (Skyworks). Tools to fetch/
extract exist under `tools/`, but the `.bin` images must **not** be
committed to the public repo.
**What:**
- Confirm `*.bin` (patch, FM, DAB images) are in `.gitignore` and absent
from git history.
- Document in `Software/Firmware/Si4684-Firmware/README.md` how a builder
obtains the images locally (tools + AN649 reference), without
redistributing proprietary binaries.
**Done when:** no proprietary blob is tracked; the procurement path is
documented and reproducible.
### T7. Si4684 firmware blob strategy (legal) — **DONE (fw 0.8.2)**
`Firmware/Si4684-Firmware/*.bin` gitignored; no blobs in git history.
Procurement documented in `Si4684-Firmware/README.md`; CI job
`si4684-blobs` runs `tools/check_si4684_blobs.py`.
### T8. Flash/NVS encryption enablement
**Why:** secure storage holds Wi-Fi and user credentials; encryption at
+101
View File
@@ -0,0 +1,101 @@
#!/usr/bin/env python3
"""check_si4684_blobs.py — CI gate: no proprietary Si4684 blobs in git.
DigiRadio firmware — https://github.com/manvalan/DigiRadio
Copyright 2026 Michele Bigi
SPDX-License-Identifier: Apache-2.0
Skyworks / uGreen Si4684 application images must live only on the builder's
machine under Firmware/Si4684-Firmware/*.bin (gitignored). This script fails
if any such blob is tracked, appears in git history, or is missing from
.gitignore.
"""
from __future__ import annotations
import argparse
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
FW_DIR = ROOT / "Firmware" / "Si4684-Firmware"
GITIGNORE = ROOT / ".gitignore"
IGNORE_LINE = "Firmware/Si4684-Firmware/*.bin"
def git(*args: str) -> str:
result = subprocess.run(
["git", *args],
cwd=ROOT.parent,
check=True,
capture_output=True,
text=True,
)
return result.stdout
def check_gitignore() -> list[str]:
errors: list[str] = []
if not GITIGNORE.is_file():
errors.append(f"missing {GITIGNORE.relative_to(ROOT)}")
return errors
text = GITIGNORE.read_text(encoding="utf-8")
if IGNORE_LINE not in text:
errors.append(
f".gitignore must contain {IGNORE_LINE!r} "
"(proprietary Si4684 blobs)"
)
return errors
def check_tracked_bins() -> list[str]:
out = git("ls-files", "--", "Software/Firmware/Si4684-Firmware/*.bin")
tracked = [line for line in out.splitlines() if line.strip()]
if not tracked:
return []
return [
"tracked Si4684 blob(s) in git index (remove and keep local only): "
+ ", ".join(tracked)
]
def check_history() -> list[str]:
out = git(
"log",
"--all",
"--oneline",
"--",
"Software/Firmware/Si4684-Firmware/*.bin",
)
lines = [line for line in out.splitlines() if line.strip()]
if not lines:
return []
return [
"Si4684 .bin file(s) found in git history — "
"history rewrite required before publishing: "
+ "; ".join(lines[:5])
+ ("" if len(lines) > 5 else "")
]
def main() -> int:
parser = argparse.ArgumentParser(
description="Ensure Si4684 firmware blobs are not in git."
)
parser.parse_args()
errors = check_gitignore() + check_tracked_bins() + check_history()
if errors:
for err in errors:
print(f"check_si4684_blobs: error: {err}", file=sys.stderr)
return 1
print(
"Si4684 blob policy OK: "
f"{IGNORE_LINE} gitignored, no tracked blobs, clean history."
)
return 0
if __name__ == "__main__":
sys.exit(main())