Bluetoothモジュールがフィールドで接続を切断したり、スリープ時にスペックより3 mA多く消費したり、DTMスイープの40チャネル中2番目のチャネルで認証に失敗したりする場合、1日の修正と2週間の迷走の違いは、開発段階からモジュールを正しく計装していたかどうかにかかっています。本記事では、回路図設計の初日から組み込むべき物理デバッグインターフェース、リアルタイムトレーシング手法、RF検証パス、ポストモーテムツールについて解説します。

## 1. デバッグインターフェース概要:JTAG vs SWD vs cJTAG

最新のBLE SoC(nRF52/53、CC2640、ESP32-C3/H2、DA1469x、BGMxx)は、フル5ピンJTAG(TCK、TMS、TDI、TDO、nTRST)またはARM専用の2ピンSWD(SWCLK、SWDIO)+ nRESETを提供しています。BLEモジュール設計におけるトレードオフを以下の表にまとめます:

パラメータ JTAG (5ピン) SWD (2ピン+RESET) cJTAG (2ピン)
ピン数 5 (nTRSTなしで4) 2-3 2
最大クロック 10-50 MHz 1-50 MHz 最大50 MHz
トレースサポート ETM (4ピントレースバス) ITM (SWO、1ピン) ITM (SWO)
フラッシュ書き込み速度 (256 KB) 約8-12秒 約6-10秒 約6-10秒
配線長制限 約15 cm (バッファなし) 約30 cm (バッファなし) 約20 cm
SoCサポート 汎用 (ARM + RISC-V) ARM Cortex-Mのみ ARM Cortex-M33/55
典型的な電流 (デバッグ動作時) 2-5 mA 1-3 mA 1-3 mA

BLEモジュールではPCB面積が平方ミリメートル単位で測られるため、SWDがデフォルトの選択です。nRF52/53シリーズでは、開発中にSWDをGPIO P0.00/P0.01と同じピンで使用できます。0.05インチピッチのマイクロヘッダー(Samtec FTSH-105等)に配線し、開発用に実装するか、量産用にはむき出しのPCBパッドとして残します。量産モジュールに実装済みヘッダーがあると不正アクセスを招くため、パッドのみのアプローチが推奨されます。

### SWD信号品質の考慮事項

SWDIOは双方向でプルアップ(通常10 kΩ内部、4.7 kΩ外部)付きのオープンドレインです。プルアップ値は多くのエンジニアが予想する以上に重要です:

プルアップ SWDIO立ち上がり時間 (tr) 最大SWCLK 備考
100 kΩ 220 ns 1 MHz 限界的—同期ロスが時々発生
47 kΩ 104 ns 4 MHz 短い配線(<15 cm)でOK
10 kΩ 22 ns 25 MHz ほとんどのセットアップで信頼性あり
4.7 kΩ 10 ns 50 MHz J-Link高速動作で推奨
1 kΩ 2.2 ns >50 MHz 過剰、Low時に3.3 mA消費

計算式は単純なRC遅延:tr ≈ 2.2 × R × C、Cは配線+プローブの合計容量(15 cmリボンケーブルで通常10-20 pF)。J-Linkで断続的な”SWD DP read failed”エラーが出る場合、最初に確認すべきは外部プルアップが未実装で内部10 kΩが長いケーブルと戦っているかどうかです。

## 2. UARTデバッグコンソール:実世界のデバッグに向けた設計

UARTデバッグコンソールは量産ファームウェアで最も価値のあるデバッグインターフェースですが、 routinely不適切に実装されます。よくある間違い:

1. BLEスタック優先度に対してボーレートが高すぎる。 1 Mbps UARTで16バイトFIFOの場合、割り込みは128 μsごとに発生します。BLEスタックが接続イベント準備中に割り込みを150 μs保持すると、文字が失われます。解決策:DMA TXで460800 bpsまたは921600 bpsを使用し、RXはリングバッファで処理。

2. printfがメインループをブロック。 115200 bpsで200バイト書くブロッキングprintfは17.4 msかかります—7.5 msのBLE接続間隔より長いです。アイドル時のDMAフラッシュ付きロックフリーリングバッファを使用:

