皆さん、こんにちは。このブログでは、次の内容を扱います。
システム概要
上の図に示すように、このシステムは2つのメインブロックで構成されています。
a. iMX8QXP[PCIe Root Complexとして構成]
b. LS1028[PCIe Endpointとして構成]
ソフトウェアコンポーネント:
iMX8QXP - Linux Factory 6.6.36
LS1028ARDB - LSDK-18.09
ハードウェアコンポーネント:
iMX8QXP MEK Board
LS1028ARDB Board
M.2 Key E PCIe Bridge
Root Complex(RC)とEndpoint(EP)は、両方のボードのM.2 Key Eコネクタ上のPCIeブリッジ経由で接続されています。
両方のPCIe RCとEPで使用するリファレンスクロックは100MHzです。
PCIe Bridge with M.2 Key E interfaceM.2キーEインターフェースを備えたPCIeブリッジ
M.2 Key E PCIe Bridge
ユースケース
顧客要件として、キャッシュ可能および非キャッシュ可能なDRAMメモリ領域を対象に、Uboot上でPCIeからDDRへの転送をベンチマークするソフトウェアプログラムが必要になりました。
このベンチマークソフトウェアは、PCIe空間からDDRへのDMA転送を、次の手順で周期的に実行します。
1) PCIeからDDRへのDMA転送(非ディスクリプタ)を開始する。データはPCIeから非キャッシュ可能なバッファ(A)へ転送される。
2) DMA転送の完了を待つ。
3) 非キャッシュ可能バッファ(A)からキャッシュ可能バッファ(B)へ受信データをコピーする。
4) データの不一致がないか確認し、それに応じて報告します。
注:
DMA転送には2種類あります:ディスクリプタと非ディスクリプタです。
DescriptorベースのDMAでは、CPUはメモリ上に「ディスクリプタ」と呼ばれるリストまたはチェーンを用意します。各ディスクリプタは、ソース、デスティネーション、転送サイズなどの情報を含むデータ構造です。
非ディスクリプタDMAでは、CPUがDMAコントローラのレジスタを直接設定し、単一の特定のDMA転送に対するパラメータを定義します。
このユースケースでは非ディスクリプタDMA転送を使用します。
RCとEPは、Uboot上で上記のユースケースを実行します。iMX8QXPは、デフォルトでRCとして動作します。ただし、LS1028 PCIe2をEPとして構成するには、RCW[HOST_AGT_PEX] = 1を設定し、その変更済みRCWをボードにフラッシュしてからLS1028を起動する必要があります。
このユースケースを実装するには、次のようにします。
iMX8QXP側では、Ubootソースコード中のcmd/pci.cを変更して「pci」ユーティリティに独自の機能を追加します。パッチ適用後は、Ubootコンソールで「pci p」コマンドを実行するだけでテストケースを実行できます。
LS1028A側では、mwコマンドを使用してPCIeコントローラレジスタに書き込むだけで、このユースケース用のEPを設定します。
PCIeにおけるOutboundトランザクションとInboundトランザクションとは何か?
OutboundトランザクションとInboundトランザクションの概念を説明するにあたり、PCIeファブリック内を流れるトランザクションについて意識しておくべきエンティティが2つあります。
Initiator - リクエストを送信してトランザクションを開始するコンポーネントです。例:Memory Readリクエストはデータを要求するリクエストを送信します。
Completer - リクエストを受信し、最終的にレスポンスを返すコンポーネントです。例:Initiatorから送られてきたMemory Readリクエストを受信すると、要求されたデータを含むCompletion TLPをInitiatorに返送します。
PCIeにおけるトランザクションは、デバイス[Root complexまたはEnd-point]に対するデータフローの方向に応じて2つに分類できます。
Inbound - PCIeバスからデバイスへデータが入ってくる方向のトランザクションです。デバイスの観点では、他のデバイスが発行したこのリクエストに対して自分がCompleterとなります。例:Root complex[initiator]がEnd-point[completer]にMemory Write TLPを送信する場合、このときEP側はRCから入ってくるInboundトランザクションを受け取ることになります。
Outbound - デバイスからPCIeバスへデータが出ていく方向のトランザクションです。デバイスの観点では、自分がこのリクエストを発行するInitiatorであり、別のデバイスがそれを完了させます。例:End-point[Initiator]がRoot-complex[completer]にMemory Read TLPを送信する場合、このときEP側はRCに向かうOutboundトランザクションを発行していることになります。
ATUとは何か、PCIeでなぜ重要なのか
PCIeのTLPトランザクションではPCIeアドレスが使用されます。これらのアドレスは、CPUやメモリ、周辺回路間のオンチップ通信に使われるローカル内部バスアドレス[AXIやAHBなど]とは異なります。そのため、PCIeのアドレスをローカル内部バスのアドレスにマッピングするエンティティが必要になります。ここで登場するのがATUです。
ATUは、デバイスのアドレス空間とホストのアドレス空間との間のアドレスマッピングを担当するAddress Translation Unitです。これにより、デバイスはホストシステムのメモリやその他のリソースへアクセスできるようになります。ATUが必要となる代表的なシナリオを次に示します。
1. PCIeデバイス(ネットワークカードやストレージコントローラなど)がDMA経由でホストメモリにアクセスする必要がある場合。したがって、デバイスが発行するアドレスは、ホストがそれらを認識して処理できるようATUによって変換される必要があります。
2. PCIeデバイスがメモリマップドIOアドレスにアクセスしたい場合。この処理を行うには、デバイスが要求したアドレスをホストが理解できるアドレスに変換するためにATUが必要になります。
したがって、PCIeデバイスがDMAによるメモリの読み書きなどのアクセス要求を開始するたびに、ATUはデバイスが発行したアドレスを、以下の図に示すようにホストシステム側のアドレスへ変換します。
ATU TranslationATU変換
これでInbound/Outboundトランザクションが何であり、PCIeにおいてATUがどのように動作するかが分かったので、次にデバイス[RC/EP]がこれらのトランザクションを実行できるようにする方法について説明します。そのために、デバイスのPCIeコントローラ内でATU変換を設定し、InboundウィンドウとOutboundウィンドウを構成します。
iMX8QXPとLS1028でPCIeのOutboundウィンドウとInboundウィンドウをそれぞれどのように設定するか?
このユースケースでは、iMX8QXP[Root Complex]側にOutboundウィンドウを1つ、LS1028A[Endpoint]側にInboundウィンドウを1つ設定します。
a. iMX8QXPにOutboundウィンドウを設定するために、以下のレジスタを設定します。
iATU Index register-どのリージョンにアクセスするかと、その方向[Inbound/Outbound]を定義します。
iATU Region Control 1 Register - 最大ATUリージョンサイズやその他のTLP属性を設定します。
iATU下位ベースアドレスレジスタ
&
iATU Upper Base Address Register
これらのレジスタは、変換前のウィンドウの開始アドレスを設定します。
iATU Limit Address Register - 変換前のウィンドウの終了アドレスを設定します。
iATU 下位ターゲットアドレスレジスタ
&
iATU Upper Target Address Register
これらのレジスタは、変換後の開始アドレスと終了アドレスを設定します。
iATU Region Control 2 Register - REGION_ENビットを有効にし、マッチモードを選択します。
Outbound WindowOutboundウィンドウ
b. LS1028AにInboundウィンドウを設定する場合も、同じレジスタ群を設定します。
iATU Index Register
iATU Region Control 1 Register
iATU下位ベースアドレスレジスタ
iATU Upper Base Address Register
iATUリミット・アドレス・レジスタ
iATU下位ターゲットアドレスレジスタ
iATU上位ターゲットアドレスレジスタ
iATU領域制御2レジスタ
さらに、次のレジスタも設定します。
PCI Express Command Register - Bus masterビットを有効にします。
PEX PF0 CONFIG Register - Config Readyビットをセットします。EP側がコントローラの初期化完了をRCに知らせるために使用されます。
このユースケースを実行している間、LS1028[EP]は終始Ubootコンソール上で動作しているため、EP側でこれら2つの追加レジスタを設定する必要があります。Linux上で動作しているのであれば、PCIeドライバがこの設定を行ってくれたはずです。
Inbound WindowInboundウィンドウ
コードの解説
このブログにはパッチが添付されており、必要に応じて読者が内容を確認して利用できるようになっています。
注:このコードは決してプロダクションレベルのコードであることを意図したものではないため、読者の方は参考としてのみ利用してください。
Ubootパッチは、以下の処理を行います。
Ubootコンソールで「pci p」コマンドを実行すると、上記のシーケンスが開始されます。
Ubootソースにパッチを適用した後、Ubootバイナリをビルドし、次にSPLをビルドします。SPLをiMX8QXPにフラッシュします。
テストケースの実行
a. RC[iMX8QXP]とEP[LS1028ARDB]をM.2 Key.Eブリッジで接続し、RCとEPをUbootが起動するところまでブートします。
b. LS1028のUbootコンソールで、Inboundウィンドウを設定するために次のコマンドを実行します。
mw.l 0x3500900 0x80000001
mw.l 0x3500904 0x0
mw.l 0x350090c 0x0
mw.l 0x3500910 0x0
mw.l 0x3500914 0x07ffff
mw.l 0x3500918 0xA0000000
mw.l 0x350091c 0x0
mw.l 0x3500908 0xC0000000
mw.b 0x3500004 0x06
mw.l 0x35C0014 0x00001001
上記のレジスタは、次のセクションで説明したPCIe ATU設定の一部です。
iMX8QXPとLS1028でPCIeのOutboundウィンドウとInboundウィンドウをそれぞれどのように設定するか
レジスタアドレスとその意味については、LS1028Aのリファレンスマニュアルで確認してみてください。
c. RC側で「pci p」を実行すると、DMA転送のユースケースがトリガされます。
PCIeのInboundトランザクションとOutboundトランザクションについて、このブログではごく表面的に触れただけです。しかし、DMAを用いてメモリ転送をどのようにトリガできるかについて、生のイメージを示すことを目的としていました。質問があれば、DMを送るかコメント欄に投稿してください。
皆さん、こんにちは。このブログでは、次の内容を扱います。