VivadoにおけるPCIe DMAのデバック　その２


## さて、アプリを作りましょうか！
まずは、評価ライセンスでできることをおさらいです。
| 項目                  | ユーザーモード             | カーネルモード            |
| ------------------- | ------------------- | ------------------ |
| **作成可能か？**          | ✅ 可能                | ⚠️ 限定的に可能（制限あり）    |
| **ドライバビルド**         | Visual Studio のみでOK | WDK + VS が必要       |
| **動作制限**            | 2時間制限（再起動でリセット）     | テスト署名必須、配布不可       |
| **DriverWizard 対応** | 生成・使用ともに問題なし        | `.sys`は作成できるが注意点あり |
| **配布可能か？**          | ❌ 評価目的のみ            | ❌ 評価目的のみ、再署名不可     |

🧠 利点の比較
🟦 ユーザーモード（WinDriver API + wdclib.lib）
✅ 主な利点

    安全（クラッシュしてもOSに影響しない）

    Visual Studio 上で簡単に開発可能

    Jungo が用意した API (WDC_ReadAddr32() 等) を使うだけで完結

    カーネル知識不要

🚫 欠点

    割り込み応答のレイテンシが大きい

    精密なタイミング制御（μs単位）が困難

    特殊なアクセス（I/Oポート、割込みルーティング変更等）ができない

🟥 カーネルモード（Kernel Plugin kp_xdma.sys）
✅ 主な利点

    割り込み即応処理（ms以下）

    FPGAへの直接レジスタアクセスが可能

    DMAの高度な制御（たとえば、タイムアウト処理、バースト処理）も可能

    実運用に近い構成をシミュレート可能

🚫 欠点

    WDK（Windows Driver Kit）の導入と習熟が必要

    一歩間違うとOSクラッシュ（BSOD）

    評価ライセンスでは署名なしドライバとなり、テストモードでしか起動できない

    デバッグが困難（WinDbg + シンボル管理などが必要）

## 🚦 まとめ：評価ライセンスでの選択ガイド
| 条件                          | 推奨モード                    |
| --------------------------- | ------------------------ |
| FPGAとの**通信確認／DMA転送確認だけが目的** | ✅ ユーザーモードで十分             |
| 割り込み応答の正確性や**μsオーダの処理が必要**  | ⚠️ カーネルモードも検討（制限あり）      |
| 実運用想定、量産デバイスへの実装確認          | ❌ 評価ライセンスでは不十分（正式ライセンス要） |


今回はDMAの動作確認が目的なので２時間という制限が付きますが、ユーザーモードで！！！

# WinDriverが自動作成するプログラムに追加したものを検証
## BAR0 +0x10000～8KB空間の読み出し(ポーリング)

追加されたメニュー(11番が追加したメニューです。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3998154/1a947f19-6ecb-4e5d-82d7-b46f800b6f5b.png)

AXI(-Lite)の動き
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3998154/1fb9062d-9b08-4d5e-9783-05738b58a143.png)

BAR0は問題なく動作していることがわかります。

## BAR2(BYPASS) 64KB空間の読み出し
追加されたメニュー(12番が追加したメニューです。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3998154/8255caf9-12eb-449d-91e5-6d3aad89f81e.png)

AXIの動き
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3998154/b3af5592-bf0b-4d7f-a457-2bf9415351f0.png)
BAR2は問題なく動作していることがわかります。

## BAR1 DMA空間の読み出し(ポーリング)
BYPASS モードでは、DMA 転送の完了通知は以下の手段で行われます：

    割り込み：ユーザー割り込み（User Interrupt）を使用して、DMA 転送の完了をホストに通知します。

    ポーリング：ホストが定期的にステータスレジスタを読み取ることで、DMA 転送の完了を確認します。

BYPASS モードでは、DMA 転送の完了時にホストメモリ上の特定アドレスへ自動的にステータスを書き戻す（write-back）機能はサポートされていません。これは、BYPASS モードがソフトウェアによる制御を最小限に抑え、ハードウェアによる高速なデータ転送を目的としているためです。

## 🔍 両者の違い

