Imported from xentron-bit/stm32-embedded-skill (
SKILL.md). Install upstream withnpx skills add xentron-bit/stm32-embedded-skill. Copyright stays with the author.
name: stm32-embedded-dev description: Use when developing, reviewing, or debugging firmware for STM32 microcontrollers (STM32F0/F1/F2/F3/F4/F7/G0/G4/H5/H7/H7RS/L0/L1/L4/L5/U0/U5/WB/WBA/N6) in bare-metal or RTOS environments (FreeRTOS, Keil RTX5/CMSIS-RTOS2, ThreadX, Zephyr). Trigger for peripheral drivers (I2C/SPI/UART/CAN/FDCAN/DMA/ADC/Timer/HRTIM/QSPI/OCTOSPI/FMC/SDMMC), RTOS task design, flash/RAM optimization, XIP/SWD/JTAG debug, compiler pitfall detection (volatile/LTO/aliasing/cache), or industrial firmware for automotive, factory automation, harsh environments. Also: STM32CubeMX/HAL/LL, Keil MDK, STM32CubeIDE, linker scripts (.ld + scatter), startup code, fault handlers, OTA/IAP/dual-bank bootloaders, secure boot/RDP/PCROP/OTFDEC, TrustZone-M (SAU/GTZC/MPCBB/CMSE), power modes/sleep/LPTIM/RTC wakeup, automotive diagnostics (UDS ISO 14229, J1939, OBD-II SAE J1979, WWH-OBD ISO 27145, DoIP ISO 13400, Modbus RTU), BLE (BlueNRG, BlueNRG-LP/LPS/355), USB device/host (CDC-ACM/HID/MSC + TinyUSB), Ethernet/LwIP, errata cross-check.
STM32 Embedded Development Skill
🌐 GitHub-First Knowledge Strategy (READ THIS FIRST OF ALL)
This skill does not memorize HAL/CMSIS API. Memorization causes drift (wrong macro names, wrong signatures, wrong field order). Instead:
| Knowledge layer | Where to fetch from | Don't memorize, don't trust ref-md |
|---|---|---|
| L0 — Library & VERSION (do this FIRST) | Project: .uvprojx/.cproject/*.csolution.yml (pack/DFP + STM32Cube_FW_* ver), *_hal_conf.h, HAL Release_Notes/@version, Middlewares/*/Release_Notes, RTX RTX_Config.h |
✓ APIs are version-specific — detect the exact HAL/RTOS version before any API advice (CLAUDE.md §"Phase 0" + "Fact-Based / No-Guess") |
| L1 — HAL / peripheral / startup / linker | gh search code '<symbol>' --owner=STMicroelectronics --extension=c (pin to the detected pack/tag) |
✓ Always verify HAL names via gh; never assert a symbol not confirmed in the detected version |
| L2 — RTOS (RTX5 + FreeRTOS) | gh search code '<symbol>' --owner=ARM-software (RTX5) / --owner=FreeRTOS / --owner=STMicroelectronics (ST's port) |
✓ Always verify RTOS API |
| L3 — Protocols (J1939/UDS/OBD/DoIP/Modbus) | Spec PDFs (ISO/SAE, paid). Ref-md is the trusted in-repo source. | Only place ref-md is authoritative |
| L4 — Design / strategy / errata | ref-md only. Task ladders, ISR patterns, stack sizing, errata workarounds, decision trees. | Only place ref-md is authoritative |
Workflow for any HAL/RTOS code you produce:
1. Need an API name? → grep ref-st-github-map.md §8 "Sık yanılan HAL isimleri" for known-wrong names
2. Not in §9? → gh search code '<name>' --owner=STMicroelectronics --extension=c
3. ≥1 hit → use it (and cite the path in your code comment)
4. 0 hits → the name is wrong; ask user for family or search variants
5. Only fall back to ref-md snippets when offline or for L3/L4 content
See ref-st-github-map.md for the full repo catalog
and gh command recipes.
🆕 Self-Update — Adding a New ST Device / Family (Mode E)
This skill extends itself. When you meet an ST MCU/SoC the skill does not
yet cover — no matching ref-*.md, missing from stm32-families.md,
absent from the errata table in CLAUDE.md — do not answer from
memory. Run this procedure to research it GitHub-first and add a new reference
file + register it in the canonical indexes.
Trigger: "add support for ", " diye bir şey var, ekle", a review/ implement request for a family not in the Reference Files table, or you notice a coverage gap mid-task.
Procedure (do not skip steps; this is itself an authoritative pipeline):
- Scope & identify. Family, concrete order codes + decode, silicon generation/cuts. Check for rebrand/order-code aliases of an existing part — ST often ships the same silicon under multiple order-code families (e.g. BlueNRG-LP = BlueNRG-355). If so, map them explicitly; it drives where the canonical SDK lives.
- GitHub catalog FIRST.
gh repo list STMicroelectronics --limit 1000,gh search repos,gh search code '<symbol>' --owner=STMicroelectronics. Find: the official Cube package, HAL driver repo, CMSIS device repo, BLE/RTOS middleware, example projects. Note the canonical active repo and any community mirrors for legacy/installer-only SDKs (an official "DK" may not be on ST's GitHub — say so). - Authoritative docs. Datasheet (DS), reference manual (RM), programming
manual (PM), errata (ES), security advisories (SA), and the key
application notes (AN). If web fetch is blocked/times out (st.com often does),
ASK THE USER to drop the PDFs in a folder and
Readthem locally — do not guess specs. - Extract verified facts. Memory map (don't assume
0x08000000!), part decode, peripherals, power modes + wakeup sources, every erratum + its workaround, security advisories, toolchain, flashing path. Tag anything you could not confirmUNVERIFIED — needs datasheet check. Cite DS/RM/ES/AN numbers andowner/repo:pathfor code. - Write
ref-<name>.mdmatching the house format:@trust-header v1block, a source-document catalog table,## INDEXwith anchor links, Roman- numeral sections, dense tables, and a closing "Olmaz sa Olmaz" checklist. Cross-link sister refs and do not duplicate generic content (link instead). - Register in ALL canonical indexes (single-source rule):
- SKILL.md §"Reference Files" — add the row in the right subsection.
- SKILL.md frontmatter
description— add trigger keywords so the skill activates for the new part. (Mirror into the deployed skill metadata.) - stm32-families.md — family/catalog row + CMSIS/HAL repo links.
- CLAUDE.md errata table —
family → ES####, plus any SA advisory.
- Verify. Cross-links resolve, errata + security captured,
ghrecipes run, no uncited hard claims. Fix wrong pre-existing rows you find (e.g. stale BLE version, wrong SDK link). - Offer to commit only if the user asks (
gitis used in this repo).
Worked example: the BlueNRG-LP/LPS addition → ref-ble-bluenrg-lp.md (GitHub-first research, user-supplied ST PDFs for errata/RM/AN, registered in all four indexes above). Use it as the template for the next device.
Principles: GitHub-first (cite, never memorize APIs) · authoritative PDFs for DS/ES/SA (ask the user if the web blocks you) · mark UNVERIFIED, never assert uncertain facts · always map rebrands/migrations.
⚡ Quick Start — Operating Modes (READ FIRST)
When this skill is invoked, identify the mode first, then follow the matching procedure.
🌐 Output-language rule (mandatory): Mirror the user's language. Write ALL prose — explanations, findings, reviews, summaries, and generated reports (incl. PDF) — in the language the user is writing to you in (user writes Turkish → reply + reports in Turkish; English → English). Keep code, identifiers, file paths, commit messages, log excerpts, ST/ARM citations, and committed
ref-*.mdcontent as-is — never translate code or symbols. When unsure, match the user's most recent message.
| Mode | Trigger | Procedure | Output shape | Max length |
|---|---|---|---|---|
| A. Quick Q&A | Single-question (≤2 lines), no code attached | Answer directly. No graphify, no checklist. | Plain answer | ≤5 lines |
| B. Review existing code | User pastes/points to a small file or snippet (<200 LOC, single peripheral) | (1) Detect MCU + family (2) Errata cross-check (3) Skip graphify (4) Use Finding Template | Findings list | ≤8 findings inline |
| B+. Deep Project Review | "Analyze my project", "Is there a bug here?", "Review this repo", project directory pointed to | 9-phase reference-benchmarked pipeline below — DO NOT skip any phase | Findings with confidence + reference citations | Until pipeline complete |
| C. Implement new feature | "Write a driver for...", "Add OTA..." | (1) Context interview if ambiguous (2) 5-phase summary (3) Code with errata notes inline | Code + 5-phase summary | ≤30 lines summary + code |
| D. Debug | "Why does X not work?", "HardFault at ..." | (1) Ask for fault dump / debug info (2) Decode CFSR/BFAR (3) Walk root cause | Q&A + decoded fields | Until root cause found |
| E. Self-Update | New/uncovered ST part ("add support for…", coverage gap) | Self-Update pipeline (§🆕 above): GitHub-first research → write ref-*.md → register in all indexes |
New reference file + index edits | Until reference written + registered |
Mandatory Finding Template (Mode B / B+ / D)
Every issue you flag MUST use this exact shape — never prose paragraphs:
[SEVERITY] [CONFIDENCE] file.c:LINE — CATEGORY
what: one sentence describing the bug
why: one sentence explaining failure mode (what breaks at runtime / on which silicon)
fix: one sentence — concrete change, with patch hint if obvious
ref: canonical source (ST repo path / RM section / spec / AN)
errata: [ES0480 §2.3.1] if applicable, else omit
Severity codes:
CRITICAL— data loss / silent corruption / compile fail / safety-of-lifeHIGH— functional bug, reproducibleMEDIUM— latent / maintainabilityLOW— style
Confidence codes (from B1 — multi-source verification):
HIGH-CONF— verified against ≥2 authoritative sources (ST repo + AN, or RM + ARM ARM, or errata + reproduction)MED-CONF— single authoritative sourceLOW-CONF— heuristic / pattern-based, not directly verified
Rule: A CRITICAL finding with LOW-CONF must be marked clearly — do not assert it as certain. If only LOW-CONF available for a critical issue, frame as "candidate / requires verification".
Decision Tree on Invocation
User request
│
▼
1. Mode = A (quick Q)? ──► Answer in ≤5 lines. STOP.
│
▼
2. Mode = B+ (project review)? — dizin/repo işaret edildi, ≥3 .c dosyası,
ya da "projemi analiz et / bug var mı / review this repo" (Faz 0 tetikleri)
──► 9-phase Mode B+ pipeline (Faz 0–8). Graphify-First Gate uygulanır. STOP.
│
▼
3. Mode = B (single-file review, <200 LOC)?
│
▼
3a. Detect MCU + silicon rev (ask if unknown)
3b. Open ref-stm32-errata.md, list relevant errata IDs (≤3 lines)
3c. Skip graphify — tek dosya/snippet için graf kurulmaz
(≥3 .c dosyası zaten adım 2'de B+'ya yönlendi)
3d. Apply Code Review Checklist filtered by triage rules (see §Code Review)
3e. Emit findings via template (above)
│
▼
4. Mode = C (implement)?
│
▼
4a. If ambiguous: 1-2 Context Interview questions (see §Context Interview)
4b. Otherwise: code first, 5-phase summary after
│
▼
5. Mode = D (debug)? ──► Walk fault dump / CFSR. Ask for missing data.
│
▼
6. Mode = E (new/uncovered ST part)? ──► Run Self-Update pipeline (§🆕). Research GitHub-first, write ref-*.md, register in all indexes.
Response language
Respond in the user's language. Reference files may mix Turkish/English — translate quoted terms inline if needed. Never code-switch within a single sentence.
🔬 Mode B+ — Deep Reference-Benchmarked Project Review
This is THE pipeline for any "analyze my project" / "is there a bug?" / "review this repo" request. It compares the user's code against ST's canonical reference implementations to find MCU-interface deviations.
Why this exists: Memorized HAL knowledge drifts; manual line-by-line review misses cross-cutting init-order bugs. By generating a call-graph of the user's code and a call-graph of ST's canonical example for the same MCU, then diffing the two on the HAL/LL/BSP/SCB surface, we find divergences from authoritative patterns — those are bug candidates.
Premium accuracy contract: This pipeline does NOT have an MVP / "skip-some-phases" mode. The phases are minimum-viable for a trustworthy bug-hunt output. Phases are tested step-by-step but never dropped.
🔒 Graphify-First Gate (mandatory): For ≥3
.cfile / project analysis, BUILD the graphify code-map FIRST (graphify update <project>→ verify<project>/graphify-out/graph.jsonon disk) BEFORE any findings, file-by-file reading, or subagent review. Do NOT bypass graphify with direct full-file reads or parallel subagents — that discards the call-graph diff and the token saving. Use the output (query-first): readGRAPH_REPORT.mdgod nodes + surprising connections, thengraphify query/affected/path→ walk node →file:line→ verify. See CLAUDE.md §"Graphify-First Gate" + ref-graphify.md.
🔒 Hard Gates — Mandatory Disk-Verified Artifacts
Each phase produces a file on disk. The next phase MUST verify it exists
via Bash before starting. If verification fails, the skill ABORTS with
a clear message — NEVER falls back to memory-based review.
| After phase | Artifact (disk) | Verify command | On miss |
|---|---|---|---|
| Faz 3 | .claude-cache/refs/**/*.c exists |
find .claude-cache/refs -name '*.c' | head -1 | grep -q . |
ABORT |
| Faz 3 | .claude-cache/refs/<repo>/.pinned-sha |
[ -f .claude-cache/refs/*/. pinned-sha ] |
ABORT |
| Faz 4 | .claude-cache/refs/graphify-out/graph.json |
[ -s .claude-cache/refs/graphify-out/graph.json ] |
ABORT |
| Faz 5 | <user-project>/graphify-out/graph.json |
[ -s <user-project>/graphify-out/graph.json ] |
ABORT |
| Faz 8 | <project>/STM32_REVIEW_<YYYY-MM-DD>.md |
[ -f <project>/STM32_REVIEW_*.md ] |
ABORT |
Citation-Mandatory rule (applies from Faz 6 onward):
Every finding line MUST include at: <file>:<line> referencing actual
bytes on disk. Before emitting "X is missing" / "Y is wrong":
grep -rn "<symbol>" <user-project>→ if found elsewhere, finding is CANCELLED (mark "verified-present")graphify query "<symbol>" --graph <user-project>/graphify-out/graph.json→ if defined, finding is CANCELLED- HAL/LL behavior claims MUST cite
.claude-cache/refs/<path>:<line>— never from memory
Findings without ≥1 ST-repo or user-code citation are FORBIDDEN.
USE-THE-REFS forcing rule (applies from Faz 6 onward):
Faz 3'te ST canonical kodu .claude-cache/refs/ altına indirildi. Faz 6'ya
başlamadan ÖNCE, user'ın init ettiği HER peripheral için en az 1 canonical
ST örneği aç ve okuyup karşılaştır. "Memory benzerlik" yerine "side-by-side
diff" zorunlu. Konkret algoritma:
for peripheral in user_peripherals (FDCAN, OCTOSPI, SPI, USART, DMA, ETH, USB, ...):
canonical = find .claude-cache/refs -name 'main.c' -path "*${peripheral}*" | head -3
if canonical empty:
# specific example yok → driver header'ında HAL_Init validation oku
canonical = .claude-cache/refs/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_${peripheral}.c
Read(canonical) # MANDATORY
diff against user's MX_${peripheral}_Init
cite differences with file:line on BOTH sides
Bu adımı atlamak skill'i ezberden çalışmaya düşürür — Faz 3'ün tüm değeri kaybolur.
Fault-Evidence-First rule (Mode D + Mode B+ ile HardFault iddiası) — HARD STOP GATE:
Kullanıcı "HardFault yapıyor" / "crash oluyor" / "ışık yanıyor sonra duruyor" derse, diğer tüm Faz'lardan ÖNCE şu prosedür çalışır:
1. grep -A5 "HardFault_Handler" stm32h7xx_it.c
body sadece while(1)? ──► EMPTY HANDLER
2. EMPTY HANDLER ise:
┌────────────────────────────────────────────────────────┐
│ ❌ STOP. Faz 2-7'YE GEÇME. Aday üretme. │
│ │
│ Çıktı sadece şu olur: │
│ "Fault handler boş — CFSR olmadan kök sebep bulunamaz.│
│ ref-fault-handlers.md §'Canonical Naked Handler' │
│ bölümündeki ARM Apache-2.0 örnek kodu (~20 satır) │
│ stm32h7xx_it.c'ye yapıştır. Debug session'da while(1)│
│ 'e breakpoint koy, fault'a yakalan, şu register │
│ snapshot'ı gönder: CFSR, HFSR, DFSR, BFAR, MMFAR, │
│ stacked PC, LR, R0-R3." │
│ + ref-fault-handlers.md'ye link │
│ + nasıl yakalanır talimatı (Keil µVision Fault Reports│
│ diyalogu / CubeIDE Live Expressions) │
└────────────────────────────────────────────────────────┘
3. CFSR/HFSR/BFAR/MMFAR/PC/LR snapshot geldi ise:
- CFSR decode et (IBUSERR / PRECISERR / IMPRECISERR / UNALIGNED /
DIVBYZERO / UNDEFINSTR / NOCP / INVPC / INVSTATE)
- PC'yi .map dosyasıyla eşleştir (arm-none-eabi-addr2line veya nm)
- BFAR/MMFAR adresini memory map'le eşleştir (FLASH / SRAM /
peripheral / SRAMCAN / OCTOSPI / unmapped)
- SP unwind ile call-chain çıkar
- TEK ROOT CAUSE emit et HIGH-CONF olarak
4. Fault evidence YOKSA ve user "disable X → no fault" gibi
yarı-bisection kanıt verdiyse:
- Bu kanıt + Faz 6 USE-THE-REFS side-by-side diff = MED-CONF
- Birden fazla aday SAYMA — en yakın canonical-divergence olanı seç
- Diğerleri "ALTERNATE HYPOTHESIS" olarak ek satırda kal
Forbidden patterns (bu oturum hataları):
- ❌ "5 candidate olabilir, hepsi LOW-CONF" — aday listesi yasak
- ❌ "FDCAN2 priority 0 olduğu için" + ezberden RTX5 SVC reasoning (ref-fdcan-multi.md citation tek başına yetmez; RTX_Config.h SVC priority ile bizzat doğrulama gerek)
- ❌ User'ın "fdcan2 disable → fix" kanıtını HIGH-CONF üretmek için KULLANIRKEN cite etmemek — her HIGH-CONF en az 2 bağımsız kaynak
- ❌ Empty fault handler tespit ettikten sonra aday saymaya devam etmek
- ❌ "Acaba şu olabilir mi" tarzı tahmin paragrafları
ALTERNATE HYPOTHESIS slot (en fazla 1 satır): "Bu finding doğrulanmazsa ikinci olasılık: [, <CFSR ile ayırt edilir>]" Üçüncü olasılık bile yazma — gürültü yapma.
Pipeline Overview (9 phases = Faz 0–8; Faz 1.5 / 1.5b, Faz 1'in alt-adımlarıdır)
[Faz 0] Mode B+ tetiklendi mi?
↓
[Faz 1] MCU + Toolchain Tespiti (otomatik, .ioc/.uvprojx/startup'tan)
↓
[Faz 1.5] PROJECT PURPOSE UNDERSTANDING ★ (anla — anlamadıysan SOR)
↓
[Faz 2] Errata + AN Context (workaround'ları "expected divergence" listesine al)
↓
[Faz 3] Reference Acquisition (sparse clone — sadece ilgili STM32Cube alt-ağacı)
↓
[Faz 4] Reference Graph (graphify on canonical examples)
↓
[Faz 5] User Graph + Side Checks (paralel: A2 CubeMX-diff, A3 memory-map, A4 concurrency)
↓
[Faz 6] Benchmark + Confidence Scoring
↓
[Faz 7] Divergence Triage (MCU-interface vs business-logic ayrımı)
↓
[Faz 8] Manual Verification + Final Findings (template + citations)
Faz 0 — Mode B+ Tetik Şartları
Aşağıdakilerden HERHANGİ BİRİ → B+:
- Kullanıcı bir dizin/repo işaret etti (tek dosya değil)
- "projemi analiz et", "kodumda sorun var mı", "review this repo"
- ≥3
.cdosyası söz konusu - "yarın production'a giriyor / kritik kod" gibi söz
Tek dosya snippet ya da "şu fonksiyona bak" → Mode B (kısa), B+ değil.
Faz 1 — MCU + Toolchain Tespiti
Precedence rule (file evidence > directory name > user words):
| Kaynak | Güven | Örnek |
|---|---|---|
.ioc Mcu.Name= / Mcu.Family= |
HIGH — kazanır | Mcu.Name=STM32H730VBTx |
*.uvprojx <Device>, *.cproject STM32-target |
HIGH | <Device>STM32G474RETx</Device> |
startup_stm32<part>.s |
HIGH | startup_stm32h730xx.s |
#include "stm32<fam>xx_hal.h" (main.c) |
MED | #include "stm32g4xx_hal.h" |
Dizin adı (my_g4_project/) |
LOW — IGNORE if file evidence says farklı | — |
| Kullanıcının söylediği "g4 projesi" | LOWEST — IGNORE if file evidence varsa | — |
Çakışma davranışı: Dizin adı veya user "G4" diyor ama dosyalar H7 ise → H7 olarak devam et, raporda discrepancy not düş ("dir/user label said G4 but files indicate H7 — file evidence wins"). Yanlış aile ile Faz 3'e geçmek yanlış repo indirir, pipeline çürür.
Sıralı arama (kullanıcıya SORMADAN ÖNCE bunları dene):
# 1. .ioc dosyası — en güvenilir
find <project> -name '*.ioc' -maxdepth 3 -exec grep -m1 'Mcu.Name=\|ProjectManager.ProjectName=' {} \;
# 2. Keil project files
find <project> -name '*.uvprojx' -maxdepth 4 -exec grep -m1 '<Device>' {} \;
# 3. Startup file → part number
find <project> -name 'startup_stm32*.s' -maxdepth 4 | head -3
# 4. Top-level main.c → which stm32xxxx.h is included
grep -m1 '#include "stm32' <project>/Core/Src/main.c 2>/dev/null \
|| grep -rm1 '#include "stm32' <project> --include='main.c' | head -3
# 5. Toolchain — .uvprojx <Cads> compiler tag, or .cproject for CubeIDE, or Makefile CC=
grep -h 'arm-none-eabi-gcc\|armclang\|armcc' <project>/Makefile <project>/**/Makefile 2>/dev/null
Sonuç part-number granülarite olmalı (STM32H730VBT6, sadece "H7" yetmez).
Belirleyemezsen → kullanıcıya sor: "MCU part-number ve silikon revizyonu (rev V/Y/X)?"
Faz 1b — Library & VERSION Detection (no-guess gate) 🔬
MCU yetmez — kullanılan kütüphanelerin tam sürümünü de tespit et. API'ler sürüme bağlıdır; bir CubeH7 sürümünde olan fonksiyon başka sürümde olmayabilir.
grep -rohE 'STM32Cube_FW_[A-Z0-9]+_V[0-9.]+' <project> | sort -u # → STM32Cube_FW_H7_V1.12.1
grep -oE 'Keil\.STM32[A-Za-z0-9]+_DFP\.[0-9.]+' <project>/**/*.uvprojx # → Keil.STM32H7xx_DFP.4.1.0
ls -d <project>/Drivers/STM32*xx_HAL_Driver 2>/dev/null && echo DISK || echo PACK-MANAGED
No-Guess gate (ZORUNLU): Bir HAL/LL/middleware sembolünü "var" diye sunmadan ÖNCE, tespit edilen sürümde var olduğunu doğrula. Pack-managed ise (disk'te yoksa) otoriter ST kaynağından — submodule SHA'yı çözüp driver repo'sunda grep'le:
SHA=$(gh api "repos/STMicroelectronics/STM32CubeH7/contents/Drivers/STM32H7xx_HAL_Driver?ref=v1.12.1" --jq '.sha')
DL=$(gh api "repos/STMicroelectronics/stm32h7xx_hal_driver/contents/Src/stm32h7xx_hal_ospi.c?ref=$SHA" --jq '.download_url')
curl -fsSL "$DL" | grep -c 'HAL_OSPI_DLYB_Cfg' # 0 → o sürümde YOK
Gerçek vaka:
HAL_OSPI_DLYB_CfgSTM32H7 HAL'de hiç yok. Doğrulanamıyorsa "emin değilim" de, otoriter kaynaktan çek, sonra öner. Bkz. CLAUDE.md §"Phase 0".
Faz 1.5 — Project Purpose Understanding ★
THE GOLDEN RULE: Tek bir satır kod analiz etmeden ÖNCE, projenin ne yaptığını anlamalısın. Anlamadıysan DURDUR ve KULLANICIYA SOR. Tahmin etme.
Procedure:
1. Top-level dokümanları oku:
- README.md / README.* (varsa)
- docs/ klasörü (varsa)
- .gitignore (tooling ipucu için)
2. Build/proje konfigürasyonu:
- .ioc dosyası → ProjectManager.ProjectName, "title" alanları
- .uvprojx → ProjectName, OutputName
- Top-level main.c başındaki yorum bloğu
3. Uygulama yüzeyi:
- Core/Src/main.c veya app_main.c → açılış akışı
- RTOS task fonksiyon adları (task'lar genelde modülün adıyla isimlendirilir)
- Klasör adları: Modbus/ BMS/ Motor/ Drivers/Custom/ BLE/ → domain ipucu
4. Bağımlılıklar:
- Middlewares/ altında ne var? (FreeRTOS, FatFs, LwIP, USB, BLE)
- X-CUBE-* paketleri kullanılıyor mu?
- Custom HAL wrapper'ları var mı?
5. Hardware ipuçları:
- .ioc'deki etkin peripheraller (USART, FDCAN, OSPI, ETH, ADC)
- GPIO label'ları (`LED_STATUS`, `BMS_INTR`, `MOTOR_PWM_A` gibi)
Çıktı — bir paragraflık özet üret:
Bu proje bir [tip: bootloader / application / gateway / motor controller / sensor node / ECU], domain: [automotive / industrial / consumer / instrumentation], stack: [bare-metal / FreeRTOS / RTX5], ana fonksiyonlar: [liste]. Hardware: [MCU + key peripherals]. Güvenim: HIGH / MEDIUM / LOW.
Güven değerlendirmesi:
- HIGH → README + .ioc + main.c açıkça aynı şeyi söylüyor. Devam et.
- MEDIUM → Domain belli ama bir detay eksik (örn. "bootloader olduğu net ama OTA target framework belirsiz"). Devam et ama Faz 2'de eksiği context interview ile çöz.
- LOW → Belirsizlik var. DUR. Kullanıcıya sor:
Cevap gelmeden Faz 2'ye GEÇME.Bu projeyi analiz etmeden önce ne yaptığını netleştirmek istiyorum: - [Spesifik soru 1: tip / domain / target] - [Spesifik soru 2: ana fonksiyonlar] - [Spesifik soru 3: kim için, hangi ortamda çalışacak] Şu an emin olduklarım: [...] Anlamadıklarım: [...]
Neden bu kadar katı? Domain anlamadan kanonik referans seçemezsin (bir motor controller'a bootloader referansı götürmek anlamsız), business-logic divergence'larını MCU-interface bug'larından ayıramazsın.
Faz 1.5b — Unified Product Detection (zorunlu alt-adım)
Eğer kullanıcı birden fazla proje dizini işaret ettiyse (örn. proje/
altında BL/ + App/, veya master/ + slave/, veya gateway/ +
radio/), aşağıdaki birleşik ürün kriterlerini test et:
Bunlar TEK ÜRÜN mü? — Aşağıdakilerden ≥2 doğru ise EVET:
□ Aynı MCU part-number (her ikisi de STM32H730VBTx gibi)
□ Aynı ürün isim prefix'i (DE-XENTRON-V3-BL vs DE-XENTRON-V3-RTX5-FATFS)
□ Bir dizinin adında "BL/Boot/Loader/IAP" var, diğerinde yok
□ Bir dizin küçük (~5-15 dosya), diğeri çok büyük
□ Birinde main.c'de "APPLICATION_ADDRESS" / "JumpTo" var
□ Hardware peripheral set'leri tamamlayıcı (BL: minimal; App: tam set)
Eğer EVET → BU NOKTADAN İTİBAREN HER ZAMAN BİRLİKTE ANALİZ ET.
Asla "önce BL'yi, sonra App'ı" diye ayırma. Bu yanlış çünkü:
| Bug sınıfı | Ayrı analizde | Birleşik analizde |
|---|---|---|
| BL→App jump sırasında flash unlock kalması | ❌ Görünmez | ✓ Yakalanır |
| VTOR offset mismatch (BL jumps to X, App linked at Y) | ❌ Her ikisi de "kendi başına" doğru | ✓ Sınırda görünür |
| OCTOSPI mem-mapped mode handoff eksik | ❌ Görünmez | ✓ Yakalanır |
| Watchdog state across handoff | ❌ Görünmez | ✓ Yakalanır |
| Clock tree BL'de set, App'ta yeniden init | ❌ Tutarsızlık görünmez | ✓ Yakalanır |
| Shared metadata page (version, boot_count) endianness | ❌ Görünmez | ✓ Yakalanır |
| Linker script overlap (BL flash + App load addr) | ❌ Her biri kendine göre doğru | ✓ Yakalanır |
Pipeline değişikliği: Faz 4 (reference graph) ve Faz 5 (user graph) her iki proje için ayrı çalıştırılır ama Faz 6 benchmark BİRLEŞİK olarak yapılır — BL graph'ı + App graph'ı + handoff metadata tek bir "product view" oluşturur. Faz 7 triage'ında yeni kategori:
| Sınıf | Kriter |
| **Cross-boundary handoff issue** | BL→App sınırında state/format/addr mismatch |
→ SEVERITY +1 (handoff bug'ları pahalıdır)
Birleşik ürün tespit edilmediyse veya kullanıcı sadece bir dizine işaret ettiyse normal pipeline.
Faz 2 — Errata + AN Context (A1)
Workaround'lar referansta var ama kullanıcıda yok → yanlış flag. Tersi: kullanıcıda var, referansta yok → false positive. Çözüm: "Beklenen divergence" listesi.
mkdir -p .claude-cache
# 1. Errata listesi (CLAUDE.md tablosundan MCU → ES sheet eşleştir)
# Örn: STM32H730 → ES0480
# 2. Önce ref-stm32-errata.md'den ilgili bölümleri çıkar
# 3. Online errata PDF'i fetch et (eğer skill'de yoksa veya stale ise)
# WebFetch: https://www.st.com/resource/en/errata_sheet/es<N>.pdf
Default AN set (project-type'a göre)
Tüm projelerde:
AN5152— H7 cache (DMA + D-cache safety)
Bootloader / IAP / XIP projeleri için (zorunlu set):
AN5188— External memory boot with reduced internal flash (H730/H7A3 XIP'ın resmi prosedür dokümanı; canonical reference readme'leri bunu işaret eder)AN2606— System memory boot mode (per-family bootloader address + entry conditions; option byte / BOOT0 / BOOT1)AN3155— USART protocol used in the STM32 bootloader (ROM bootloader UART komut seti; user'ın BL'i AN3155 fallback sağlıyor mu?)AN4861— Dual-bank flash + OTA (sadece dual-bank kullanılıyorsa)
Yüksek hız OSPI/QSPI projeleri için:
AN5050— OCTOSPI/QUADSPI yüksek hız + DLYB tuning
480+ MHz hedefli H7 projeleri için:
AN5312— H7 ODEN sequence (sadece H743/H753 rev V için kritik; H730/H735 farklı)
TrustZone-M projeleri için (H5/U5/L5):
AN5347— TrustZone-M SAU/GTZC design
Kural: Faz 1.5'te tespit edilen project type'a göre yukarıdan ilgili
AN'leri çek, .claude-cache/errata-context.md'ye özet çıkar. Sonra Faz 6
benchmark'ında "expected divergence" filter'ı olarak kullan.
AN fetch pattern
# WebFetch URL şablonu:
# https://www.st.com/resource/en/application_note/an<NNNN>-<slug>-stmicroelectronics.pdf
# Slug ST'nin kendi düzeninde, "an5188-external-memory-boot..." gibi.
# Aramak için WebSearch da kullanılabilir.
# Yerel ref-md'de özet varsa onu kullan (hızlı):
grep -i 'AN5188\|AN3155\|AN2606' ref-iap-ota.md ref-secure-boot.md
Çıktı: .claude-cache/errata-context.md — bu MCU + project-type için
"expected workarounds + standard procedures" listesi.
Faz 3 — Reference Acquisition (sparse clone)
Parametric — MCU ailesinden türetilir, ASLA hardcoded olmaz.
Tüm STM32Cube klonlamak ~2 GB. Sadece MCU-uygun kısımları çek.
# === Adım 0: MCU ailesini parça numarasından türet (Faz 1 çıktısı) ===
# STM32H730VBTx → FAMILY=H7, HAL_PREFIX=STM32H7xx
# STM32G474RETx → FAMILY=G4, HAL_PREFIX=STM32G4xx
# STM32F407VGTx → FAMILY=F4, HAL_PREFIX=STM32F4xx
# STM32L476RGTx → FAMILY=L4, HAL_PREFIX=STM32L4xx
# STM32U585AIIx → FAMILY=U5, HAL_PREFIX=STM32U5xx
# STM32H563ZITx → FAMILY=H5, HAL_PREFIX=STM32H5xx
# STM32F746ZGTx → FAMILY=F7, HAL_PREFIX=STM32F7xx
# STM32G071RBTx → FAMILY=G0, HAL_PREFIX=STM32G0xx
# STM32WB55RGVx → FAMILY=WB, HAL_PREFIX=STM32WBxx
# STM32MP157AAA → FAMILY=MP1, HAL_PREFIX=STM32MP1xx
# ... (ref-st-github-map.md ailesi katalog)
FAMILY=$(echo "$PART_NUMBER" | sed -E 's/^STM32([A-Z][0-9]).*/\1/') # → H7
HAL_PREFIX="STM32${FAMILY}xx"
REPO="STM32Cube${FAMILY}"
CACHE_DIR=".claude-cache/refs/${REPO}"
# === Adım A: Hangi referans projelerin uygun olduğunu keşfet ===
gh search code "STM32${PART_NUMBER%??}" --owner=STMicroelectronics \
--filename='main.c' --json repository,path \
> .claude-cache/refs-discovery.json
# === Adım B: Aile için pin-compatible / silikon-yakın board'ları belirle ===
# ref-st-github-map.md §3 tablosundan bak. Tipik eşleme:
# H7 → H735G-DK / H750B-DK / H743ZI-NUCLEO / NUCLEO-H723ZG
# G4 → NUCLEO-G474RE / STM32G474E-EVAL / B-G474E-DPOW1
# F4 → STM32F4-Discovery / NUCLEO-F411RE / STM32F4-EVAL
# L4 → NUCLEO-L476RG / STM32L4R9I-DISCO
# U5 → NUCLEO-U575ZI-Q / STM32U5A9J-DK
# H5 → NUCLEO-H563ZI / STM32H573I-DK
# F7 → STM32F769I-DISCO / NUCLEO-F767ZI
# G0 → NUCLEO-G071RB / NUCLEO-G0B1RE
# Pipeline'ın bilinmeyen aile karşısındaki davranışı: refs-discovery.json'dan
# en çok eşleşen Projects/<BOARD>/ alt-ağacını otomatik seç.
# === Adım C: Sparse clone — Projects + HAL driver ===
mkdir -p "${CACHE_DIR}"
cd "${CACHE_DIR}"
git clone --filter=blob:none --sparse --depth=1 \
"https://github.com/STMicroelectronics/${REPO}.git" .
# ⚠️ STM32Cube* repolarında HAL_Driver ve CMSIS/Device/ST/${HAL_PREFIX}
# alt-yolları **git submodule** (160000 commit mode). `sparse-checkout set`
# bunları "not a directory" hatasıyla reddeder. İki adım gerekli:
# Faz 1.5'te tespit edilen project type'a göre Examples alt-ağaçlarını seç.
# Bu liste **dinamik** — her project type için farklı:
EXAMPLE_PATHS=$(case "${PROJECT_TYPE}" in
bootloader|xip) echo "Projects/*/Applications/ExtMem_CodeExecution" ;;
motor_control) echo "Projects/*/Examples/HRTIM Projects/*/Examples/TIM" ;;
automotive_gateway) echo "Projects/*/Examples/FDCAN Projects/*/Applications/USB_Host" ;;
iot) echo "Projects/*/Applications/LwIP Projects/*/Applications/MbedTLS" ;;
sensor_node) echo "Projects/*/Examples/I2C Projects/*/Examples/ADC" ;;
*) echo "Projects/*/Examples/GPIO" ;;
esac)
git sparse-checkout set --skip-checks \
${EXAMPLE_PATHS} \
"Drivers/${HAL_PREFIX}_HAL_Driver" \
"Drivers/CMSIS/Device/ST/${HAL_PREFIX}"
# Sonra submodule'leri MANUEL init et (skip-checks bunu yapmaz):
git submodule update --init --depth=1 "Drivers/${HAL_PREFIX}_HAL_Driver"
git submodule update --init --depth=1 "Drivers/CMSIS/Device/ST/${HAL_PREFIX}"
# === Adım D: Reference SHA pinle (B4) ===
git rev-parse HEAD > "${CACHE_DIR}/.pinned-sha"
# Adım E (ZORUNLU) — Disk gate: artifacts var mı? (family-agnostic)
find .claude-cache/refs -name '*.c' | head -1 | grep -q . \
|| { echo "FAZ 3 FAIL: hiçbir referans .c yok"; exit 1; }
find .claude-cache/refs -name '.pinned-sha' | head -1 | grep -q . \
|| { echo "FAZ 3 FAIL: .pinned-sha yok"; exit 1; }
find .claude-cache/refs -path "*/Drivers/STM32*xx_HAL_Driver/Src/*.c" | head -1 | grep -q . \
|| { echo "FAZ 3 FAIL: HAL_Driver submodule init edilmedi"; exit 1; }
Çıktı: .claude-cache/refs/ ~50-200 MB; her referansta .pinned-sha.
Hard gate: Yukarıdaki Adım E exit-code 0 değilse skill ABORT eder ve şunu yazar: "ST referansı indirilemedi — review yapılamaz. İnternet/gh auth kontrolü gerek." ASLA ezbere review'a düşme.
Faz 4 — Reference Graph (canonical patterns)
Pre-gate: [ -s .claude-cache/refs-discovery.json ] && find .claude-cache/refs -name '*.c' | head -1 | grep -q . — geçmezse Faz 3'e geri dön.
# Graphify — Tool Bootstrap: detect → ASK permission → install; if present → update check.
# Binary is `graphify` (pip package: graphifyy). NEVER force-install silently.
if command -v graphify >/dev/null 2>&1; then
# PRESENT → update check (offer upgrade WITH permission; never auto-upgrade)
if python3 -m pip list --outdated 2>/dev/null | grep -iq '^graphifyy '; then
echo "graphify update available — ASK the user before: pip install --upgrade graphifyy"
fi
else
# MISSING → explain what graphify does + why, ASK the user, then on approval:
# pip install graphifyy (macOS: Homebrew python; ensure ~/.local/bin on PATH)
# If the user DECLINES → do NOT exit-fail; tell them the ≥3-file graph review needs
# graphify and offer a reduced (no-graph) review instead. Detection: `command -v graphify`.
echo "graphify missing → ASK permission, then: pip install graphifyy (decline → reduced review)"
fi
# v0.8.39 CLI: `graphify update <dir>` (AST-only, LLM gerekmez) → <dir>/graphify-out/graph.json
# (v0.8.39'da --include-pattern / --out <file> / --no-viz (update'te) YOK; dir başına tek graf.
# HAL/LL/BSP sinyallerini build'de değil, sonra `graphify query` ile daralt.)
graphify update .claude-cache/refs
# Post-gate: artifact diskte mi?
[ -s .claude-cache/refs/graphify-out/graph.json ] \
|| { echo "FAZ 4 FAIL: refs graph üretilmedi"; exit 1; }
Çıktı: ST'nin kanonik HAL/LL kullanım örüntüsü.
Faz 5 — User Graph + 3 Yan Kontrol (A2 + A3 + A4)
Paralel olarak çalıştır:
5.0 User MCU-interface graph
# v0.8.39: dir başına tek graf <user-project>/graphify-out/graph.json
# (mcu/full ayrımı + --include-pattern yok — sonra `graphify query`/`affected` ile daralt)
graphify update <user-project>
# Post-gate: graph diskte mi?
[ -s <user-project>/graphify-out/graph.json ] \
|| { echo "FAZ 5 FAIL: user graph üretilmedi"; exit 1; }
5.A2 — CubeMX Regen + Diff (sadece .ioc varsa)
# Sandbox kopyaya regen at, kullanıcı koduyla diff'le
mkdir -p .claude-cache/cubemx-sandbox
cp <user-project>/*.ioc .claude-cache/cubemx-sandbox/
cd .claude-cache/cubemx-sandbox
# MCP tool: cubemx_generate(ioc_path) veya STM32CubeMX-CLI -s gen.script
# Sonra:
diff -u .claude-cache/cubemx-sandbox/Core/Src/*.c <user-project>/Core/Src/*.c > drift.diff
Her drift satırı → candidate finding.
5.A3 — Linker / Memory Map Validation
# Kullanıcının .ld veya .sct dosyasını parse et
# MCU part'ından beklenen memory map'i çek (cmsis-device-h7/.../partition_*.h
# veya STM32_open_pin_data/mcu/<part>.xml)
# Karşılaştır:
# - ORIGIN/LENGTH each region
# - DMA-incapable bölgelerde DMA buffer placement (DTCM!)
# - Stack/heap sizes vs task count
5.A4 — Shared Variable / Concurrency Analysis
# graphify ile ISR ve task arasında paylaşılan değişken bul
graphify query "shared variables between ISR handlers and tasks" \
--graph <user-project>/graphify-out/graph.json
# Her shared var için doğrula:
# - `volatile` qualifier var mı?
# - size > 4 byte mı? → atomic değil → kritik bölge gerek
# - ISR_PRIORITY ≥ configMAX_SYSCALL_INTERRUPT_PRIORITY mi?
5.A5 — Per-Peripheral Canonical Diff (ZORUNLU — bu adım atlanırsa Faz 6 çürür)
User'ın MX_*_Init çağırdığı HER peripheral için side-by-side diff:
# 1. Kullanıcının init ettiği peripheral'ları listele
USER_PERIPHS=$(grep -h "MX_.*_Init\b" <user-project>/Core/Src/main.c \
| grep -oE "MX_[A-Z0-9_]+_Init" | sort -u)
# Örnek: MX_GPIO_Init MX_DMA_Init MX_FDCAN1_Init MX_FDCAN2_Init MX_SPI1_Init ...
for periph in $USER_PERIPHS; do
NAME=${periph#MX_} # FDCAN1_Init → FDCAN1
NAME=${NAME%_Init} # FDCAN1
BASE=${NAME%%[0-9]*} # FDCAN1 → FDCAN, USART2 → USART
# 2. ST canonical örnek bul
REF=$(find .claude-cache/refs -path "*Examples/${BASE}*" -name 'main.c' | head -1)
[ -z "$REF" ] && \
REF=".claude-cache/refs/.../Drivers/STM32${FAMILY}xx_HAL_Driver/Src/stm32${family}xx_hal_${base}.c"
# 3. User'ın init config'iyle canonical karşılaştır
USER_CONF=$(awk "/h${name}\.Init/,/HAL_${BASE}_Init\(/" <user-project>/Core/Src/${base}.c)
REF_CONF=$(awk "/h${name}\.Init/,/HAL_${BASE}_Init\(/" "$REF")
diff <(echo "$USER_CONF") <(echo "$REF_CONF")
# 4. NVIC priority'leri ÇIKAR — her ikisinden, side-by-side
grep "HAL_NVIC_SetPriority.*${NAME}" <user-project>/Core/Src/${base}.c
grep "HAL_NVIC_SetPriority.*${BASE}" "$REF"
done
Findings emit ETMEDEN ÖNCE her finding için: kanonik referansta KARŞILIĞINI gör. Yoksa finding CANCELLED veya "no canonical equivalent — best practice claim only" notu.
5.A6 — Stale SystemCoreClock × HAL_GetTick Cross-Check
# 1. SystemClock_Config çağrılıyor mu?
SCC_CALLED=$(grep -c "SystemClock_Config()" <user-project>/Core/Src/main.c)
# Çağrılan satırın #ifdef sarmasında olup olmadığını kontrol et
awk '/SystemClock_Config()/' <user-project>/Core/Src/main.c | \
while read -r line; do
# Bu satırın etrafındaki #ifdef'i bul
echo "context: $line"
done
# 2. SystemInit gövdesi #ifdef sarmasında mı?
grep -B2 "void SystemInit" <user-project>/Core/Src/system_*.c | head -5
grep -A3 "^void SystemInit" <user-project>/Core/Src/system_*.c | grep '#ifdef'
# 3. Bu shorted ise: HAL_GetTick kullanan TÜM peripheral init'lerini listele
# (HAL_FDCAN_Init, HAL_USART_Init, HAL_I2C_Init, ... — hepsi HAL_GetTick'le timeout)
grep -l "HAL_GetTick\|FDCAN_TIMEOUT_VALUE\|UART_TIMEOUT_VALUE" \
.claude-cache/refs/<repo>/Drivers/STM32*_HAL_Driver/Src/
Eğer SystemClock_Config skipped VE peripheral init'leri HAL_GetTick polling kullanıyorsa → TEK finding olarak emit et: "stale SystemCoreClock × HAL_GetTick timeout race" — birden fazla peripheral'ı ayrı ayrı yazma, root cause aynı.
Faz 6 — Benchmark + Confidence Scoring (B1)
İki graph'ı HAL/LL/BSP yüzeyinde karşılaştır. Her divergence için 2-3 kaynaktan doğrulama dene:
For each divergence D:
sources_confirming = []
# ST repo (varsa)
if D pattern matches canonical ST example:
sources_confirming.append("ST:<path>@<sha>")
# AN (varsa)
if D matches a documented AN procedure:
sources_confirming.append("AN<N> §<section>")
# RM (register-level claims için)
if D claims a register bit position / mode:
# gh api fetch RM via cmsis SVD or AN cross-ref
sources_confirming.append("RM<NNNN> §<section>")
# ARM ARM (core-level: cache, NVIC, MPU, SAU)
if D involves Cortex-M core feature:
sources_confirming.append("ARMv7-M ARM §X" or "ARMv8-M ARM §X")
# Errata
if D in errata-context.md:
sources_confirming.append("ES<N> §<x.y>")
# Confidence:
if len(sources_confirming) >= 2: HIGH-CONF
elif len(sources_confirming) == 1: MED-CONF
else: LOW-CONF (heuristic only — mark in finding)
Faz 7 — Divergence Triage (MCU-interface vs Business-logic)
| Sınıf | Kriter | Aksiyon |
|---|---|---|
| MCU-interface mismatch | ST API yüzeyinde (HAL/LL/BSP/SCB/__NVIC/register) | → CANDIDATE BUG, Faz 8'e gönder |
| Business logic difference | Sadece kullanıcının kendi modülleri arası | → IGNORE (beklenen) |
| Custom HAL wrapper | Kullanıcı HAL üzerine kendi sarmalayıcısını yazmış | → wrapper'ı ayrıca incele; wrapper'ın HAL'i doğru çağırıp çağırmadığına bak |
| Abstraction-level mismatch | Kullanıcı LL, referans HAL (veya tersi) | → tek başına BUG değil; çağrı sıralaması doğru mu kontrol et |
| Documented errata workaround | Faz 2 expected-list'te | → IGNORE |
Faz 3+4+5 filter'ı zaten business-logic'i dışlıyor, ama wrapper/abstraction sınıfları için manuel ayırma gerek.
Faz 8 — Manual Verification + Final Findings
Sadece Faz 7'den "CANDIDATE BUG" işaretli divergence'ları al. Her biri için:
- Source dosyayı aç (Read), tam context'i gör
- Karşılığını ST referans dosyasında aç, karşılaştır
- Cross-file doğrulama (ZORUNLU): Finding emit etmeden önce:
Symbol başka dosyada tanımlıysa → finding iptal, "verified-present"grep -rn "<symbol>" <user-project> graphify query "definitions of <symbol>" --graph <user-project>/graphify-out/graph.json - Bug muhtemel ise: Finding Template'e yaz (severity + confidence +
at: file:line) - False positive ise: kaydet ama emit etme (cache for next run)
🚫 False-Positive Avoidance Gate — her CRITICAL/HIGH bulgudan ÖNCE (ZORUNLU)
Gerçek bir audit, bu kontroller olmadan high-severity bulguların ~%28'ini over-claim etti (false-positive/abartılı). Bir CRITICAL/HIGH emit etmeden önce:
- Cache / DMA-coherency iddiası mı? → linker
.map'i oku (buffer adresi + hizası), MPU config'i oku (bölge CACHEABLE mı?), scatter/section'a bak. Cache maintenance "overrun" yalnızca CACHEABLE belleğe ulaşırsa bozulmadır; buffer MPU non-cacheable bölgedeyse op'lar zararsız no-op'tur, corruption değil. - Frekans / timing iddiası mı? → tüm clock tree'yi HESAPLA:
HSE_VALUE'yu doğrula (*_hal_conf.h— kristali VARSAYMA) → PLL(M/N/P) → SYSCLK → AHB/APB böler → peripheral clock mux'ı → prescaler. Hesaplamadığın bir saati asla yazma. - Severity veriyor musun? → dosya/sembol build'de mi teyit et (
.uvprojx/.cprojectsource listesi,.map). Dead/derlenmeyen kod → "dead" not düş, live-CRITICAL skorlama. - Bir peripheral sınıfının çoklu instance'ı mı (2 flash, 3 CAN, 2 SPI)? → her
config'in HANGİ chip/instance'a ait olduğunu (part
#define/header) önce belirle. - Dizi-dışı index mi? → struct layout'a bak: kasıtlı komşu alana (terminator/sentinel) düşen index mevcut config'de kasıtlı olabilir, OOB değil.
Verifier'ı da doğrula: Bir karşı-inceleme bir bulguyu çürütürse ona da körü körüne güvenme — çürütmeyi de kaynağa karşı yeniden doğrula (çürütme de yanlış olabilir). Her iddia VE karşı-iddia kaynağa dayanır. (Bkz. CLAUDE.md §"Fact-Based / No-Guess".)
ZORUNLU: Rapor diske yazılır. Final output sadece chat'e basılmaz —
Write tool ile şuraya kaydedilir:
<user-project>/STM32_REVIEW_<YYYY-MM-DD>.md
Dosya diskte yoksa skill INCOMPLETE — chat'e bu mesaj basılır: "⚠️ STM32_REVIEW_*.md disk'e yazılmadı, review eksik."
Final output (rapor template'i):
PROJECT SUMMARY (Faz 1.5'ten):
[bir paragraf — project ne yapıyor]
MCU + TOOLCHAIN:
STM32H730VBT6 (rev V), armclang AC6 -O3 -flto, RTX5
REFERENCE BENCHMARK:
Compared against:
- STM32Cube<FAMILY>@<sha>:Projects/<BOARD>/.../<Example>
- <repeat per peripheral that was diffed>
Per-peripheral side-by-side coverage:
FDCAN1 ✓ refs:Projects/.../FDCAN/FDCAN_Loopback/Src/main.c
FDCAN2 ✓ same canonical (no dual-FDCAN ST example — used skill ref-fdcan-multi.md)
SPI1 ✓ Drivers/STM32<F>xx_HAL_Driver/Src/stm32<f>xx_hal_spi.c
...
Peripherals WITHOUT canonical comparison: <list> — findings on these are LOW-CONF.
Total divergences in MCU-interface surface: N
GRAPHIFY MODE:
⚠️ AST-only (graphify update — no LLM semantic clustering)
Reason: graphify extract needs ANTHROPIC/GEMINI/OPENAI key; Claude Code session
hook integration not yet available. Semantic similarity not used in this report;
findings derive from direct file:line reads + HAL driver source diff.
APPLIED ERRATA CONTEXT:
- ES0480 §2.1.1 AXIRAM (workaround verified present)
- AN5312 ODEN sequence (workaround MISSING → finding below)
FINDINGS:
[CRITICAL] [HIGH-CONF] main.c:142 — H7-CLOCK-INIT
what: 480 MHz hedeflenmiş ama SYSCFG_PWRCR.ODEN sequence eksik
why: Rev V silikon: VOS0 yazımı yetmez, ODEN + ACTVOSRDY de gerek
fix: `SYSCFG->PWRCR |= SYSCFG_PWRCR_ODEN; while(!(PWR_FLAG_ACTVOSRDY));`
ref: STM32CubeH7@a3f2b9c:.../system_stm32h7xx.c:140
errata: AN5312 §3
[...]
CONFIDENCE NOTE:
3 findings are LOW-CONF (heuristic) — verify manually before action.
Faz 1.5 Kuralı — Bir kez daha vurgu
Project Purpose Understanding atlanırsa Faz 7 triage çöker: business-logic divergence'ları MCU bug'ı sanırsın. Bu yüzden Faz 1.5'i ASLA atla. Eğer "genel olarak skill çağrıldı, proje yok" → Mode B+ değil, A/C/D kullan.
Overview
Systematic workflow for production-grade STM32 firmware: constraints → architecture → drivers → optimization → verification. Applies to bare-metal, FreeRTOS, and Keil RTX5/CMSIS-RTOS2 targets. Covers the full chain from HAL/LL driver authoring to XIP debug and compiler optimization traps.
5-Phase Development Workflow
[1. Analyze Constraints] → [2. Design Architecture] → [3. Implement Drivers]
↓ ↓ ↓
MCU specs, flash/RAM Task/ISR/peripheral HAL + LL drivers
limits, timing budget memory layout RTOS integration
↓ ↓ ↓
[4. Optimize Resources] → [5. Test & Verify]
Code size, RAM, power Timing, edge cases,
performance measurement
Phase 1: Analyze Constraints
Document before writing a single line:
| Constraint | Questions to answer |
|---|---|
| MCU specs | Part number + silicon rev? Flash/RAM split? Core speed? FPU present? |
| Memory budget | Flash target (KiB left for future), RAM headroom, stack per task |
| Timing | Worst-case ISR latency allowed? Scheduler tick? DMA transfer deadlines? |
| Power budget | Average mA target? Sleep mode? Wakeup latency budget? |
| Communication | Bus speeds, frame rates, error budgets, master/slave roles |
| Industrial | Operating temp range, EMC class, ESD requirements, vibration |
Memory map template (document in linker script comments):
Flash: 0x08000000 - Bootloader (48KB) / App (remainder)
RAM: 0x20000000 - .data + .bss / Heap (if used) / Task stacks
DTCM: 0x20000000 (H7) - ISR handlers, critical data — CPU ONLY, DMA CANNOT ACCESS
AXI: 0x24000000 (H7) - DMA buffers (32B aligned), FatFS work area, large arrays
D2SRAM: 0x30000000 (H7) - DMA1/DMA2 accessible (128KB D2S1 + 32KB D2S2)
CCMRAM: 0x10000000 (F4/F7) - ISR code, fast data — NO DMA capable
Phase 2: Design Architecture
RTOS Task Design (FreeRTOS & Keil RTX5)
Priority ladder (highest → lowest):
Priority 7 (highest): Safety/watchdog monitor
Priority 6: Real-time control (motor, actuator)
Priority 5: Protocol RX (CAN/UART time-critical)
Priority 4: Protocol TX
Priority 3: Application logic
Priority 2: Communication (non-RT)
Priority 1: Background processing
Priority 0: Idle + watchdog pet
Task sizing rules:
- Stack = max call depth × frame size × 1.5 safety margin
- Measure stack HWM in debug; never skip in production-bound code
- One task per peripheral domain (not one mega-task)
Keil RTX5 / CMSIS-RTOS2 specifics:
// Thread definition — prefer static allocation
static uint64_t task_stack[256]; // 64-bit aligned
static osRtxThread_t task_cb; // canonical RTX5 CB type (rtx_os.h) — `osStaticThreadDef_t` is NOT a CMSIS-RTOS2 type
const osThreadAttr_t task_attr = {
.name = "ctrl",
.stack_mem = task_stack,
.stack_size = sizeof(task_stack),
.cb_mem = &task_cb,
.cb_size = sizeof(task_cb),
.priority = osPriorityHigh,
};
tid = osThreadNew(ctrl_task, NULL, &task_attr);
// Event flags instead of semaphores for ISR→task signaling
osEventFlagsSet(evt_id, FLAG_CAN_RX); // safe from ISR
FreeRTOS specifics:
// Static allocation — avoid heap in hard-RT code
static StaticTask_t task_tcb;
static StackType_t task_stack[256];
xTaskCreateStatic(ctrl_task, "ctrl", 256, NULL,
PRIORITY_CTRL, task_stack, &task_tcb);
// ISR → task notification (faster than queue for single events)
BaseType_t woken = pdFALSE;
vTaskNotifyGiveFromISR(ctrl_task_handle, &woken);
portYIELD_FROM_ISR(woken);
Interrupt Architecture
ISR responsibilities (< 2µs each):
✓ Set flag / give semaphore / send notification
✓ Read hardware status register (clear-on-read flags)
✓ Increment counter
✓ Arm DMA for next transfer
ISR forbidden:
✗ malloc / new
✗ printf / semihosting
✗ Blocking RTOS calls (use FromISR variants)
✗ Floating point (unless lazy stacking enabled AND registers saved)
✗ Long computation
NVIC priority mapping (FreeRTOS + Cortex-M4/M7):
// configMAX_SYSCALL_INTERRUPT_PRIORITY = 5 (numerical value)
// ISRs using FromISR APIs: priority 5..15 (lower urgency = higher number)
// ISRs NOT using RTOS: priority 0..4 (true real-time, cannot call RTOS)
HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 5, 0); // uses FromISR → OK
HAL_NVIC_SetPriority(TIM1_UP_IRQn, 2, 0); // pure HW ISR, no RTOS
Phase 3: Implement Drivers
Peripheral Driver Pattern (LL preferred over HAL for RT code)
// GOOD: LL driver — zero overhead, direct register access.
// Spins are BOUNDED (Iron Rule: timeout on every peripheral wait).
#define SPI_SPIN_GUARD 10000U
static inline HAL_StatusTypeDef spi_xfer_byte(uint8_t tx, uint8_t *rx)
{
uint32_t guard = SPI_SPIN_GUARD;
while (!LL_SPI_IsActiveFlag_TXE(SPI1)) { if (--guard == 0U) return HAL_TIMEOUT; }
LL_SPI_TransmitData8(SPI1, tx);
guard = SPI_SPIN_GUARD;
while (!LL_SPI_IsActiveFlag_RXNE(SPI1)) { if (--guard == 0U) return HAL_TIMEOUT; }
*rx = LL_SPI_ReceiveData8(SPI1);
return HAL_OK;
}
// BAD: HAL polling in tight loop — overhead + blocking
HAL_SPI_TransmitReceive(&hspi1, &tx, rx, 1, HAL_MAX_DELAY);
DMA Driver Pattern (cache-safe, M7)
// DMA buffer: 32-byte aligned, placed in AXI SRAM (NOT DTCM on H7)
// DTCM (0x20000000) is CPU-only — DMA1/DMA2 cannot access it!
__attribute__((section(".dma_buffer"), aligned(32)))
static uint8_t rx_buf[DMA_BUF_SIZE];
// Size formula: round up to 32-byte boundary — NEVER use (len + 32)
#define DMA_CACHE_SIZE(n) ((int32_t)(((n) + 31U) & ~31U))
void dma_rx_complete_cb(DMA_HandleTypeDef *hdma)
{
// Invalidate cache BEFORE reading buffer (M7 D-cache)
// Wrong formula overshoots buffer → dirty adjacent data discarded → corruption at -O1+
SCB_InvalidateDCache_by_Addr((uint32_t *)rx_buf, DMA_CACHE_SIZE(DMA_BUF_SIZE));
process_rx_data(rx_buf, DMA_BUF_SIZE);
}
void dma_tx_start(const uint8_t *data, size_t len)
{
// Clean cache BEFORE DMA reads buffer (M7 D-cache)
SCB_CleanDCache_by_Addr((uint32_t *)data, DMA_CACHE_SIZE(len));
HAL_UART_Transmit_DMA(&huart2, data, len);
}
UART / USART
// Ring buffer for UART RX — IDLE line interrupt + DMA
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t size)
{
// size = bytes received since last callback (IDLE or HT or TC)
ring_buf_write(&uart_rx_ring, dma_rx_buf, size);
osEventFlagsSet(uart_evt, UART_RX_FLAG);
// Restart DMA in circular mode — no explicit restart needed
}
I2C (with timeout protection)
// Never use HAL_MAX_DELAY on I2C — bus can hang forever
#define I2C_TIMEOUT_MS 10
HAL_StatusTypeDef i2c_write_reg(uint8_t addr, uint8_t reg, uint8_t val)
{
uint8_t buf[2] = { reg, val };
HAL_StatusTypeDef r = HAL_I2C_Master_Transmit(&hi2c1, addr << 1,
buf, 2, I2C_TIMEOUT_MS);
if (r != HAL_OK) {
// Reset I2C on bus error — critical for industrial reliability
__HAL_RCC_I2C1_FORCE_RESET();
HAL_Delay(1);
__HAL_RCC_I2C1_RELEASE_RESET();
MX_I2C1_Init();
}
return r;
}
CAN / FDCAN (industrial)
// FDCAN filter: whitelist by range — reject all others
FDCAN_FilterTypeDef filter = {
.IdType = FDCAN_STANDARD_ID,
.FilterIndex = 0,
.FilterType = FDCAN_FILTER_RANGE,
.FilterConfig = FDCAN_FILTER_TO_RXFIFO0,
.FilterID1 = 0x100, // accept 0x100..0x1FF only
.FilterID2 = 0x1FF,
};
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
HAL_FDCAN_ConfigGlobalFilter(&hfdcan1,
FDCAN_REJECT, FDCAN_REJECT, // non-matching frames rejected
FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE);
// Bus-off recovery: MANUAL — never automatic in safety-critical apps
HAL_FDCAN_Start(&hfdcan1);
// In error ISR:
if (HAL_FDCAN_GetProtocolStatus(&hfdcan1, &status) == HAL_OK)
if (status.BusOff) log_error(ERR_CAN_BUSOFF); // app decides when to recover
Watchdog Pattern (multi-task)
// Checklist bitmap — every task must kick its own bit
static volatile uint32_t wdg_checklist;
#define WDG_TASK_CTRL BIT(0)
#define WDG_TASK_COMMS BIT(1)
#define WDG_TASK_SENSOR BIT(2)
#define WDG_ALL_TASKS (WDG_TASK_CTRL | WDG_TASK_COMMS | WDG_TASK_SENSOR)
// Each task periodically sets its bit — ATOMICALLY. A plain `|=` is a
// LDR/ORR/STR read-modify-write: racing with the monitor's clear it can
// write back stale bits → watchdog fed while another task is dead.
void ctrl_task(void *arg) {
for (;;) {
__atomic_fetch_or(&wdg_checklist, WDG_TASK_CTRL, __ATOMIC_RELAXED);
// ... work ...
osDelay(10);
}
}
// Watchdog monitor task (highest priority)
void wdg_task(void *arg) {
for (;;) {
uint32_t alive = __atomic_exchange_n(&wdg_checklist, 0U, __ATOMIC_RELAXED);
if ((alive & WDG_ALL_TASKS) == WDG_ALL_TASKS) {
HAL_IWDG_Refresh(&hiwdg); // pet only when ALL tasks alive
}
osDelay(IWDG_FEED_PERIOD_MS);
}
}
Phase 4: Optimize Resources
Flash / Code Size
Compiler flags (GCC arm-none-eabi):
-Os → optimize for size (prefer over -O2 for flash-constrained)
-flto → link-time optimization (can save 10-20% flash)
-ffunction-sections -fdata-sections → enable linker GC
-Wl,--gc-sections → remove dead code/data
-fno-exceptions -fno-rtti → C++ projects: saves 10-30KB
-fshort-enums → pack enums to smallest type
Check size:
arm-none-eabi-size build/firmware.elf
arm-none-eabi-nm --size-sort --print-size build/firmware.elf | tail -20
RAM Optimization
// Put large read-only tables in flash
const uint16_t sine_lut[1024] __attribute__((section(".rodata"))) = { ... };
// Zero-init large buffers in .bss (not .data) — only matters for link size
static uint8_t frame_buf[4096]; // .bss — no flash image cost
// Use bitfields for flag clusters
typedef struct {
uint8_t sensor_ok : 1;
uint8_t can_ok : 1;
uint8_t ota_active : 1;
uint8_t reserved : 5;
} sys_flags_t;
Compiler Optimization — Silent Bug Prevention
Tanı: Kod -O0'da çalışıyor, -O1/-O2/-Os'ta sessizce bozuluyor. Hata mesajı yok.
Cat 1: volatile Eksikliği (En Yaygın)
/* YANLIŞ — -O2'de sonsuz döngü */
uint8_t dma_done = 0;
void DMA_IRQHandler(void) { dma_done = 1; }
void wait(void) { while (!dma_done) {} }
/* DOĞRU */
volatile uint8_t dma_done = 0;
| Kural | Açıklama |
|---|---|
| ISR ile paylaşılan değişken | volatile zorunlu |
| Hardware register pointer | CMSIS __IO (= volatile) — zaten tanımlı |
| DMA buffer | volatile değil — cache/barrier gerekli (Cat 3) |
| Multi-byte struct | volatile yetmez — critical section şart |
/* Multi-byte atomic okuma */
uint32_t primask = __get_PRIMASK();
__disable_irq();
MyStruct_t snap = shared_struct; /* atomik blok */
__set_PRIMASK(primask);
Cat 2: Memory Barrier Eksikliği
/* DMA TX — eksik barrier → eski veri gönderilir */
memcpy(tx_buf, data, len);
__DSB(); /* CPU write buffer flush */
SCB_CleanDCache_by_Addr((uint32_t *)tx_buf, len); /* M7 */
HAL_SPI_Transmit_DMA(&hspi, tx_buf, len);
/* Peripheral enable sonrası */
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
__DSB(); /* clock enable etkili olsun */
TIM2->CR1 |= TIM_CR1_CEN;
/* MPU/VTOR değişikliği sonrası — pipeline flush şart */
SCB->VTOR = new_vt;
__DSB();
__ISB();
/* Compiler reorder engelle */
#define COMPILER_BARRIER() __asm__ volatile("" ::: "memory")
prepare_data(buf);
COMPILER_BARRIER(); /* compiler buf'ı flag sonrasına taşımasın */
volatile bool ready = true;
| Barrier | Ne Yapar |
|---|---|
__DSB() |
Tüm bellek yazmaları tamamla (DMA, peripheral sonrası) |
__DMB() |
Sıralama garantisi (tamamlamayı beklemez) |
__ISB() |
Pipeline flush (MPU/VTOR/CPACR sonrası şart) |
Cat 3: DMA Cache Coherency (M7: F7, H7, H7RS)
/* TX: CPU yazar → DMA okur → peripheral */
void dma_tx_start(uint8_t *data, uint32_t len) {
SCB_CleanDCache_by_Addr((uint32_t *)data, len); /* cache → SRAM */
__DSB();
HAL_SPI_Transmit_DMA(&hspi, data, len);
}
/* RX: peripheral → DMA yazar → CPU okur */
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
SCB_InvalidateDCache_by_Addr((uint32_t *)rx_buf, sizeof(rx_buf));
process(rx_buf); /* artık SRAM'dan güncel veri gelir */
}
/* Zorunlu: 32-byte hizalama ve 32'nin katı boyut */
ALIGN_32BYTES(uint8_t tx_buf[TX_SIZE]) __attribute__((section(".dma_buf")));
ALIGN_32BYTES(uint8_t rx_buf[RX_SIZE]) __attribute__((section(".dma_buf")));
/* Alternatif: MPU ile non-cacheable — Clean/Invalidate gerekmez */
/* MPU Region: TEX=001, S=0, C=0, B=0 */
Cat 4: LTO — ISR / Callback Silme
/* ISR ve weak callback override'ları LTO tarafından silinebilir */
__attribute__((used, interrupt("IRQ")))
void USART1_IRQHandler(void) { /* ... */ }
__attribute__((used))
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* ... */ }
/* CMakeLists.txt / Makefile */
/* -Wl,--undefined=USART1_IRQHandler */
/* -Wl,--undefined=HardFault_Handler */
/* noinline: LTO inline etmesin — naked ISR'lar için zorunlu */
__attribute__((noinline)) void hard_fault_handler_c(uint32_t *sp) { }
Cat 5: Strict Aliasing İhlali
/* YANLIŞ — -O2'de undefined behavior */
float f = 3.14f;
uint32_t bits = *(uint32_t *)(&f);
/* DOĞRU — memcpy (compiler optimize eder, UB yok) */
uint32_t bits;
memcpy(&bits, &f, 4);
/* DOĞRU — C99 union */
union { float f; uint32_t u; } cv;
cv.f = 3.14f;
uint32_t bits2 = cv.u;
/* Modbus / protocol byte extract — doğru yol */
uint32_t from_be
*Truncated - read the full file at https://github.com/xentron-bit/stm32-embedded-skill/blob/7d81ee0c2bff8399c32fb2fdaa4ba3d98bcac338/