# AGENTS.md ## Hardware - **Board**: STM32H7S78-DK evaluation board - **MCU**: STM32H7S78 (Cortex-M7 @ 480MHz) - **Flash**: 64 KB internal flash at 0x08000000 (Boot) + 128 MB external OctoSPI NOR flash at 0x70000000 (Appli code + TouchGFX assets) - **RAM**: 1 MB internal + external PSRAM (for Audio/Framebuffers) ## Project Architecture ### Dual-Boot Structure This project uses a **dual-project architecture**: | Project | Directory | Purpose | |---------|-----------|---------| | **Boot** | `/Boot/` | Bootloader - initializes hardware, loads application from external flash | | **Appli** | `/Appli/` | Main application - TouchGFX UI + audio processing | **Flashing order**: Boot must be flashed first (without loader), then Appli (with external loader). After flashing Appli, press NRST button to start. **Note**: The Appli uses external memory (0x70000000) for TouchGFX assets and requires the external FlashLoader `MX66UW1G45G_STM32H7S78-DK.stldr` to program the external flash. ## Project Setup - **IDE**: STM32CubeIDE with STM32CubeMX plugin - **RTOS**: FreeRTOS V10.5.1 (CMSIS-RTOS V2) - **GUI**: TouchGFX 4.26.1 with NemaGFX GPU acceleration - **Language**: C/C++ - **Build System**: GCC Makefile (not CMake) ## Key Constraints 1. **Dual-boot flashing sequence**: Flash Boot first, then Appli (`gcc/flash.sh all` does both and hard-resets the board). 2. **External FlashLoader required**: Use `gcc/MX66UW1G45G_STM32H7S78-DK.stldr` for programming external flash (0x70000000) 3. **Never edit `Core/Src/` and `Core/Inc/` directly** - regenerate via STM32CubeMX instead 4. **High-speed audio acquisition (48kHz)** - uses DMA for ADF1 (microphone) and audio codec 5. **External memory usage**: Audio buffers (>900KB) stored in external PSRAM (section `AudioLooperBuffer`) 6. **TouchGFX architecture**: Two-stage debug - debug Boot first, then Appli 7. **FreeRTOS heap size**: Configured to 50KB in `FreeRTOSConfig.h` (heap_4) 8. **Interrupt priorities**: Audio interrupts at priority 2 (high priority), touch/SD at 14-15 9. **Debug probe connection**: always `ap=1 mode=UR` (STM32_Programmer_CLI) / `-m 1 -k` (ST-LINK_gdbserver). Without them the connection fails (`DEV_TARGET_NOT_HALTED`, `Cannot connect to access port 1`) 10. **No OpenOCD**: the installed OpenOCD 0.12 has no STM32H7RS target; debug with ST-LINK_gdbserver (Cortex-Debug in VS Code) ## Build & Flash ### Toolchain (macOS, verified) | Tool | Path | |------|------| | `arm-none-eabi-gcc` / `-gdb` 10.3 | `/usr/local/bin` (in PATH) | | STM32_Programmer_CLI | `/Applications/STMicroelectronics/programmer/STM32CubeProgrammer.app/Contents/MacOs/bin/STM32_Programmer_CLI` (not in PATH; `gcc/flash.sh` finds it, override with `$STM32_PROGRAMMER_CLI`) | | ST-LINK_gdbserver | `/Applications/STMicroelectronics/STM32CubeIDE.app/Contents/Eclipse/plugins/com.st.stm32cube.ide.mcu.externaltools.stlink-gdb-server.macos64_*/tools/bin/ST-LINK_gdbserver` | | SVD | `/Applications/STMicroelectronics/programmer/STM32CubeProgrammer.app/Contents/MacOs/SVD/STM32H7S.svd` | | Probe | on-board STLINK-V3 (USB) | TouchGFX 4.26.1 + STM32CubeMX are only needed to regenerate code. On Linux, `arm-none-eabi-gcc` comes from STM32CubeIDE (`~/st/stm32cubeide_*/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.*/tools/bin`, GCC 14.3); `~/.bashrc` adds it to `PATH`. The build also needs `ruby` (TouchGFX text/video conversion) and the executable bit on `Appli/Middlewares/ST/touchgfx/framework/tools/{imageconvert,fontconvert}/build/linux/*.out`. Build outputs (`*.o`, `*.d`, `*.su` in `*/TouchGFX/build` and `STM32CubeIDE/*/Debug`) are git-ignored and untracked. ### Build Commands Run from the repository root (the makefiles `cd` to the root themselves): ```bash make -C gcc -j8 # Build Boot + Appli make -C gcc -j8 -f makefile_boot all # Boot only make -C gcc -j8 -f makefile_appli all # Appli only make -C gcc clean # Clean python3 gcc/gen_compile_commands.py # Regenerate compile_commands.json (IntelliSense/clangd) after adding sources ``` Outputs: - Boot: `Boot/TouchGFX/build/bin/target.elf|.hex` (internal flash 0x08000000) - Appli: `Appli/TouchGFX/build/bin/target.elf|.hex` (external flash 0x70000000) The "old Ruby version (2.6.10)" TouchGFX warning during the build is harmless. ### Flashing Commands Use `gcc/flash.sh` (wraps STM32_Programmer_CLI with `ap=1 mode=UR` and the external loader): ```bash gcc/flash.sh boot # Boot -> internal flash (with verify) gcc/flash.sh appli # Appli -> external flash via gcc/MX66UW1G45G_STM32H7S78-DK.stldr (~20 s) gcc/flash.sh all # Boot then Appli (required order), then hard reset gcc/flash.sh probe # Check the ST-Link/board connection (no write) gcc/flash.sh reset # Hardware reset gcc/flash.sh verify # Dump 4 KB of 0x08000000 / 0x70000000 to /tmp/read_int.bin, /tmp/read_ext.bin make -C gcc flash # Build + flash both (calls gcc/flash.sh on macOS/Linux) ``` Equivalent raw commands: ```bash STM32_Programmer_CLI -c port=SWD ap=1 mode=UR -d Boot/TouchGFX/build/bin/target.hex -v -hardRst STM32_Programmer_CLI -c port=SWD ap=1 mode=UR -el gcc/MX66UW1G45G_STM32H7S78-DK.stldr \ -d Appli/TouchGFX/build/bin/target.hex -hardRst ``` Only one tool can hold the ST-Link at a time: stop any debug session before flashing. ### VS Code (`.vscode/`, git-ignored) Linux setup (uses the `ms-vscode.cpptools` extension, no Cortex-Debug needed; tools come from STM32CubeIDE in `~/st`): - `gcc/toolchain-env.sh`: sourced by the helpers below; adds `arm-none-eabi-gcc/gdb` to `PATH` and exports `ST_GDBSERVER` / `ST_PROGRAMMER_BIN` (globs, so CubeIDE updates do not break it). - `gcc/gdbserver.sh `: starts `ST-LINK_gdbserver -p 61234 --swd --halt -m 1 -k -cp ` (+ `-el` external loader for `appli`). Kills a stale server first. - `gcc/arm-gdb.sh`: wrapper around `arm-none-eabi-gdb` (VS Code `miDebuggerPath` cannot glob). - `tasks.json`: Build All (default, Ctrl+Shift+B) / Boot / Appli, Clean, Flash Boot/Appli/All, Build + Flash All, Board: Probe/Reset/Verify, `gdbserver: Boot|Appli` (background), `Debug prep: ...` (build + gdbserver, used as `preLaunchTask`), Kill gdbserver, Generate compile_commands.json. - `launch.json` (`cppdbg`, `miDebuggerServerAddress: localhost:61234`): **Debug Boot** (build, `load`, `monitor reset`, hw break on `main`), **Debug Appli** (build, `load` via the external loader, reset, stops at Appli `main()`), **Debug Appli (no flash)** (reset only). - `launch.json` also has three `stlinkgdbtarget` configs (they need `deviceName: STM32H7S7L8H6H` = CMSIS pack variant / `Mcu.CPN`, and `deviceCore: Cortex-M7`; the ST extension cannot infer them for this Makefile project) (**ST Debug Boot / Appli / Appli (no flash)**) for the STMicroelectronics VS Code extensions (`stm32cube-ide-debug-stlink-gdbserver`, `-registers`, `-rtos`): they start their own ST-LINK server from `~/.local/share/stm32cube/bundles` (no `gdbserver:` task needed), and give the peripheral **Registers** view (SVD `~/STMicroelectronics/STM32Cube/STM32CubeProgrammer/SVD/STM32H7S.svd`), the **FreeRTOS** threads view (`serverRtos`, `ARM_CM7`) and Live Watch. Validated headless (DAP driven by script: server, RTOS proxy, GDB client and Live Watch all start; flash `load` not exercised). **Required, git-ignored file** `.settings/ide.store.json` at the repo root: `{"device": "STM32H7S7L8H6H", "core": "Cortex-M7", "tz": "TZ-disabled"}`. The extension looks for it by walking up from the ELF directory; without it the ST-LINK server step is silently skipped and launch fails with "No GDB Server running" ("project settings not found" in the log). The ST configs need `"preRunCommands": ["monitor reset"]`: after `load` the extension starts at the ELF `Reset_Handler`, but external flash is only memory-mapped once Boot has run, so without the reset Appli faults on its first instruction (SIGTRAP in Boot's handler, black screen). Other notes: the extension's CMSIS pack root `~/.local/share/stm32cube/packs` should contain `STMicroelectronics/stm32h7xx_dfp/1.3.0`; the `ST Debug Appli (no flash)` variant only has a symbol file, so it sets `program` (the RTOS proxy needs the ELF; without it the proxy dies and GDB times out). - `settings.json` / `extensions.json`: IntelliSense from `compile_commands.json`. macOS keeps the Cortex-Debug (`servertype: stlink`) setup described under "Debugging". `pkill -x ST-LINK_gdbserver` does NOT work (process name truncated to 15 chars): use `pkill -f '[S]T-LINK_gdbserver'`. Keep `AGENTS.md`, `.vscode/tasks.json`, `.vscode/launch.json` and `gcc/flash.sh` consistent when changing any build/flash/debug command. ### Debugging Boot must already be in internal flash (`gcc/flash.sh boot`): at reset the core runs Boot, which maps the XSPI memories and jumps to Appli `main()` at 0x70000000. VS Code: run "Debug Appli" (F5). No NRST press needed. Command line: ```bash GDBSRV=$(ls -d /Applications/STMicroelectronics/STM32CubeIDE.app/Contents/Eclipse/plugins/com.st.stm32cube.ide.mcu.externaltools.stlink-gdb-server.macos64_*/tools/bin | tail -1)/ST-LINK_gdbserver "$GDBSRV" -p 61234 --swd --halt -m 1 -k \ -cp /Applications/STMicroelectronics/programmer/STM32CubeProgrammer.app/Contents/MacOs/bin \ -el gcc/MX66UW1G45G_STM32H7S78-DK.stldr # In another terminal arm-none-eabi-gdb Appli/TouchGFX/build/bin/target.elf (gdb) target extended-remote :61234 (gdb) load # optional: programs 0x70000000 through the external loader (gdb) monitor reset (gdb) break main # hardware breakpoint chosen automatically (0x70000000 is flash in the memory map) (gdb) continue ``` STM32CubeIDE alternative: debug Boot, terminate (Ctrl+F2), debug Appli, Resume (F8), press NRST. ## Sensors/Peripherals ### Audio (Primary Function) - **ADF1** - Digital microphone (PDM interface, 48kHz sample rate) - **I2C1** - Audio codec (STM32 Audio BSP) - **DMA2D/GPDMA1/HPDMA1** - Audio DMA channels ### Display - **LTDC** - LCD-TFT controller - **GPU2D (NemaGFX)** - 2D acceleration - **Display**: 800x480 RGB565 (LTDC) ### Storage & Communication - **XSPI1** - External PSRAM (for audio buffers) - **XSPI2** - External Flash (for TouchGFX assets) - **I2C1** - Touch controller (FT5336), sensors ### Touch Control - **FT5336** - Capacitive touch controller - **SPI** - Touch interface - **GPIO** - Touch interrupt (TP_IRQ) ## Audio Processing ### Effects Chain (in order) 1. **Octaver** - Dual-grain pitch shifting (-2, -1, +1, +2 octaves) 2. **Equalizer** - 10-band graphic EQ (31 Hz - 16 kHz, -24..+24 dB) 3. **Overdrive** - Distortion effect 4. **Saturation** - Audio saturation 5. **Echo** - Delay-based echo 6. **Reverb** - Room reverb ### Analysis - **FFT**: 2048-point real FFT (CMSIS-DSP) - **Sample rate**: 48kHz - **Bins**: 1024 (0-24kHz spectrum) - **Peak detection**: Frequency with maximum magnitude ### Looping - **Duration**: Up to 5 seconds at 48kHz - **Playback**: 2x speed (raises pitch by octave) - **Storage**: External PSRAM (937.5KB buffer) ## TouchGFX Architecture ### Project Structure ``` Appli/ ├── TouchGFX/ │ ├── App/ # Application code (user created) │ ├── assets/ # Images, fonts, videos │ ├── build/ # Generated build files │ ├── config/ # Build configuration │ ├── generated/ # Auto-generated code (DO NOT EDIT) │ ├── gui/ # Custom GUI code (views, presenters) │ ├── simulator/ # Simulator files │ └── target/ # Target-specific code ``` ### GUI Screens - **InitScreen** - Splash screen (title + central button) -> Overview - **Overview** - Home screen: power toggle + master volume, 3x2 grid of effect cards (tap = open the effect screen, switch = enable/bypass), looper (hold to record, plays back x2), Spectrum button - **Octaver, Equalizer, Overdrive, Saturation, Echo, Reverb** - Effect tuning screens (common header: back, title, on/off; sliders with live value) - **Oscillo** - Spectrum analyzer (title "SPECTRE") ### UI Conventions (keep when editing the GUI) - **Never write files in `Appli/TouchGFX/generated/`**: it is regenerated by TouchGFX Designer. Screens, widgets and interactions come from `CelloEffects.touchgfx`. - **Custom look is done in user code only** (`Appli/TouchGFX/gui/`): each `XxxView::setupScreen()` re-lays out the generated widgets and adds extra ones. - `gui/include/gui/common/UiTheme.hpp`: palette, per-effect accent color (`ui::accent()`), `ui::Header`, `ui::ValueText` (formatted value: `show("%d %%", v)`, `showDb(v)`), `ui::layoutEffectHeader()`, `ui::layoutParamRow()` - `gui/include/gui/common/EffectCard.hpp`: home-screen effect card - The home grid lives in `OverviewView`; the generated `buttonWithLabel1..6` of Overview are hidden on purpose. - **A new screen must be created in TouchGFX Designer** (so `FrontendHeapBase` and `FrontendApplicationBase` include it). A screen hand-written outside Designer is not in the screen heap and corrupts memory. - **Texts**: add them to `Appli/TouchGFX/assets/texts/texts.xml` (group `UiRework`); the build regenerates texts and fonts (`T_`). Typographies: `Default` 20 px, `Medium` 28 px, `Large` 40 px. Wildcard texts: `VALUE_M_LEFT` (Medium), `VALUE_S_CENTER` (Default); a character shown in a wildcard must be listed in the typography's `WidgetWildcardCharacters`. - **Canvas widgets** (Circle, Graph...) need a per-screen `CanvasWidgetRenderer::setupBuffer()` (see `OverviewView`, `OscilloViewBase`). ## Memory Map ### Key Sections - **AudioLooperBuffer** - External PSRAM (audio loop recording) - **TouchGFX framebuffers** - External PSRAM (LCD display) - **Heap** - Internal RAM (50KB FreeRTOS heap) - **Stack** - Internal RAM (task stacks) ### External Memory - **PSRAM**: 8MB (XSPI1) - Audio buffers + framebuffers - **Flash**: 128MB (XSPI2) - TouchGFX assets (compressed images/fonts) - **External FlashLoader**: `gcc/MX66UW1G45G_STM32H7S78-DK.stldr` (required for programming external flash via STM32CubeProgrammer) ## Data Logging - **Format**: CSV (to be implemented) - **Storage**: SD card via SPI (not currently implemented in main code) - **FATFS**: File system (available via Middlewares/ST) --- *Last updated: October 2026* *Project: STM32H7S78-DK Audio Effects Processor with TouchGFX* **Last fixed**: October 18, 2026 (VS Code/CLI build-flash-debug verified on board: `gcc/flash.sh`, Cortex-Debug + ST-LINK_gdbserver) ## Flashing Troubleshooting ### Common Issues 1. **`DEV_TARGET_NOT_HALTED` / `Cannot connect to access port 1` / gdbserver `Target unknown error 32`** - Cause: missing access port or connect-under-reset option - Solution: `ap=1 mode=UR` (CLI) or `-m 1 -k` (ST-LINK_gdbserver), as used by `gcc/flash.sh` and `.vscode/launch.json` 2. **"Upload size is bigger than flash memory size"** - Cause: reading beyond the 64 KB internal flash (0x08000000-0x08010000) - Solution: use a smaller read size 3. **External memory (0x70000000) not accessible** - Cause: External FlashLoader not loaded - Solution: add `-el gcc/MX66UW1G45G_STM32H7S78-DK.stldr` (CLI and gdbserver) 4. **Application runs but display is blank** - Cause: Appli not flashed in external memory correctly, or Boot missing - Solution: `gcc/flash.sh all`, then `gcc/flash.sh verify` 5. **ST-Link busy** - Cause: a debug session or STM32CubeProgrammer GUI holds the probe - Solution: stop it, then retry 6. **`nul` files appear in the repo** - Cause: Windows `2> nul` redirections in `gcc/include/cube_programmer.mk` (now guarded with `ifeq ($(OS),Windows_NT)`)