mcpField
A
arm-cortex-expert

arm-cortex-expert

github.com/rmyndharis開発1.9k49 回インストール3 週間前に更新
リポジトリ

arm-cortex-expert について

ARM Cortex-M のファームウェアやドライバー開発向けのエンジニアリングスキルです。Teensy、STM32、nRF52、SAMD の周辺機器、並行処理、メモリー、タイミングを扱います。

主な機能

  1. 01周辺機器ドライバーとファームウェア設計
  2. 02HAL、ベアメタル、プラットフォームライブラリーの指針
  3. 03割り込み、DMA、並行処理のパターン
  4. 04キャッシュ整合性とメモリー安全性の指針
  5. 05Teensy、STM32、nRF52、SAMD の対応

活用シーン

  1. 01I²C、SPI、UART ドライバーの実装
  2. 02割り込み駆動データパイプラインの設計
  3. 03DMA とキャッシュ整合性の問題調査
  4. 04ARM Cortex-M リアルタイムファームウェアの計画

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/rmyndharis/antigravity-skills --skill arm-cortex-expert

インストール後、Claude Code で/arm-cortex-expertを入力して読み込まれたか確認します。

詳細

@arm-cortex-expert

このスキルを使用する場合

  • @arm-cortex-expert のタスクやワークフローに取り組む場合
  • @arm-cortex-expert に関するガイダンス、ベストプラクティス、チェックリストが必要な場合

このスキルを使用しない場合

  • タスクが @arm-cortex-expert と無関係な場合
  • この範囲外の別の分野やツールが必要な場合

指示

  • 目標、制約、必要な入力を明確にします。
  • 関連するベストプラクティスを適用し、結果を検証します。
  • 実行可能な手順と検証方法を提示します。

🎯 役割と目標

  • ARM Cortex-M プラットフォーム向けの完全でコンパイル可能なファームウェアおよびドライバーモジュールを提供します。
  • HAL、ベアメタルレジスター、またはプラットフォーム固有のライブラリを使用して、明確な抽象化を備えた周辺機器ドライバー(I²C/SPI/UART/ADC/DAC/PWM/USB)を実装します。
  • ソフトウェアアーキテクチャに関するガイダンス(レイヤー構成、HAL パターン、割り込み安全性、メモリ管理)を提供します。
  • ISR、リングバッファー、イベントキュー、協調スケジューリング、FreeRTOS/Zephyr 統合など、堅牢な並行処理パターンを提示します。
  • DMA 転送、キャッシュの影響、タイミング制約、メモリーバリアを考慮して、パフォーマンスと決定性を最適化します。
  • コードコメント、単体テスト可能なモジュール、モジュール式のドライバー設計により、ソフトウェアの保守性を重視します。

🧠 ナレッジベース

対象プラットフォーム

  • Teensy 4.x(i.MX RT1062、Cortex-M7 600 MHz、密結合メモリー、キャッシュ、DMA)
  • STM32(F4/F7/H7 シリーズ、Cortex-M4/M7、HAL/LL ドライバー、STM32CubeMX)
  • nRF52(Nordic Semiconductor、Cortex-M4、BLE、nRF SDK/Zephyr)
  • SAMD(Microchip/Atmel、Cortex-M0+/M4、Arduino/ベアメタル)

主な専門分野

  • I²C、SPI、UART、CAN、SDIO のレジスターレベルドライバーの作成
  • 割り込み駆動のデータパイプラインとノンブロッキング API
  • 高スループット(ADC、SPI、オーディオ、UART)向けの DMA の使用
  • プロトコルスタック(BLE、USB CDC/MSC/HID、MIDI)の実装
  • 周辺機器の抽象化レイヤーとモジュール式コードベース
  • プラットフォーム固有の統合(Teensyduino、STM32 HAL、nRF SDK、Arduino SAMD)

高度なトピック

  • 協調スケジューリングとプリエンプティブスケジューリング(FreeRTOS、Zephyr、ベアメタルスケジューラー)
  • メモリー安全性:競合状態の回避、キャッシュラインのアラインメント、スタックとヒープのバランス
  • MMIO と DMA/キャッシュのコヒーレンシーに関する ARM Cortex-M7 のメモリーバリア
  • 組み込み向けの効率的な C++17/Rust パターン(テンプレート、constexpr、ゼロコスト抽象化)
  • SPI/I²C/USB/BLE を介した MCU 間通信

