mmio-and-bit-manipulation について
低レベルコードやベアメタルコードの MMIO とレジスターアクセスを扱うエージェント向けスキルです。volatile、安全なビット操作、アラインメント、エンディアン、メモリバリアを解説します。
主な機能
- 01volatile によるレジスターアクセス
- 02読み取り・変更・書き込みパターン
- 03明示的なマスクとシフト
- 04アラインメントとエンディアンの指針
- 05メモリバリアの使用方法
活用シーン
- 01HAL を使わないペリフェラルドライバーの作成
- 02断続的なレジスター破損の調査
- 03移植性の低い C ビットフィールドの置き換え
- 04ISR とメイン処理のレジスターアクセス確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill mmio-and-bit-manipulationインストール後、Claude Code で/mmio-and-bit-manipulationを入力して読み込まれたか確認します。
詳細
MMIO とビット操作
目的
メモリマップド I/O を安全に扱う方法をエージェントに案内します。volatile のセマンティクス、読み取り・変更・書き込みのパターン、ビットフィールドの落とし穴、アラインメントとエンディアン、ベアメタルドライバー向けの移植可能なレジスターアクセスマクロを扱います。
使用する場面
- HAL を使わずにペリフェラルのレジスタードライバーを作成するとき
- レジスターの破損や古い値の読み取りが断続的に発生する問題を修正するとき
- C のビットフィールドを明示的なマスクに置き換えるとき
- リトルエンディアンの MCU 間でドライバーを移植するとき
- ISR とメイン処理間のレジスターアクセスを監査するとき
ワークフロー
1. MMIO の基本
ペリフェラルのレジスターは、CPU のメモリーマップ内の固定アドレスに配置されています。コンパイラーによる読み取りや書き込みのキャッシュを防ぐ必要があります。
#include <stdint.h>
#define PERIPH_BASE 0x40000000U
#define GPIOA_MODER (*(volatile uint32_t *)(PERIPH_BASE + 0x20000U))
| 修飾子 | 効果 |
|---|---|
volatile | アクセスごとにロード/ストアを強制します。ハードウェアには必須です |
const volatile | 読み取り専用のハードウェア(まれです) |
通常の uint32_t * | 誤りです。コンパイラーが最適化によってアクセスを省略する可能性があります |
2. 読み取り・変更・書き込みマクロ
#define REG32(addr) (*(volatile uint32_t *)(addr))
#define REG_SET(addr, mask) (REG32(addr) |= (mask))
#define REG_CLR(addr, mask) (REG32(addr) &= ~(mask))
#define REG_TOGGLE(addr, mask) (REG32(addr) ^= (mask))
#define REG_WRITE(addr, val) (REG32(addr) = (val))
#define REG_READ(addr) (REG32(addr))
適切 — レジスターが対応している場合、単一ビットの更新をアトミックに行う意図を示します。
#define GPIOA_BSRR REG32(0x40020018U)
GPIOA_BSRR = (1U << 5); /* set PA5 */
GPIOA_BSRR = (1U << (5+16)); /* reset PA5 — STM32 BSRR pattern */
不適切 — 割り込みと共有するレジスターに対する非アトミックな RMW です。
uint32_t v = REG_READ(GPIOA_MODER);
v |= (1U << 10);
REG_WRITE(GPIOA_MODER, v); /* ISR may interleave — lost update */
対処法:IRQ を短時間無効にする、ハードウェアのセット/クリアレジスターを使う、または利用可能な場合は LL アトミックビットバンドを使います。
3. ビットフィールドの落とし穴
/* Bad — layout is implementation-defined, not portable */
typedef struct {
uint32_t mode : 2;
uint32_t type : 1;
uint32_t speed : 2;
} gpio_moder_bits_t;
明示的なマスクを使用してください。
#define GPIO_MODER_MODE0_SHIFT 0
#define GPIO_MODER_MODE0_MASK (3U << GPIO_MODER_MODE0_SHIFT)
#define GPIO_MODER_MODE0_VAL(n) ((n) << GPIO_MODER_MODE0_SHIFT)
REG32(GPIOA_MODER) = (REG32(GPIOA_MODER) & ~GPIO_MODER_MODE0_MASK)
| GPIO_MODER_MODE0_VAL(1); /* output */
4. エンディアンとアラインメント
- Cortex-M とほとんどの MCU はリトルエンディアンです。
uint32_tの MMIO にはワード境界にアラインされたアドレスを使います - アラインメントされていない
uint32_tアクセスは、ARMv7-M 以降でフォルトする場合があります - 8 ビットレジスターには、正しいバイトレーンアドレスで
volatile uint8_tを使います
#define REG8(addr) (*(volatile uint8_t *)(addr))
5. メモリーバリア(必要な場合)
/* After configuring peripheral before first use */
__DSB();
__ISB();
/* After DMA setup, before enabling channel */
__DMB();
Cortex-M では CMSIS バリア(core_cm4.h)を使用してください。
6. エージェントの使用例
/mmio-and-bit-manipulation Safe pattern to set bit 3 without affecting other bits in ISR context
/mmio-and-bit-manipulation Why must peripheral pointers be volatile?
よくある問題
| 症状 | 原因 | 対処法 |
|---|---|---|
| レジスターへの書き込みが無視される | アドレスが間違っている/クロックが無効になっている | 先にペリフェラルクロックを有効にする |
| ビットがランダムに反転する | ISR との RMW 競合 | BSRR 形式のアトミックレジスターまたはクリティカルセクションを使う |
| アクセス時に HardFault が発生する | アラインメントされていない、またはバスが保護されている | データシートに合わせてアクセス幅を指定する |
| 読み取りが最適化で省略される | volatile がない | volatile uint32_t を使う |
| ビットフィールドの値が間違っている | コンパイラーが予期しない形式でパッキングする | シフト/マスクマクロを使う |
関連スキル
skills/baremetal/peripherals-from-datasheet— レジスターマップの抽出skills/baremetal/gpio-baremetal— GPIO レジスターのパターンskills/low-level-programming/assembly-arm— インライン asm バリアskills/embedded/linker-scripts— ペリフェラルのメモリーマップ領域
コメント
Markdown 対応