| 種類 | 主な用途 | 動作 | 使用条件 |
|------|----------|------|-----------|
| **ステータスレジスタ**<br>(Status Register / Completed Descriptors Register) | DMAエンジンの現在の状態や完了したディスクリプタ数の確認 | BAR0上のアドレス空間をCPUがポーリングまたは読出し | 常時使用可能（BYPASS含む） |
| **Write-Backレジスタ**<br>(Write-Back Address Register / 自動書き戻し) | DMA転送完了時にホストメモリ上の**物理アドレスに自動書き込み** | 完了時、FPGAがAXI経由でホストメモリへ書き込み | **非BYPASSのみ対応**<br>（2023.1のXDMAでは非推奨） |

---

## ✅ Status Register は何か

**例：H2C Channel 0**

- `0x44`：Status Register（エラーなどのフラグ）
- `0x48`：Completed Descriptors Register（完了済みディスクリプタ数）

DMA が完了しているかを確認するには、`0x48` を polling して、expected 数と一致したら完了と判定する。

:::note warn
> **注意**  
> WinDriverが自動生成するサンプルやライブラリには、このステータスレジスタを使用するポーリング関数は含まれていません。したがって、BYPASSを使用している場合は自作する必要があります。

ソースはこんな感じになります。

```c++:xdma_diag.c
// ステータスレジスタをポーリングしてDMA完了を待つ関数（BYPASSモード用）
// 0x48 (Completed Descriptor Count) を polling し、expected数と一致したら完了と判定
static DWORD XDMA_DmaPollStatusCompletion(XDMA_DMA_HANDLE hDma)
{
    XDMA_DMA_STRUCT *pxdmaDma = (XDMA_DMA_STRUCT *)hDma;
    PXDMA_DEV_CTX pDevCtx = (PXDMA_DEV_CTX)WDC_GetDevContext(pxdmaDma->hDev);
    UINT32 completed = 0;
    DWORD offset;
    int timeout = 1000000; // 必要に応じて調整
    UINT32 expected = pxdmaDma->pDma->dwPages; // 転送要求数（ディスクリプタ数）

    // 0x48: Completed Descriptor Count レジスタ
    offset = XDMA_CHANNEL_OFFSET(
        pxdmaDma->dwChannel,
        pxdmaDma->fToDevice ? XDMA_H2C_CHANNEL_COMPLETED_DESC_COUNT_OFFSET : XDMA_C2H_CHANNEL_COMPLETED_DESC_COUNT_OFFSET
    );

    while (timeout-- > 0)
    {
        WDC_ReadAddr32(pxdmaDma->hDev, pDevCtx->dwConfigBarNum, offset, &completed);
        if (completed >= expected)
        {
            // 完了ディスクリプタ数が要求数に到達したら完了
            return WD_STATUS_SUCCESS;
        }
        // 必要に応じて短いsleepを入れる
    }

    printf("XDMA_DmaPollStatusCompletion: timeout (completed=%u, expected=%u)\n", completed, expected);
    return WD_TIME_OUT_EXPIRED;
}
```
:::
追加されたメニュー(13番が追加したメニューです。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3998154/2316b75b-597d-491e-b2ce-fadf0f8e4f3d.png)

AXI(Write Burst)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3998154/e75f2e7d-ffa9-46b6-be39-b61f765a7acb.png)

AXI(Read Burst)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3998154/34924614-42a9-4ff1-be43-0559a9c48462.png)

### 📌当然の注意点
生成されるサンプルプログラムには初期化処理などのケアが含まれていないため、初回の動作は問題なくても、2回目以降に正常に動作しないといった症状が発生することがあります。
この点については、適切な初期化やリセット処理を自前で実装する必要があります。試行に作成/使用したプログラムでは、これらの問題に対処済みです。

# まとめ
DMAポーリングではさほど苦労することなく構成ができました。
そのうち気が向いたら割り込みDMAをやるかもです。

このボード固有問題もあるので、気が向けば・・・。

※誤記等あるかもしれませんが、細かな指摘はご容赦ください。
※質問ありましたら、ご連絡ください。