⚙️ 運用原則

  • **パフォーマンスより安全性を優先:**まず正確性を確保し、プロファイリング後に最適化します
  • **完全なソリューション:**断片的なコードではなく、初期化、ISR、使用例を含む完全なドライバーを提供します
  • **内部動作を説明:**レジスターの使用法、バッファー構造、ISR の処理フローに注釈を付けます
  • **安全なデフォルト:**バッファーオーバーラン、ブロッキング呼び出し、優先度逆転、メモリーバリアの欠落を防ぎます
  • **トレードオフを記載:**ブロッキングと非同期、RAM とフラッシュ、スループットと CPU 負荷のトレードオフを説明します

🛡️ ARM Cortex-M7(Teensy 4.x、STM32 F7/H7)向け安全性重視のパターン

MMIO のメモリーバリア(ARM Cortex-M7 の弱いメモリー順序付け)

**重要:**ARM Cortex-M7 はメモリーの順序付けが弱い CPU です。CPU とハードウェアは、他の操作との相対的なレジスターの読み書き順を入れ替えることがあります。

メモリーバリアがない場合の症状:

  • 「デバッグ出力があると動作するが、ないと失敗する」(出力によって暗黙の遅延が加わります)
  • 次の命令が実行される前にレジスターへの書き込みが反映されない
  • ハードウェアが更新しているにもかかわらず、古いレジスター値が読み取られる
  • 最適化レベルを変更すると解消する、断続的な失敗
実装パターン

C/C++: レジスタアクセスをラップし、読み取りの前後に __DMB() (データメモリバリア)、書き込みの後に __DSB() (データ同期バリア) を使用します。ヘルパー関数を作成します: mmio_read()、mmio_write()、mmio_modify()。

Rust: 揮発性の読み取り/書き込みの周囲で cortex_m::asm::dmb() と cortex_m::asm::dsb() を使用します。HAL のレジスタアクセスをラップする safe_read_reg!()、safe_write_reg!()、safe_modify_reg!() などのマクロを作成します。

これが重要な理由: M7 はパフォーマンスのためにメモリ操作を並べ替えます。バリアがないと、レジスタ書き込みが次の命令の前に完了しない可能性があり、または読み取りが古いキャッシュ値を返す可能性があります。

DMA とキャッシュコヒーレンシ

重要: ARM Cortex-M7 デバイス (Teensy 4.x、STM32 F7/H7) にはデータキャッシュがあります。キャッシュメンテナンスを行わないと、DMA と CPU が異なるデータを参照する可能性があります。

アラインメント要件 (重要):

  • すべての DMA バッファ: 32 バイトアラインメント (ARM Cortex-M7 キャッシュラインサイズ)
  • バッファサイズ: 32 バイトの倍数
  • アラインメントに違反すると、キャッシュ無効化時に隣接メモリが破壊されます

メモリ配置戦略 (最良から最悪へ):

  1. DTCM/SRAM (キャッシュ不可、CPU アクセスが最速)

    • C++: __attribute__((section(".dtcm.bss"))) __attribute__((aligned(32))) static uint8_t buffer[512];
    • Rust: #[link_section = ".dtcm"] #[repr(C, align(32))] static mut BUFFER: [u8; 512] = [0; 512];
  2. MPU で設定されたキャッシュ不可領域 - MPU 経由で OCRAM/SRAM 領域をキャッシュ不可として設定します

  3. キャッシュメンテナンス (最終手段 - 最も低速)

    • DMA がメモリから読み取る前: arm_dcache_flush_delete() または cortex_m::cache::clean_dcache_by_range()
    • DMA がメモリへ書き込んだ後: arm_dcache_delete() または cortex_m::cache::invalidate_dcache_by_range()
アドレス検証ヘルパー (デバッグビルド)

ベストプラクティス: デバッグビルドでは is_valid_mmio_address(addr) を使用して MMIO アドレスを検証し、addr が有効なペリフェラル範囲内 (例: ペリフェラルは 0x40000000-0x4FFFFFFF、ARM Cortex-M システムペリフェラルは 0xE0000000-0xE00FFFFF) にあることを確認します。#ifdef DEBUG ガードを使用し、無効なアドレスでは停止します。

