1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

中古Kintex-7 PCIeカード(XC7K480T)にMicroBlazeを載せて、開発環境からLED・DDR3・温度を動かすまで【FPGA×PCIe実践 P-1】

1
Last updated at Posted at 2026-07-28

この記事の前提

  • 対象は公式評価ボードではない中古/リバースエンジニアリング由来のPCIeカードです。board files(ボード定義)や配線情報はコミュニティの成果を利用しています
  • 環境は Vivado / Vitis 2023.2。手順・値は執筆時点のものです
  • 今回のゴールは「開発環境(Vitis+JTAG)から、カード上の周辺を自分の手で動かす」ことです。PCIeホストからのDMA転送は今回まだやっていません(=次回以降の展望)

はじめに

きっかけは単純で、技術的な興味だけでPCIe DMAの練習がしたかったからです。探してみると、1万円程度で遊べる中古カードが見つかったので購入しました。もう一つの理由は、普段の実機がCycloneシリーズばかりだったので、久しぶりにXilinx(現AMD)系を触ってみたかったという単純な動機もあります。

Kintex-7 XC7K480T を積んだPCIe x8カードを中古で入手しました。もとは特定用途の演算カードですが、コミュニティがリバースエンジニアリングした board files(tiferkingさんのリポジトリ)のおかげで、Vivadoから普通の開発ボードのように扱えます。

今回やったのは、このカードに MicroBlaze(ソフトCPU) を載せた小さなSoCを作り、開発環境から次の3つを動かすことです。

  1. LEDを点ける(AXI GPIO)
  2. DDR3を読み書きする(MIG+メモリテスト)
  3. 温度センサを読む(I2CのLM73)

操作は、JTAG経由でVitisのTCF Debug Terminalにつないで行います。実際の標準入出力は、MicroBlaze Debug Module(MDM)が持つJTAG UARTです。地味に見えますが、これで「FPGAにCPUを載せて、DDR3・GPIO・I2Cを一通り叩く」という組み込みの基礎体力が、PCIeカードという少し変わった土俵で手に入りました。

このbring-upで参考にした資料

中古で入手したKintex-7 XC7K480T搭載PCIeカード

このカードは何者か

分かっている範囲の構成です。

項目 内容
FPGA Kintex-7 XC7K480T(-2FFG1156I)
ホスト I/F PCI Express x8
メモリ DDR3(今回のVitis platformでは2チャネル×2GBとして認識)
不揮発 NOR Flash
その他 JTAG、GPIO接続のLED×3、温度センサ(LM73)、200MHz差動リファレンス

本記事では読みやすさを優先してGB/KBと表記します。アドレス計算やテスト範囲の算出は、2のべき乗単位で扱っています。

board filesは、リポジトリのypcb003381p1フォルダを Vivado/<version>/data/boards/board_files/ へコピーすると、新規プロジェクト作成時に「YPCB-00338-1P1 Accelerator Card」として選べるようになります。公式評価ボードではないので、MIGの設定やピン割当は実チップ・実配線に合わせて確認する前提です(EBAZ4205と同じ「正常が保証されていない」立ち位置)。

Vivadoのプロジェクトサマリー(Board Part・Project part。画面下のDesign Runsの数値は作業途中のスナップショットのため、本文中のWNS値とは対応しません):
Vivadoプロジェクトサマリー(Board Part情報)

全体像:載せたMicroBlaze SoC

作ったブロックデザインの地図です。中心にMicroBlaze、そこにAXIインターコネクト経由で周辺をぶら下げています。

P-1で確認した経路:MicroBlazeからLED・DDR3・温度センサまで

この図では、色の付いた実線を今回確認できた経路、灰色の破線をまだ評価していないPCIeホスト側の経路として分けています。まず概念図で位置関係をつかみ、次のVivado画面で実際のIP構成を確認します。

Default View(ブロック単位の全体配置)
Vivadoブロックデザイン(Default View)

Interface View(AXI接続関係を中心に整理した表示)
Vivadoブロックデザイン(Interface View)

Sourcesタブのヒエラルキー表示(axi_gpio・axi_iic・mig・microblaze等の実体が並ぶ)
Vivado Sourcesタブ(Hierarchy表示)

  • MicroBlaze:ソフトCPU。Vitisのアプリはここで動く
  • AXI GPIO → LED×3
  • MDM(MicroBlaze Debug Module) → JTAG UARTによるキー入力・print()出力
  • MIG(7-series) → DDR3コントローラ(今回は133MHz系)
  • AXI IIC → 温度センサLM73(I2Cアドレス0x4A)
  • XDMA(PCIe):デザインには入れてあるが、ホストからのDMAは今回未使用(次回)