// ロックフリーUARTデバッグリングバッファ (単一プロデューサー、単一コンシューマー)
#define DBG_BUF_SIZE 2048
static volatile uint16_t dbg_head = 0, dbg_tail = 0;
static char dbg_buf[DBG_BUF_SIZE];

void dbg_printf(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    char tmp[256];
    int len = vsnprintf(tmp, sizeof(tmp), fmt, ap);
    va_end(ap);
    for (int i = 0; i < len; i++) {
        uint16_t next = (dbg_head + 1) & (DBG_BUF_SIZE - 1);
        if (next == dbg_tail) break;  // バッファフル、文字破棄
        dbg_buf[dbg_head] = tmp[i];
        dbg_head = next;
    }
    // TXアイドル時にDMAフラッシュトリガー
    if (!(UART0->STAT & UART_STAT_TXBUSY)) {
        uart_dma_flush();
    }
}

3. ログレベルフィルタリングなし。 量産モジュールが接続イベント中に460800 bpsでVERBOSEレベルログを出力するとタイミングジッターが発生します。コンパイル時と実行時のレベルフィルタリングを実装:

レベル コンパイルフラグ 実行時 典型的な用途
ERROR DBG_ERROR 常にオン ハードフォールト、アサーション失敗
WARN DBG_WARN 常にオン 再試行、性能低下
INFO DBG_INFO 設定可能 接続/切断、状態変化
DEBUG DBG_DEBUG 量産でオフ イベントごとのタイミング、パケットダンプ
VERBOSE DBG_VERBOSE 量産でオフ スロットごとのスケジューリング、RSSI

4. バイナリクラッシュダンプ機能なし。 モジュールがハードフォールトを起こした時、UARTが唯一の命綱です。ウォッチドッグリセット前にスタックレジスタ(R0-R3、R12、LR、PC、xPSR)とスタックの最初の16ワードをダンプする1 KBのクラッシュダンプハンドラを事前確保:

void HardFault_Handler(void) {
    uint32_t *stk;
    __asm volatile("tst lr, #4 
"
                   "ite eq 
"
                   "mrseq %0, msp 
"
                   "mrsne %0, psp 
" : "=r"(stk));
    dbg_printf("
*** HARD FAULT ***
");
    dbg_printf("R0:  0x%08X  R1:  0x%08X  R2:  0x%08X  R3:  0x%08X
",
               stk[0], stk[1], stk[2], stk[3]);
    dbg_printf("R12: 0x%08X  LR:  0x%08X  PC:  0x%08X  PSR: 0x%08X
",
               stk[4], stk[5], stk[6], stk[7]);
    dbg_printf("BFAR: 0x%08X  CFSR: 0x%08X  HFSR: 0x%08X
",
               SCB->BFAR, SCB->CFSR, SCB->HFSR);
    for (int i = 0; i < 16; i++) {
        dbg_printf("SP+%02d: 0x%08X
", i*4, stk[i]);
    }
    while(1);  // ウォッチドッグ待機
}

### BLEモジュールのUARTピン選択

P0.00/P0.01(SWDまたは32 kHz水晶用予約)、P0.02/P0.03(ADCやアナログ)、BLEスタックが無線タイミングに使用するピンは避けてください。主要SoCの安全なUARTピン:

SoC 安全なTX 安全なRX 回避
nRF52832 P0.06 P0.08 P0.00/01 (XL1/XL2), P0.09 (NFC)
nRF52840 P1.08 P1.10 P0.00/01, P0.09/10 (NFC), P0.24-25 (USB)
ESP32-C3 GPIO2 GPIO3 GPIO0 (boot), GPIO2 (strapping)
CC2640R2 DIO2 DIO3 DIO0-1 (boot), DIO7-8 (JTAG)
DA1469x P0_06 P0_07 P0_00 (XTAL32M), P0_01-03 (SWD)

## 3. リアルタイムトレーシング:RTT vs ITM/SWO vs ETM

printfベースのUARTデバッグは低頻度イベントには適していますが、BLEスタックのタイミング分析にはサブマイクロ秒の分解能が必要です。3つのオプションがあり、それぞれ異なるトレードオフがあります:

### Segger RTT (Real-Time Transfer)

RTTはターゲットRAMのリングバッファをデバッガがSWD経由で高速ポーリングします。ログエントリごとのターゲットCPU時間ゼロ(デバッガがRAMを直接読み取り)で、タイミングクリティカルなコードに最適:

指標 UART @ 460800 RTT ITM/SWO @ 2 MHz
100文字ログあたりのCPU時間 約2.2 ms 約5 μs 約50 μs
スループット 46 KB/s 約500 KB/s 約200 KB/s
レイテンシ (ログ→デバッガ) 2.2 ms 約10 μs 約25 μs
必要ピン数 2 (TX/RX) 0 (SWD使用) 1 (SWO) + SWD
RAM内バッファ 0 1-4 KB 0 (ハードウェアFIFO)
デバッガサポート 任意のターミナル J-Linkのみ J-Link, ST-Link, CMSIS-DAP

RTTの主な利点は既存のSWDインターフェースで追加ピン不要で動作することです。主な制限はSegger J-Link(またはJ-Linkファームウェア互換デバッグプローブ)が必要なことです。開発には問題ありませんが、フィールドデバッグではJ-Linkを持っていない可能性があります。

RTTは双方向通信もサポート—デバッガはUARTピンを使用せずにターゲットにコマンドを注入できます:

#include "SEGGER_RTT.h"

void rtt_command_loop(void) {
    char cmd[64];
    int len = SEGGER_RTT_Read(0, cmd, sizeof(cmd) - 1);
    if (len > 0) {
        cmd[len] = '';
        if (strcmp(cmd, "stats") == 0) {
            print_ble_stats();
        } else if (strcmp(cmd, "scan") == 0) {
            start_debug_scan(100);  // 100 msスキャンウィンドウ
        } else if (strcmp(cmd, "conn") == 0) {
            print_connection_info();
        }
    }
}

### ITM/SWOトレーシング

ITM(Instrumentation Trace Macrocell)はARM Cortex-M3/4/7/33の機能で、アプリケーションレベルのトレーシング用に32の刺激ポートとタイムスタンプ、例外トレーシングを提供します。SWO(Serial Wire Output)ピンがITMデータをCPUクロック速度までのシングルワイヤUARTとして伝送:

// デバッグメッセージ用ITM刺激ポート0
#define ITM_Port8(n)  (*((volatile uint8_t *)(0xE0000000 + 4*n)))
#define ITM_Port32(n) (*((volatile uint32_t *)(0xE0000000 + 4*n)))

void itm_printf(char *s) {
    while (*s) {
        while (ITM_Port8(0) == 0);  // 刺激ポートready待ち
        ITM_Port8(0) = *s++;
    }
}

SWOは2つのモードで設定可能:

モード SWOボーレート クロックソース 利点 欠点
UARTモード 2-50 MHz HCLK / プリスケーラ 任意のSWO対応プローブで動作 ボーレートを正確に一致させる必要
Manchesterモード 最大CPUクロック HCLK直接 自己クロッキング、ボーレート一致不要 J-Linkのみ、ST-Link非対応

BLEタイミング分析では、ITMはイベントごとのタイムスタンプログに優れています。ITMハードウェアは各書き込みに1サイクル分解能のカウンタでタイムスタンプを付けるため、CPUオーバーヘッドなしで接続イベントのタイミングを測定できます:

// ITMポート1で接続イベントタイミングをログ
void on_conn_event(uint16_t handle, int8_t rssi, uint32_t elapsed_us) {
    ITM_Port32(1) = handle;
    ITM_Port32(1) = (uint32_t)rssi;
    ITM_Port32(1) = elapsed_us;
}

J-Link SWO ViewerまたはSystemViewで、これらのイベントをナノ秒精度のタイムスタンプ付きでタイムライン表示できます。

### ETM (Embedded Trace Macrocell)

ETMは4ビット並列トレースバス経由でフル命令レベルのトレーシングを提供します。Cortex-M7(および一部のM4)で利用可能ですが、4-7本の追加ピンとTrace Only以上のデバッグコネクタが必要です。BLEモジュール用途ではETMが正当化されることは稀です—ピンコストが高すぎ、プロトコルレベルのデバッグには命令レベルのトレースがデータ量すぎて役立ちません。デュアルコアモジュール(nRF5340)のキャッシュミス分析には有用ですが、ほとんどのBLE作業ではITM/SWOがより良い選択です。

## 4. BLEタイミングのロジックアナライザ手法

ロジックアナライザはソフトウェアベースの計装では見えないBLEタイミング問題のデバッグに不可欠です。主なユースケース:

### 4.1 BLEスタックイベント検証

ほとんどのBLEスタックは各無線アクティビティの開始と終了でトグルする「ラジオイベント」または「タイマー比較」GPIOを提供します。nRF52ではRADIO_SHORTSまたはTIMER0比較、ESP32ではBT_CONTROLLERイベントシグナルです。ラジオイベントでトグルする1つのデバッグGPIOを設定すると以下を測定できます:

測定項目 方法 何が分かるか
接続イベント開始ジッター ラジオGPIO to GPIO-PPSオフセット測定 水晶精度、スリープタイマードリフト
接続イベント持続時間 ラジオGPIOのパルス幅 ペイロードサイズvs間隔の不一致
スキャンウィンドウタイミング スキャンGPIOのパルス幅 実際のスキャンvs設定スキャン
アドバタイズ間隔精度 周期測定 ドリフト、ランダム化の正確性
イベント間ギャップ 立ち下がりエッジから次の立ち上がりエッジまでの時間 スタックオーバーヘッド、処理時間

### 4.2 SWIRE/HCIスニッフィングによるプロトコルデコード

ほとんどのBLEモジュールはホストとコントローラ間のHCIトラフィックを内部でルーティングしますが、一部はUARTまたはSPIインターフェースで外部に公開します。ロジックアナライザのプロトコルデコーダでこのトラフィックをスニッフィングするのが、ホスト-コントローラ間相互作用問題をデバッグする最速の方法です:

UART HCI: Saleae Logic 2にはHCI UARTデコーダが含まれ、コマンド完了、コマンドステータス、ACLデータ、イベントを解析します。

SPI HCI: 一部のモジュール(外部ホスト付きCC26xx)はHCIに4線SPIを使用します。

プロプライエタリ: NordicのプロプライエタリSWIREはシングルワイヤ双方向プロトコルです。

### 4.3 ロジックアナライザ+オシロスコープによる電力プロファイリング

最も一般的なBLEデバッグシナリオ:モジュールが予想より多くの電流を消費する。ロジックアナライザを主要GPIOに接続し、シャント抵抗+オシロスコープまたはPower Profiler Kitで電流測定と並行して行い、ソフトウェアイベントと電流スパイクを相関させます:

GPIOシグナル 電流プロファイルで確認すべき点
ラジオアクティブ 4-15 mAスパイク、通常イベントで1-5 ms
CPUアクティブ 2-8 mA、イベント間で<10 μAに低下すべき
フラッシュ消去/書き込み ページあたり8-15 mAスパイク、20-50 ms継続
UART TX 送信継続時間中1-3 mA
ADCサンプル 0.5-1.5 mA、サンプルレートで短いスパイク
外部センサー読み取り (I2C/SPI) センサーにより1-5 mA

よくある発見:nRF52のBLEスタックのapp_timerモジュールがラジオイベントの200 μス前にソフトウェアタイマーを発火させ、CPUを1.5 ms間起こして準備します。アプリケーションも同時にタイマーをスケジュールしている場合、CPUがより長く起き続け、平均電流が2-4 mA追加されます。ロジックアナライザの相関でこれが即座に可視化されます。

## 5. RFデバッグ:ダイレクトテストモード (DTM)

DTMは完全なスタックを通さずにBLE無線をテストする標準化(BT 4.2+ Vol 6, Part F)された方法です。以下が可能です:

1. 特定の周波数と電力で一定キャリア(CW)を送信

2. PRBS9/PRBS15ペイロードで変調信号を送信

3. 設定可能なパラメータでパケットを受信してカウント

4. パケットエラーレート(PER)vs RSSIを測定

### DTMアクセス方法

方法 インターフェース コマンド 典型的な用途
2線UART RX/TXピン HCIベンダー固有 量産テスト、認証
3線UART RX/TX/RTS 2線と同じ 高レートテスト用フロー制御
HCI over USB USB HCIコマンド USBドングルテスト
ベンダープロプライエタリ SWD/JTAG SoC固有 SoCレベルデバッグ (ファームウェア不要)

### DTMコマンド構造 (2線UART)

DTMは19200 bps (8N1)でシンプルな16ビットコマンドワードを使用:

ビット フィールド 説明
15 Type 0 パケットタイプ
14-6 Length 0-37 パケット内のバイト数 (RX) またはTX長
5-2 RF Channel 0-39 BLEチャネル 0-39 (2402-2480 MHz)
1-0 Test Type 0-3 0=PRBS9 TX, 1=PRBS15 TX, 2=PRBS9 RX, 3=CW TX

例:チャネル19 (2440 MHz)でPRBS9送信、32バイト:

– コマンドワード = 0b0_00001000_010011_00 = 0x044C

– 2バイトで送信:0x4C, 0x04 (リトルエンディアン)

// RF検証用DTMテストシーケンス
void dtm_verify_rf(void) {
    // 1. チャネル19でPRBS9送信、32バイト
    dtm_send(0x044C);
    delay_ms(100);  // テスターがキャプチャ
    dtm_send(0x0000);  // テスト終了、イベント読み取り

    // 2. チャネル19でRXテスト
    dtm_send(0x044E);  // bits: 0_00001000_010011_10
    delay_ms(500);     // 500ms間パケット収集
    dtm_send(0x0000);  // テスト終了
    uint16_t event = dtm_read_event();
    uint16_t pkt_count = event & 0x7FFF;
    dbg_printf("DTM RX: %d packets in 500ms
", pkt_count);
}

### DTMによる認証プレコンプライアンス

モジュールを認証ラボに送る前に、以下のDTMテストがパスすることを確認:

テスト DTMコマンド 合格基準 必要機器
TX電力 (全40ch) チャネルごとにCW TX スペックの±2 dB スペクトラムアナライザまたはパワーメータ
変調特性 PRBS15 TX Δf1avg = 225-275 kHz, Δf2avg/Δf1avg ≥ 0.8 スペクトラムアナライザ + ファームウェア
キャリア周波数オフセット CW TX 公称値から±50 kHz スペクトラムアナライザ (周波数カウンタモード)
帯域内エミッション PRBS9 TX, 1ch 隣接chで< -20 dBm スペクトラムアナライザ
20 dB帯域幅 PRBS15 TX < 2.0 MHz スペクトラムアナライザ (RBW 100 kHz)
PER感度 RXテスト -93 dBm (LE 1M)で< 30.8% PER 信号発生器 + DTM RX
PER最大入力 RXテスト -20 dBmで< 30.8% PER 信号発生器 + DTM RX
同チャネル拒否 RX + 干渉波 希望波/干渉波 = +3 dBで< 30.8% PER 信号発生器2台

## 6. デバッグGPIO戦略:開発から量産まで

よく設計されたBLEモジュールはデバッグ専用に2-4個のGPIOを割り当て、開発ビルドと量産ビルドで機能を切り替えるべきです:

### デバッグGPIO割り当て例 (nRF52832モジュール)

GPIO 開発機能 量産機能 備考
P0.31 ラジオイベントトグル 未使用 (入力、プルダウン) ラジオ開始/停止でトグル
P0.30 タイマイベントトグル 未使用 (入力、プルダウン) アプリタイマー発火でトグル
P0.29 BLE状態マシン状態 未使用 (入力、プルダウン) 3ビット状態コード (2 GPIO)
P0.28 BLE状態マシン状態 未使用 (入力、プルダウン) 3ビット状態コード (2 GPIO)

状態マシンGPIOは現在のBLE状態をバイナリ値としてエンコード:

状態コード 意味 典型的な持続時間
000 IDLE/スリープ 時間の90-99%
001 アドバタイズ準備 50-100 μs
010 アドバタイズTX 80-376 μs
011 アドバタイズRX (スキャンレスポンス) 100-200 μs
100 接続準備 100-200 μs
101 接続イベントTX 80-200 μs
110 接続イベントRX 100-300 μs
111 フラッシュ/処理 可変

これにより、ソフトウェアオーバーヘッドなしでロジックアナライザ上でリアルタイムの状態マシンビューが得られます—GPIO書き込みは割り込みコンテキストで行われ、それぞれ2-3サイクルかかります。

### 量産デバッグピンストラッピング

量産ではデバッグヘッダーを実装する余裕は通常ありません。ポゴピンフィクスチャでアクセス可能なテストパッドを使用するのが一般的な戦略:

パッド シグナル ポゴピン直径 備考
1 SWDIO 0.68 mm 0.05″ピッチ、2×5パターン
2 SWCLK 0.68 mm
3 GND 0.68 mm 2x GNDパッド
4 VDD 0.68 mm ターゲット電源センス
5 nRESET 0.68 mm オプション
6 UART TX 0.68 mm 量産コンソール
7 UART RX 0.68 mm 量産コンソール
8 RF_OUT 0.68 mm 伝導テスト (アンテナなしの場合)

アンテナ内蔵モジュールの場合、RFテストには開発中にRFコネクタ(U.FL等)または量産で結合フィクスチャ(TEMセルまたは近接磁界プローブ)が必要です。結合アプローチは±3-5 dBの不確定性を加え、量産のgo/no-goテストには許容範囲ですが、認証には不十分です。

## 7. クラッシュダンプとポストモーテム分析

BLEモジュールがフィールドでクラッシュした場合、これまでのデバッグインターフェースは使用できません。リセット後も生き残る自己完結型クラッシュダンプメカニズムが必要です:

### 7.1 保持RAM (リセットなし) クラッシュダンプ

ARM Cortex-Mはソフトリセット(NVIC_SystemReset)後もRAMを保持します。既知のRAMアドレスにマジックナンバーを配置することで、ブートローダーがクラッシュを検出し、保存されたコンテキストをダンプできます:

// ソフトリセット後も保持される既知のRAMセクションに配置
#define CRASH_MAGIC_ADDR  0x20007FF0  // RAM最後の16バイト
#define CRASH_MAGIC       0xDEAD5F70

typedef struct {
    uint32_t magic;
    uint32_t r0, r1, r2, r3, r12, lr, pc, psr;
    uint32_t cfsr, hfsr, bfar;
    uint32_t uptime_ms;
    uint16_t conn_handle;
    uint8_t  ble_state;
    uint8_t  reserved;
} crash_dump_t;

// ブートローダー内 (メインアプリ前に実行):
crash_dump_t *cd = (crash_dump_t *)CRASH_MAGIC_ADDR;
if (cd->magic == CRASH_MAGIC) {
    // 前回のクラッシュを検出、UARTまたはフラッシュにダンプ
    uart_dump_crash(cd);
    flash_store_crash(cd);
    cd->magic = 0;  // 再ダンプを防ぐためクリア
}

### 7.2 フラッシュベースクラッシュログ

クラッシュダンプが取得される前に電源が切れる可能性のあるモジュールでは、専用フラッシュページにクラッシュデータを保存します。1クラッシュにつき1エントリの循環ログを使用:

フィールド サイズ 説明
Magic 4 B 0xDEAD5F70
リセット要因 4 B RCUリセットレジスタ
スタックレジスタ 32 B R0-R3, R12, LR, PC, xPSR
CFSR/HFSR/BFAR 12 B フォールトステータスレジスタ
稼働時間 4 B 起動からのms
接続ハンドル 2 B アクティブ接続ハンドル (0xFFFF = なし)
BLE状態 1 B クラッシュ時の状態
予約 1 B アライメント
CRC16 2 B 整合性チェック
合計 62 B 1フラッシュ書き込みユニットに収まる

4 KBフラッシュページで65のクラッシュエントリを格納できます。実際には10-20エントリで十分です—モジュールが20回以上クラッシュして取得されていない場合、クラッシュパターンは体系的であり、最初の数エントリで診断できます。

### 7.3 ウォッチドッグタイムアウト分析

最もフラストレーションのたまるクラッシュタイプ:ウォッチドッグが発火するがHardFaultがないため、クラッシュダンプが空です。ウォッチドッグタイムアウトを診断するには:

1. タイマーISRスナップショット: ウォッチドッグフィード関数で現在のコールスタック(LR値)を保持RAMに保存。ウォッチドッグ発火時、最後のフィード位置が保存されます。

2. 定期ハートビートログ: 30秒ごとにタイムスタンプ付きハートビートをフラッシュに書き込み。ウォッチドッグリセット後、最後のハートビートでモジュールが応答を停止した正確な時刻が分かります。

3. ウォッチドッグ事前警告: 実際のリセット500 ms前に早期警告割り込みを発火するようウォッチドッグを設定。ISR内で現在のスタックポインタと数フレームをダンプ:

void WDT_IRQHandler(void) {
    // 早期警告 — リセット500ms前
    uint32_t *sp;
    __asm volatile("mrs %0, psp" : "=r"(sp));
    crash_dump_t *cd = (crash_dump_t *)CRASH_MAGIC_ADDR;
    cd->magic = CRASH_MAGIC;
    cd->pc = sp[6];  // PSPスタックフレームからのリターンアドレス
    cd->lr = sp[5];
    cd->cfsr = SCB->CFSR;
    cd->uptime_ms = system_uptime();
    cd->ble_state = ble_get_state();
    while(1);  // 保持RAMに保存、ウォッチドッグが500msでリセット
}

## 8. デバッグセキュリティ:ロックビット、セキュアデバッグ、量産強化

デバッグインターフェースはセキュリティ上の脅威です。量産モジュールにSWDヘッダーが実装されていると、$10のCMSIS-DAPプローブを持つ誰でもファームウェアを読み取り、キーを抽出し、モジュールをクローンできます。多層防御アプローチ:

### 8.1 ロックビットレベル

保護レベル メカニズム ブロックするもの 復帰
レベル0 (オープン) なし なし N/A
レベル1 (フラッシュロック) APPROTECTレジスタ SWDによるフラッシュ/RAM読み書き アンロックにフル消去
レベル2 (永久) ヒューズ/OTPビット 全デバッグアクセス、永久 なし — 不可逆

nRF52の場合、UICR.APPROTECT = 0xFFFFFF00でレベル1を有効化。デバッグプローブは接続できますがフラッシュを読めません—フル消去のみ可能。レベル2は一部SoC(ESP32-C3 efuse焼き込み、STM32 RDP Level 2)で利用可能で、デバッグアクセスを永久に無効化します。

### 8.2 セキュアデバッグ (認証デバッグ)

一部SoC(nRF5340、セキュアブートローダー付きCC2640R2)は認証デバッグをサポートし、デバッグプローブがデバッグアクセスを得る前に暗号学的チャレンジレスポンスを提示する必要があります:

// nRF5340ネットワークコア: セキュアデバッグチャレンジ
typedef struct {
    uint8_t  challenge[16];   // ターゲットからのランダムノンス
    uint8_t  signature[64];   // デバッグ証明書からのECDSA-P256署名
    uint32_t cert_id;         // 使用するデバッグ証明書
} secure_debug_auth_t;

これにより、セキュリティを永久に妥協することなく、フィールドでのデバッグアクセス(認証されたサービス担当者向け)が可能になります。トレードオフは暗号ライブラリ用の追加フラッシュ(8-12 KB)とやや複雑なデバッグセットアップです。

### 8.3 量産デバッグ戦略

フェーズ APPROTECT UARTコンソール デバッグGPIO DTM
ブリングアップ (EVT) オフ オン、460800 全実装 SWD経由
検証 (DVT) オフ オン、115200 全実装 SWD経由
パイロット (PVT) レベル1 オン、115200 (テストパッド) シグナルパッド2個のみ テストフィクスチャ経由
量産 レベル1 オフ (不良品はテストパッド) なし テストフィクスチャのみ
フィールド返品 レベル1 リワークで有効化 リワーク リワーク

基本原則:量産で不要なインターフェースはすべて実装除去(ヘッダー)、無効化(UARTはコンパイルフラグ)、または保護(SWDはAPPROTECT)すべきです。フィールド返品はデバッグアクセスを再有効化するために物理的リワーク(ヘッダーのはんだ付け)を要求すべきです—これにより軽微なプロービングを防ぎつつ故障分析は可能です。

## 9. よくあるデバッグアーキテクチャの間違い

間違い 影響 修正
SWDIOにプルアップなし 断続的プローブ接続、ランダムな”DP read failed” 4.7-10 kΩ外部プルアップ追加
UART TXがBLEスタックDCXと同じピン 無線設定の乱れ、ランダムな接続切断 安全なピンにUARTを移動 (セクション2表参照)
非保持RAMのRTTバッファ スリープ/ウェイク後RTTデータ消失 保持RAMセクションにRTTバッファ配置
ラジオISR内printf 2+ msのISRレイテンシ、パケットロス RTTを使用またはアイドルタスクに遅延
保持RAMにクラッシュダンプなし リセット後診断データなし セクション7.1のクラッシュダンプを実装
量産でデバッグGPIOアクティブ 追加電流、浮遊入力1つあたり50-200 μA 量産ビルドでプルダウン入力として設定
フルファームウェア書き込みなしでDTMアクセス不可 アプリ消去なしでRFテスト不可 ブートローダーがブート時にDTMピンストラップをチェック
出荷ファームウェアでAPPROTECT未設定 $10プローブでファームウェア抽出可能 量産フラッシュスクリプトでUICR.APPROTECTを設定
BLEスタックに対してUARTボーレートが高すぎる 接続イベント中の文字欠落 DMA TXを使用または16バイトFIFOで115200に制限
ウォッチドッグ事前警告ISRなし WDTタイムアウトを診断不可 リセット500ms前にWDT早期警告割り込みを有効化

## 10. 新規モジュール設計のデバッグインターフェースチェックリスト

BLEモジュールPCBをテープアウトする前に以下を確認:

– [ ] SWDパッド (SWDIO, SWCLK, nRESET, VDD, GND) が0.05″マイクロヘッダーまたはテストパッドとしてアクセス可能

– [ ] SWDIOに4.7-10 kΩ外部プルアップ (内部に依存しない)

– [ ] UART TX/RXが安全なピン (水晶、NFC、BLEスタックと競合しない)

– [ ] UART DMA TX設定、RXリングバッファ

– [ ] コンパイル時ログレベルフラグ (DBG_ERROR/WARN/INFO/DEBUG/VERBOSE)

– [ ] 2-4デバッグGPIO割り当て、コンパイルフラグで設定可能

– [ ] HardFaultハンドラが保持RAM + UARTにスタックレジスタをダンプ

– [ ] 保持RAMのクラッシュダンプマジックナンバー、ブート時にブートローダーがチェック

– [ ] フラッシュクラッシュログ (循環、10-20エントリ)

– [ ] スタックスナップショット付きウォッチドッグ事前警告ISR

– [ ] 2線UARTでDTMアクセス可能 (量産はテストパッド)

– [ ] 伝導DTMテスト用RFテストパッドまたはU.FLコネクタ

– [ ] 量産フラッシュスクリプトのAPPROTECT設定

– [ ] 量産ファームウェアがDBG_INFOのみでコンパイル (DEBUG/VERBOSEなし)

– [ ] 量産ビルドでデバッグGPIOが入力プルダウン設定

## まとめ

デバッグインターフェース設計は後付けではなく、フィールド故障の診断速度、認証パス、ファームウェア反復速度を決定する第一級のエンジニアリング規律です。投資は控えめです:2-4個の追加GPIO、5パッドのSWDフットプリント、保持RAMクラッシュダンプハンドラ、DTMブートモード。リターンはブリングアップ中に節約される週数と、フィールド返品の推測を回避して節約される数千ドルで測られます。次のBluetoothモジュール設計では、最初のバグレポートからではなく、回路図レビューからこれらのインターフェースを計上してください。