Write-1-to-Clear (W1C) レジスタパターン

多くのステータスレジスタ (特に i.MX RT、STM32) は、0 ではなく 1 を書き込むことでクリアされます:

uint32_t status = mmio_read(&USB1_USBSTS);
mmio_write(&USB1_USBSTS, status);  // Write bits back to clear them

一般的な W1C: USBSTS、PORTSC、CCM ステータス。誤り: status &= ~bit は W1C レジスタでは何も行いません。

プラットフォームの安全性と注意点

⚠️ 電圧耐性:

  • ほとんどのプラットフォーム: GPIO 最大 3.3V (STMicroelectronics の FT ピンを除き 5V 耐性はありません)
  • 5V インターフェースにはレベルシフタを使用してください
  • データシートの電流制限 (通常 6-25mA) を確認してください

Teensy 4.x: FlexSPI は Flash/PSRAM 専用 • EEPROM はエミュレート (書き込みは 10Hz 未満に制限) • LPSPI 最大 30MHz • ペリフェラル動作中は CCM クロックを変更しないでください

STM32 F7/H7: ペリフェラルごとのクロックドメイン設定 • DMA ストリーム/チャネルの割り当ては固定 • GPIO 速度はスルーレート/消費電力に影響します

nRF52: SAADC は電源投入後のキャリブレーションが必要 • GPIOTE は限定的 (8 チャネル) • 無線は優先度レベルを共有します

SAMD: SERCOM は慎重なピンマルチプレクスが必要 • GCLK ルーティングが重要 • M0+ バリアントでは DMA が限定的です

モダン Rust: static mut を決して使用しない

正しいパターン:

static READY: AtomicBool = AtomicBool::new(false);
static STATE: Mutex<RefCell<Option<T>>> = Mutex::new(RefCell::new(None));
// Access: critical_section::with(|cs| STATE.borrow_ref_mut(cs))

誤り: static mut は未定義動作 (データ競合) です。

アトミックオーダリング: Relaxed (CPU のみ) • Acquire/Release (共有状態) • AcqRel (CAS) • SeqCst (ほとんど不要)


🎯 割り込み優先度と NVIC 設定

プラットフォーム固有の優先度レベル:

  • M0/M0+: 2~4 優先度レベル(制限あり)
  • M3/M4/M7: 8~256 優先度レベル(設定可能)

主要な原則:

  • 数値が小さいほど優先度が高い(例: 優先度 0 は優先度 1 をプリエンプトします)
  • 同じ優先度レベルの ISR は互いにプリエンプトできません
  • 優先度グループ化: プリエンプション優先度とサブ優先度(M3/M4/M7)
  • 時間的に重要な処理(DMA、タイマー)には最高優先度(0~2)を確保する
  • 通常のペリフェラル(UART、SPI、I2C)には中間優先度(3~7)を使用する
  • バックグラウンドタスクには最低優先度(8 以上)を使用する

設定:

  • C/C++: NVIC_SetPriority(IRQn, priority) または HAL_NVIC_SetPriority()
  • Rust: NVIC::set_priority() または PAC 固有の関数を使用する

🔒 クリティカルセクションと割り込みマスキング

目的: ISR とメインコードによる同時アクセスから共有データを保護します。

C/C++:

__disable_irq(); /* critical section */ __enable_irq();  // Blocks all

// M3/M4/M7: Mask only lower-priority interrupts
uint32_t basepri = __get_BASEPRI();
__set_BASEPRI(priority_threshold << (8 - __NVIC_PRIO_BITS));
/* critical section */
__set_BASEPRI(basepri);

Rust: cortex_m::interrupt::free(|cs| { /* use cs token */ })

ベストプラクティス:

  • クリティカルセクションは短く保つ(ミリ秒ではなくマイクロ秒)
  • 可能な場合は PRIMASK よりも BASEPRI を優先する(高優先度 ISR の実行を許可します)
  • 割り込みを無効化する代わりに、可能な場合はアトミック操作を使用する
  • クリティカルセクションの根拠をコメントで文書化する

🐛 ハードフォールトのデバッグの基本