C-9で見たCPU+FPGA SoC(PS/PL)の話が、今回は「FPGAに自分でCPU(MicroBlaze)を立てる」形で再現されます。AXIでレジスタを叩き、MIG経由でDRAMを触る構図はそっくりです。

開発環境

  • Vivado 2023.2:ブロックデザイン作成+bitstream生成(YPCB_00338_1P1_systest
  • Vitis 2023.2:MicroBlaze上のアプリ開発(YPCB_00338_1P1_App
  • JTAG:Xilinx USBケーブル(D-2で触れたFPGA用JTAG)
  • TCF Debug Terminal:JTAG経由でprint()xil_printf()の出力を表示し、キー入力も受ける

Step 1:LEDを点ける(AXI GPIO)

まずは生存確認、LEDです。アプリはUARTのキー入力を待ち、r/g/yで各色をトグルします。

// LED定義(memorytest.cより)
#define LED_R 0x01
#define LED_G 0x02
#define LED_Y 0x04

// キー入力でトグル
case 'r':
    led ^= LED_R;
    XGpio_DiscreteWrite(&GpioLed, LED_CH, led);
    break;

GPIOは出力方向に初期化してから使います。

XGpio_SetDataDirection(&GpioLed, LED_CH, 0x0);  // 全ビット出力
XGpio_DiscreteWrite(&GpioLed, LED_CH, 0x0);     // 消灯で開始

TCF Terminalでrを押すと赤が点く。これが、このカードで最初に得られる「生きている」の実感でした。

Step 2:DDR3を読み書きする(メモリテスト)

本命のDDR3です。mキーで、Xilinxのメモリテスト(Xil_TestMem)を各DDR領域に対して走らせます。

// 32bit MicroBlaze+32bit超のアドレスでは、アドレスを下位/上位へ分けて渡す
status = Xil_TestMem32(
    range->base & LOWER_4BYTES_MASK,
    (range->base & UPPER_4BYTES_MASK) >> 32,
    1024, 0xAAAA5555, XIL_TESTMEM_ALLMEMTESTS);

// Xil_TestMem16は2048要素、Xil_TestMem8は4096要素を同様にテスト

XIL_TESTMEM_ALLMEMTESTSは、インクリメント、ウォーキング1/0、アドレス反転、固定パターンを一通りかけるモードです。実行するとTCF Terminalには次のような出力が得られます。

Testing memory region: mig_7series_0_c0_memaddr
    Memory Controller: mig_7series_0
         Base Address: 0x0000000100000000
                 Size: 0x80000000 bytes
          32-bit test: PASSED!
          16-bit test: PASSED!
           8-bit test: PASSED!
Testing memory region: mig_7series_0_c1_memaddr
         Base Address: 0x0000000180000000
          ...
Successfully ran Memory Test Application.

実際のVitis TCF Debug Terminalでの実行画面(メモリテストPASSEDと温度読み取りが同時に流れています):
Vitis TCF Debug Terminal(メモリテストPASSED+温度読み取り)

実行時に生成されていたmemory_config_g.cとTCF Debug Terminalの表示では、2つのDDR領域は0x1000000000x180000000から始まり、各2GBでした。4GBを超えるアドレスを扱うため、このMicroBlazeは36bitアドレス構成になっています。

ここで注意したいのは、2GB全域を検査したわけではないことです。上の呼び出しでは、32bit×1024、16bit×2048、8bit×4096なので、各テストが触る範囲はいずれも先頭4KBです。今回確認できたのは、2つのDDRチャネルについて、MicroBlaze→AXI→MIG→DDR3の基本的な読み書き経路が通ることまでです。全域試験、連続負荷、帯域測定はまだ行っていません。

2GBのDDR3に対して、今回確認したのは各チャネルの先頭4KB

図の左下が今回確認できた範囲、右下がまだ確認していない範囲です。PASSEDを「DDR3全域の健全性を保証した」と読み替えないことが、この結果を扱ううえで大切です。

この結果はDキャッシュ無効時の先頭4KBテストです

テストアプリはDキャッシュを無効にして走ります。全域試験や性能評価ではなく、この条件で基本的な読み書き経路が通った結果として扱います。

おまけ:温度センサを読む(LM73 / I2C)

tキーで、I2C(AXI IIC)越しに温度センサLM73(アドレス0x4A)から2バイト読み、温度に変換します。

XIic_MasterRecv(&IicLM73, (u8 *)TemperaturePtr, 2);
// ...
Temperature = (TemperaturePtr[0] << 8) + TemperaturePtr[1];
xil_printf("Temperature: %d.%dC\r\n", Temperature/128, (Temperature*100/128)%100);

I2Cは割り込み駆動で受信完了を待つ作りです。GPIO・DDR3に続き、I2Cペリフェラルも生きていることが確認できました。

実画面では46.0Cを取得できました。この簡易コードは正温度のbring-up確認用で、LM73の負温度を扱う符号処理や通信異常時の復旧までは実装していません。

ハマりどころ(実際に踏んだもの)

TCF Debug TerminalはPCIeではなくJTAG/MDM経由

画面だけを見るとUARTターミナルに見えますが、今回の標準入出力はPCIe経由ではありません。Vitis platformのsystem.mssにはstdin = mdm_1stdout = mdm_1と残っており、MDMのJTAG UARTをTCF Debug Terminalから使っています。

つまり、今回確認した経路は「開発PC→JTAG→MDM→MicroBlaze」です。カード上に別の物理UARTを出す場合は、TX/RXピンを割り当てたUART IPとUSB-UARTブリッジなどを別途用意します。

タイミングが最後まで詰まらない(WNSとの戦い)

これがいちばんの山場でした。当時のVivadoプロジェクトを確認すると、Implementationではタイミング違反が残っていました。そこで、次の設定で改善を試みています。

  • Strategy を Performance_ExtraTimingOpt に変更
  • phys_opt_designAggressiveExploreで実行
  • route_designNoTimingRelaxationで実行

保存されている最終のrouted timing reportでは、WNS -0.012ns、TNS -0.023ns、Failing Endpoints 2でした。違反箇所はXDMA/PCIe内部のuserclk1側のパスで、判定もTiming constraints are not metのままです。

phys_opt_designAggressiveExploreに設定した画面:
Post-Place Phys Opt Design(Directive: AggressiveExplore)

最終のrouted timing reportと一致するTiming Summary:
Timing Summary(WNS -0.012ns、TNS -0.023ns)

この状態でもbitstreamは生成され、今回のLED・DDR3・I2Cの実機確認はできました。ただし、実機で動いたことと、静的タイミング解析を通過したことは別です。この記事では「タイミング収束した」とは扱わず、違反が残った状態で限定的な実機確認を行った結果として記録します。

その他

  • board filesとMIG設定の実物合わせ:非公式board filesはDDR設定やピン割当が実チップと食い違うことがあります。MIGは実チップ(速度・幅・チャネル)で再確認
  • PCIeトランシーバの制約:GTのロケーション制約など、board filesだけでは足りず手当てが要る場合があります(参考記事でも言及)
  • bit生成後の書き込み:まずJTAG(Hardware Manager)でFPGAが生きているか、bitが載ったかを切り分けてから上位を疑う

現場コラム:中古FPGAカードは最高の教材

新品なら数万〜十数万円クラスのKintex-7が、中古なら驚くほど安く手に入ります。「正常が保証されない」「情報はコミュニティ頼み」というEBAZ4205と同じハンデはありますが、MicroBlaze・DDR3・I2C・GPIO・(この先の)PCIe/DMAという“実務ど真ん中”を、自腹の実機で端から端まで触れる教材はそう多くありません。

効いたのはやはり「まず正常系を1周」です(C-6の基準ログ、A-3のEBAZの話と同じ)。参考記事のsystest構成をそっくり動かして、LEDが点き・メモリテストが通る状態を先に作ってから、自分の改造を1つずつ足す。タイミングで詰まったときも、この“動く基準”があると切り分けが速い。

今後の展望(=このシリーズの続き)

今回の到達点(MicroBlazeからLED・DDR3・温度)を土台に、次回P-2では、残っているVivado/Vitisプロジェクトから設計の中身を読み解きます。36bitアドレス、2チャネルDDR3、MDM、XSAとVitis platform、リソース使用量、タイミングレポートまでを扱います。

PCIeホストからの転送はP-3以降のテーマです。手元のメモに書いた宿題がそのまま続きになります。

  • P-2:実プロジェクトから構成・アドレス・生成物・実装結果を読み解く
  • P-3以降:PCIe認識、DMA転送、ILAデバッグ、性能測定

まとめ

  • 中古のXC7K480T PCIeカードに、非公式board files+Vivado/Vitis 2023.2でMicroBlaze SoCを構築した
  • 開発環境(JTAG+TCF Terminal)から、LED(GPIO)・DDR3(MIGメモリテストPASSED)・温度(LM73/I2C)を動かせた
  • 標準入出力はPCIeではなくJTAG/MDM経由。DDR3は各チャネル先頭4KBの基本読み書きまでを確認した
  • 最終のタイミング解析はWNS -0.012nsで、違反を残したまま限定的な実機確認を行った
  • P-2では設計データを読み解き、PCIeホストからのDMAはP-3以降で扱う

参考資料

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?