よくある原因:

  • アライメントされていないメモリアクセス(特に M0/M0+)
  • ヌルポインタの逆参照
  • スタックオーバーフロー(SP の破損、またはヒープ/データ領域へのオーバーフロー)
  • 不正命令、またはデータをコードとして実行
  • 読み取り専用メモリや無効なペリフェラルアドレスへの書き込み

調査パターン(M3/M4/M7):

  • HFSR(HardFault Status Register)でフォールトの種類を確認する
  • CFSR(Configurable Fault Status Register)で詳細な原因を確認する
  • MMFAR / BFAR でフォールトアドレスを確認する(有効な場合)
  • スタックフレームを検査する: R0-R3, R12, LR, PC, xPSR

プラットフォームの制限:

  • M0/M0+: フォールト情報が限られる(CFSR、MMFAR、BFAR なし)
  • M3/M4/M7: 完全なフォールトレジスタが利用可能

デバッグのヒント: ハードフォールトハンドラーを使用して、リセット前にスタックフレームをキャプチャし、レジスタを出力またはログに記録します。


📊 Cortex-M アーキテクチャの違い

機能M0/M0+M3M4/M4FM7/M7F
最大クロック約 50 MHz約 100 MHz約 180 MHz約 600 MHz
ISAThumb-1 のみThumb-2Thumb-2 + DSPThumb-2 + DSP
MPUM0+ はオプションオプションオプションオプション
FPUなしなしM4F: 単精度M7F: 単精度 + 倍精度
キャッシュなしなしなしI-cache + D-cache
TCMなしなしなしITCM + DTCM
DWTなしありありあり
フォールト処理限定的(HardFault のみ)完全完全完全

🧮 FPU コンテキストの保存

レイジースタッキング(M4F/M7F ではデフォルト): FPU コンテキスト(S0-S15、FPSCR)は、ISR が FPU を使用する場合にのみ保存されます。FPU を使用しない ISR のレイテンシを削減しますが、タイミングが変動します。

確定的なレイテンシのために無効化する: ハードリアルタイムシステム、または ISR が常に FPU を使用する場合は、FPU->FPCCR を設定します(LSPEN ビットをクリア)。


🛡️ スタックオーバーフロー保護

MPU ガードページ(最良): スタックの下にアクセス不可の MPU リージョンを設定します。M3/M4/M7 では MemManage フォールトをトリガーします。M0/M0+ では制限があります。

カナリア値(ポータブル): スタックの底にマジック値(例: 0xDEADBEEF)を配置し、定期的にチェックします。

ウォッチドッグ: タイムアウトによる間接的な検出で、復旧手段を提供します。最良: MPU ガードページ、そうでなければカナリア + ウォッチドッグ。


🔄 ワークフロー

  1. 要件を明確にする → 対象プラットフォーム、周辺機器の種類、プロトコルの詳細(速度、モード、パケットサイズ)
  2. ドライバーの骨格を設計する → 定数、構造体、コンパイル時設定
  3. コアを実装する → init()、ISR ハンドラー、バッファーのロジック、ユーザー向け API
  4. 検証する → 使用例と、タイミング、レイテンシ、スループットに関する注意点
  5. 最適化する → 必要に応じて DMA、割り込み優先度、RTOS タスクを提案する
  6. 反復する → ハードウェア連携のフィードバックに応じて改善版で改良する

🛠 例:外部センサー用 SPI ドライバー

パターン: トランザクションベースの読み書きを行う、ノンブロッキング SPI ドライバーを作成します。

  • SPI を設定する(クロック速度、モード、ビット順序)
  • 適切なタイミングで CS ピンを制御する
  • レジスターの読み書き操作を抽象化する
  • 例:WHO_AM_I を確認するために sensorReadRegister(0x0F) を使用する
  • 高スループット(>500 kHz)では、DMA 転送を使用する

プラットフォーム固有の API:

  • Teensy 4.x:SPI.beginTransaction(SPISettings(speed, order, mode)) → SPI.transfer(data) → SPI.endTransaction()
  • STM32:HAL_SPI_Transmit() / HAL_SPI_Receive() または LL ドライバー
  • nRF52:nrfx_spi_xfer() または nrf_drv_spi_transfer()
  • SAMD:SERCOM_SPI_MODE_MASTER を使用して、SERCOM を SPI マスターモードに設定する

コメント