« 2009年2月 | トップページ | 2009年4月 »

2009.03.27

PCI Expressで制御する電球と扇風機

今日は特電PCI Express基板の応用例として、GPIOを用いて電球と扇風機を動かせるようにしました。

もはやレガシーなのだか最新なのだかわからないような企画です。

まず万能基板上に、PCI Expressボードを乗せます。リレーを使ってAC100VをON/OFFします。

Tut6_1

リレーはAC100VをON/OFFします。100Vのラインには白熱電球と扇風機をつなぎます。

システム全体の構成は次のような形になりました。

Tut6_3

パソコンの画面でボタンを押すと電球と扇風機がON/OFFします。リレーが軽快な音を立てながらワキワキうごきます。

PCI Expressは、パソコンからExternal Cablingによって3メートル延長しています。PCI Expressのボードは万能基板の上に乗せ、机の上に置いています。電源は安定化電源から8Vを供給しています。

次の画像をクリックすると、動作している時の動画(約1.8MBytes)をご覧いただけます。

実は、この基板には温度センサとADコンバータも乗っているので、いずれは気温を測定できるようにしたいと思います。
電球で暖めて扇風機で冷やすという温調が作れたら素敵ですね。

本システムの製作記事は、「応用製作①」PCI Expressでコントロールする扇風機と電球にアップロードしました。動作中の動画も4種類アップロードしました。ぜひご覧下さい。

手伝ってくれたアルバイトさんありがとう!

| | コメント (0)

2009.03.26

PCIe評価ボードの使い方のページを作成

PCI Express評価ボードの使い方のページを作成しました。

http://www.tokudenkairo.co.jp/pcie/index.html#tutorial

現在、次のような内容で書いています。
 第1章 準備の準備
 第2章 FPGAの書き込みとパソコンからの認識
 第3章 サンプルアプリケーションの起動
 第4章 FPGAの再論理合成と再書き込み
 第5章 ユーザ・ソフトウェアの作り方

Tut2_5

第1章は、ベンダIDをVHDLファイルやINFファイルに設定する方法と、WebPACKでの論理合成のやりかたを。
第2章は、パソコンのスロットに挿してから新しいハードウェアとして認識されるまでを。
第3章は、2種類のサンプルアプリケーションの使いかたを。
第4章は、FPGAを再書き込みと、書き込み後にBARレジスタがクリアされてしまう問題に対する対処法を。
第5章は、PCI Expressデバイスドライバを使ったユーザアプリの作り方を解説しました。

分かりにくい部分があればご指摘ください。特電PCI Expressボードがより使いやすくなるよう、加筆・訂正いたします。

明日からは何かの具体的なアプリケーションを作っていこうと思います。

| | コメント (0)

2009.03.24

WDMでのDMAと物理アドレス

PCI Expressボード用にDMAを行うためのWDMデバイスドライバを作っています。

DMAを行うには物理アドレスが必要になります。PCI(e)のバスに出てくるアドレスは物理アドレスだからです。しかし、Windowsのプログラミングで用いられるのは仮想アドレスなので、少々面倒な手続きを経て物理アドレスに変換しなければなりません。

NTドライバのころは、MmGetPhysicalAddress()という関数があって、任意の仮想アドレスに対応する物理アドレスを簡単に知ることができました。DMA用のバッファはMmAllocateContiguousMemory()を使って物理メモリ上の連続する領域を確保し、MmGetPhysicalAddress()でその先頭の物理アドレスを得て、DMAの転送先として使うという方法が用いられていました。TECH-Iの「PCIデバッグライブラリ」などでもそのようにしていました。

しかし、WDMになってからはMmGetPhysicalAddress()関数がなくなってしまいました。こうなったら正当な方法でDMA先のアドレスを調べなければなりません。

試行錯誤の末、次のようにしてやるのがもっとも簡単だということがわかってきました。
①DMAアダプタを作成
  アドインカードがどのようなDMA機能を備えているかを申告して、DMAアダプタというオブジェクトを作る
②CommonBufferを作成
 物理メモリアドレスと仮想アドレスの両方が返されるバッファを作る
③アドインカードに、転送対象の物理アドレスを知らせ、アドインカードにDMAを発行させる
④CommonBufferを開放
⑤DMAアダプタを破棄

この手順を順に見ていきます。
DMAアダプタを作成するには、まずDEVICE_DESCRIPTION構造体というのを作ります。この構造体を引数にしてIoGetDmaAdapterを呼び出します。


DEVICE_DESCRIPTION DeviceDescription;
RtlZeroMemory(&DeviceDescription,sizeof(DeviceDescription));
DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
DeviceDescription.Master = TRUE;
DeviceDescription.ScatterGather = FALSE;
DeviceDescription.DemandMode = FALSE; // バスマスタなら必ずFALSE
DeviceDescription.AutoInitialize = FALSE; // バスマスタなら必ずFALSE
DeviceDescription.Dma32BitAddresses = TRUE;
DeviceDescription.IgnoreCount = TRUE; // ???
DeviceDescription.Reserved1 = FALSE; // Must be FALSE
DeviceDescription.Dma64BitAddresses = FALSE;
//DeviceDescription.BusNumber = 0; // 使わない
DeviceDescription.DmaChannel = 0; // ???
DeviceDescription.InterfaceType = PCIBus;
DeviceDescription.DmaWidth = Width32Bits; // バスマスタなので使わないけど
DeviceDescription.DmaSpeed = TypeF; // バスマスタなので使わないけど
DeviceDescription.MaximumLength = 4096;
DeviceDescription.DmaPort = 0; // obsolete
 
// DMAアダプタを作成
PDMA_ADAPTER dmaAdapter = IoGetDmaAdapter(dx->pdo,
&DeviceDescription, &nMapedRegs);

最後のIoGetDmaAdapter関数の引数に与えたdx->pdoは、親となるバスデバイスドライバのデバイスオブジェクトです。IoGetDmaAdapterを実行すると、マップレジスタの数が返されます。

次にCommonBufferを作ります。CommonBufferは、仮想アドレスと物理アドレスのセットが得られるバッファです。これが連続する物理メモリ領域上に確保されるのかとかページングされるのかとかは調べていません。


// CommonBufferを作成して物理アドレスを取得!!
PHYSICAL_ADDRESS combufPhysAddr;
PVOID comBuf = dmaAdapter->DmaOperations->AllocateCommonBuffer(
dmaAdapter, // さっき作ったやつ
transferLength, // ほしいサイズ
&combufPhysAddr, // 作ったバッファの物理アドレス
FALSE); // キャッシュ不可

DMAでアドインカードに書き込みするなら、この時点で、CommonBufferの仮想アドレスにデータを格納しておきます。RtlCopyMemなどが使えます。

バッファの物理アドレスが得られたら、アドインカードにそれを知らせてやります。今回はアドインカード上のBAR0空間に制御レジスタを作って、対象アドレスや転送長をその中に格納するようにしました。

PCIのコンフィギュレーションレジスタに書かれたアドレスは物理アドレスなので、MmMapIoSpaceを使って仮想アドレスに変換します。その仮想アドレスに対してWRITE_REGISTER_ULONGマクロなどを使うことで、アドインカードにデータが転送されます。


// 物理アドレスをメモリ空間にマップして仮想アドレスを得る
PULONG bar0va = (PULONG)MmMapIoSpace(dx->barStartAddress[0],
16, // 16バイト確保
MmNonCached); // キャッシュ不可
// 順序が大切なのでCombinedWriteは不可
WRITE_REGISTER_ULONG(&bar0va[7], 0);
 
TPDMA dmaInfo; // 自分で定義した構造体
dmaInfo.LowAddr = memaddr;//LogicalAddress.LowPart;
dmaInfo.HighAddr = 0;//combufPhysAddr.HighPart;
dmaInfo.LocalAddr = dx->barStartAddress[1].LowPart;
dmaInfo.DmaCommand = transferLength | dmaFlag; // DMA Read 開始!
 
WRITE_REGISTER_BUFFER_ULONG(&bar0va[4], (PULONG)&dmaInfo,4);
MmUnmapIoSpace(bar0va,16); // 最後に開放

これで、(FPGAにDMA機能を作っておけば)アドインカードはDMA転送を開始します。
ただし、このままではプログラムはDMAの完了を知ることができません。アドインカードがDMA完了割り込みなどを発生させるか、デバイスドライバのプログラムがFPGA内のレジスタをポーリングするなどしてDMAの完了を知る必要があります。

DMAで読み出しするなら、ここでCommonBufferの内容にポインタでアクセスするか、RtlCopyMemなどを使います。どうやってDMAが完了したかを知るのは難しい問題ですが・・・

最後にCommonBufferとDMAアダプタを開放します。


// CommonBufferを開放
dmaAdapter->DmaOperations->FreeCommonBuffer(dmaAdapter,
transferLength,combufPhysAddr,comBuf,TRUE);
 
// DMAアダプタを開放
dmaAdapter->DmaOperations->PutDmaAdapter(dmaAdapter);

こんな感じでDMA転送用のバッファを作れることがわかってきました。
ただし、この方法では、ユーザがDeviceIoControlで与えたデータ(ユーザの仮想メモリ空間上にある)とCommonBufferとの間でデータのコピーをしなければなりません。ユーザプログラムでmallocとかして作ったバッファに対して直接DMAで転送ができればパフォーマンス的には最高なのですが、それにはMDLというのを使わなければならないようです。それはまた今度試してみることにします。


さて、この方法でDMA転送を発行させられることがわかってきました。
次の図は、物理メモリの0番地から1024バイトをDMAで読み出したときの波形です。64バイトごとに小分けにされて転送されています。
Pciesh

PCI(e)からメモリリードリクエストを受け取ると、RootComplex(パソコンの中のチップセットがそれを兼ねている)は、要求されたとおりにメモリの内容を吐き出してしまいます。チップセットはOSの管理下ではないので、PCI Expressのアドインカードからはどのようなアドレスであっても物理メモリを自由に覗けてしまうようです。これに対してOSはプロテクトをかけることができず、メモリリード要求を拒否できません。

読み出したデータをJTAGロジックアナライザで見てみると、ソフトウェアで物理メモリの0番地をダンプしたものと一致しているのが確認できました。
これは、PC/AT互換機のアーキテクチャ自身が持つセキュリティーホールではないかと思います。昔やった実験の裏づけが取れました。
デスクトップパソコンならまだいい(箱を開けなければPCIeカードが挿せないから)のですが、ExpressCard付のノートパソコンはちょっと危険ではないかと思います。

| | コメント (1)

2009.03.22

WDM版汎用PCI Exiressデバイスドライバをリリースしました

ようやく、WDM版汎用PCI Exiressデバイスドライバをリリースできました。
これで、本当にようやく、お客様にアプリケーションの開発を始めてもらうことができるようになりました。

このドライバは、PCI Express用という名前ですが、Expressではない普通のPCIでも使えます。
使い方と説明はこちらのページにあります。
http://www.tokudenkairo.co.jp/pcie/drvman.html
ダウンロードはこちらのページにあります。


このドライバは、特電PCI Express評価ボードがなくても、汎用のPCI Expressデバイスドライバとして、PCIの構成を見たり、システムのメモリにアクセスしたりすることができます。
・PCIの構成を見るツール(PCIeMan)はこちら
・PCIにいろんな(コンフィグ/メモリ・リード/ライト)リクエストを発行するツール(testapp.exe)はこちら
です。
PCI Expressでなくても普通のPCIでも使えます。Capability Pointerを辿っていく機能も実装したので、結構遊べると思います。
Pcieman

このドライバは、非商用であれば、汎用PCIドライバとして無償で使用することができます。
また、特電PCI Expressボード用ドライバとしてならば、商用・非商用を問わず無償で使用することができます。

このドライバをフルにインストールすると、下の図のように2つのデバイスがインストールされます。
Dm

ところで、WDMドライバの開発に便利なテクニックを発見しましたので紹介します。

PCIやPCI ExpressをFPGAで作る場合、FPGAをコンフィギュレーションしてしまうとPCIの構成情報(BARレジスタなど)が消えてしまいます。パソコンを再起動するのは至極面倒!
そんなとき、WindowsのデバイスマネージャでPCIデバイスを選択して右クリックし、一旦「無効」にして「有効」にすると、PCIのコンフィギュレーションレジスタが再度設定されるのです。
デバイスマネージャで無効→有効をすると、デバイスドライバが更新され、PCIのコンフィギュレーションレジスタが再設定される

それどころか、無効にしたとき、ある条件が揃うとsysファイルがメモリからアンロードされます。次に「有効」にしたとき、デバイスドライバのsysファイルがメモリに再度読み込まれます。

要するに、デバイスドライバのsysファイルを作りなおしたら、C:\windows\system32\driversにコピーして、無効→有効、とやれば新しいドライバがインストールされるのです。ハードウェアウィザートとかの面倒なGUIを操作してINFファイルを指定する必要はありません。
パソコンを再起動しなくても、デバイスドライバの更新や、PCIコンフィグ空間の再設定ができるのです。

これは便利!

さて、このドライバは以下のような特徴を備えています。
・WDMに対応した、正当な方法で作られている
・PCIコンフィギュレーションレジスタの読み書きが可能
・PCIボード(Express含む)上のメモリ空間の読み書きが可能
・PCIバスドライバにアクセスして構成情報を得ている(コンフィギュレーション空間に直接アクセスはしない)
・GUIDを用いた新しい方法でシンボリックリンクを作成している
・CombinedWriteを用いて、64バイトサイズの書き込みリクエストを発行できる
 (CPU→PCI Express方向へのバースト書き込みは非常に高速)

という感じで、WDMの世界では行儀がよいと思われる方法に従って作成しました。
なので、今後、64bit版やVistaやWindows7などへの対応もスムーズにいけるのではないかと期待しています。

あとは、物理メモリの確保やDMAなどの機能や、アクセスしてはいけない領域にうっかりアクセスするのを防ぐ機構の開発などをしていきたいと思っています。

また、特電PCI Express IPコアで大量のデータを読み書きする実験を行いました。
Testapp

これは、8192バイトの乱数データを読み書きするテストなのですが、最終リリースのコア(0.5)では、ACKとSKPが偶然ぶつかるとパソコンが読み取り失敗するという問題があることがわかってきました。
この問題を取り除いたところ、1500万回程度読み書き(120GBytes程度のデータ量)しても、エラーが起きなくなったので、おそらく問題は解決されたのでしょう。
不安いっぱいのスタートでしたが、このコアに自信が持てるようになってきました。

最新版のIPコアは、Version0.55としてアップロードしました。
次はアドインカード→PC方向のDMA転送を目指します。

どうぞよろしくお願いします。

| | コメント (1)

2009.03.19

データ転送速度190MBytes/sec達成!!

WDM版汎用PCIドライバの開発もいよいよ大詰めです。
今日はメモリリード・ライトの機能を実装しました。

アドインカード上のメモリへCPUから読み書きを行うには、準備段階でMmMapIoSpace()という関数が使われますが、この関数はNT形式のドライバとWDMで仕様が変更されているようでした。

NTドライバでは、マッピング先をキャッシュするかしないかの指定しかできなかったのですが、WDMではMmCached、MmNonCached、MmWriteCombinedに選択肢が増えています。

MmWriteCombinedというオプションを見たとき、川井さんがDWM1月号で書かれていた話はこれか、と思いました。コンバインド書き込みを実行するには、デバイスドライバでそういうオプションを指定するわけですね。

MmMapIoSpace()の第三引数をMmWriteCombinedにすると、複数の書き込み動作が1個に結合されて発行されているようすがJTAGロジアナで確認できました。

実験したところでは、64バイト以上のまとまった書き込みを1個に結合するようです。

・64バイト書き込み → 64バイト書き込み×1回
・16バイト書き込み → 8バイト書き込み×2回
・48バイト書き込み → 8バイト書き込み×6回
・68バイト書き込み → 4バイト書き込み×1回 + 64バイト書き込み×1回
・72バイト書き込み → 64バイト書き込み×1回 + 8バイト書き込み×1回
という感じでした。

たしかに書き込みの順序は保障されていません。
しかも、開始アドレスが64の倍数に揃っていないと効率よく発行されなかったりします。
BAR0の先頭から68バイト連続で書き込んだら、アドレス+64からの4バイトが先に書き込まれました。
なんだか、メモリ上の、アドレスが64バイトにアラインメントされた領域に高速に転送するような感じでした。

PentiumはSSE2命令を使えば128bit(=16バイト)の書き込みバスサイクルを発行できるはずなのですが、これまでいろんなパソコンで試したところでは、PCI Expressのバスに出るときに8バイト×2回のサイクルに分割されてしまっていました。コンバインド書き込みを使えば大きなサイズで書き込みパケットを作れるようで、パフォーマンスが大幅に改善します。

次の図は、4096バイトの連続書き込みを実行したときの、FPGA内部の波形です。
Wrcomb

336nsごとに64バイトのデータが乗ったTLPを受信していて、この波形がずーっと続きます。したがって、64/0.336us=190MBytes/secという速度になります。

コンバインド書き込みを使うことで、PC→アドインカードの転送方法が約130MBytesから190MBytesに向上しました。

このデバイスドライバは、汎用PCI Expressドライバとして、明日、リリースする予定です。

| | コメント (0)

2009.03.17

PCIのコンフィグ空間を読み書きするWDM

PCI デバイスの構成情報と場所情報の取得方法を参考にして、コンフィグ空間の読み書きができるようにしたのですが、DeviceIoControlの中からやるとブルー画面になってしまいました。

今、クリーム色の「WDMデバイスドライバ」という書籍を参考に作っていますが、このサンプルコードでは、DeviceIoControlのハンドラの中でスピンロックを取得しているようです。これがまずかったようです。
スピンロックというのは、カーネルモードのデバイスドライバの中のコードに排他制御をかけるためのしくみです。
あるCPUがI/O処理をしているときに別のCPUが同じリソースに対してI/O処理をしないようにするため、デバイスドライバのもっともコアな部分では排他制御が必要になることが多くあります。

しかし、スピンロックはIRPを下に送るとかいう抽象的な処理の際にはやってはいけないようですね。あくまでも低レベルのI/O処理ルーチンをロックするために使うべきなのでしょう。

ところで、「PCIデバッグライブラリ」では、バス番号やデバイス番号を自由に設定して他のデバイスのコンフィギュレーション空間を見ることができましたが、これはHalGetBusDataByOffeset()関数を使うことによって実現していました。
しかし、WDMでは、HalGetBusData()が使えなくなっていました。つまり、WDMのPCIドライバは、自分のデバイスのコンフィギュレーション空間しか見ることができません。

WDMの自分で作るデバイスドライバ(ファンクションドライバ)は、バスにアクセスするのではなく、バスドライバに処理を委託するのです。自分のデバイスのバス番号さえ、バスドライバから教えてもらうわけです。

NTからWindows2000に変わって、デバイスドライバでさえもやりたいことが自由にやらせてもらえない環境になりました。

PCIの持つメモリやI/O空間の情報を得る正当な方法は、PnPで IRP_MN_START_DEVICE が送られてきたとき(つまり、デバイスが開始したとき)に、AllocatedResourcesTranslated構造体の中身を解析することのようです。コンフィギュレーションレジスタに一切アクセスすることなく、BAR0~BAR5がどのアドレスに割り当てられたかや割り込みラインなどの情報も得られます。
要するに、Windows2000になってから、バスが抽象化され、バスに一切触ることなく、コンフィグレーション空間の知識も知らなくてもよくなり、安全なやり方が提供されるようになったのです。

それなら面倒なカーネルモードじゃなくてもいいじゃん、と思うのは私だけでしょうか。

| | コメント (1)

2009.03.16

Lattice XP2のUSB-JTAG書き込み

気分を変えて、今日はトラ技78Kマイコンを使って、Lattice XP2のUSB-JTAG書き込み器を作ろうと実験しています。

実験に使ったFPGAボードは、富士エレクトロニクスさんのXP2評価ボードです。
トラ技78K基板で、USB-JTAGをつくり、LatticeのXP2を書き込み

しかし、現在の78K-USB-JTAGのプログラムにはバグがあって、Lattice XP2には正常に書き込めないことがわかってきました。

バグの原因を修正し、LFXP2-17Eに書き込んでみました。
78kusbjtagxp2_1

結果は、
・消去 5秒
・書き込み300秒
・ベリファイ300秒
と、異様に遅いということがわかりました。

試しにXILINXのParallel IVでやってみたら、
=
Xilpar4xp2_1_2

・消去 5秒
・書き込み11秒
・ベリファイ17秒
と、耐えられる速さでした。

来月はデジタル・デザイン・テクノロジとかいう雑誌が創刊されるようですが、そのおまけ基板がLFXP2-5Eだそうです。
それに向けて、トラ技78Kマイコンや、XILINX Parallelケーブル、ALTERA ByteBlasterなどでXP2に書き込みができるようなプログラムを作り、無償でリリースしたいと思っています。

今の78K-JTAGのプログラムでは、書き込みとベリファイにそれぞれ120秒ほどかかってしまう計算になります。
FPGAの書き込みに4分は待てないので、なんとかしたいと思います。

| | コメント (0)

2009.03.13

WDM版の汎用PCIドライバ

WDM版の汎用PCIドライバを開発中です。
WDMではNTドライバと違い、建前上は自分で作ったドライバは下位のバスに接続されていることになります。その下位のバスのドライバは、プラグアンドプレイによって、アドインカードを見つけます。

だから、自分で作ったWDMドライバは、親ドライバから呼び出してもらうことによって、はじめて起動されます。
(NTドライバはサービスマネージャでいつでも起動できたのと対照的です)

よってPCI ExpressデバイスはPCIバスドライバから起動されますが、汎用PCIデバッグライブラリのように、カーネルモードでI/Oやメモリを自由にアクセスしたいドライバはどうすればよいのでしょうか?

この場合、親ドライバがいないので、パソコンの起動時にUnknownドライバ(正しい名前はわからない)から起動してもらうようです。このようなドライバは、コントロールパネルから「ハードウェアの追加」で強制的にインストールできます。

現在開発中の「PCI Express汎用デバイスドライバ」は、PCIカードのドライバとしてもインストールでき、また宙に浮いた単独のドライバとしてもインストールできるようにしました。

Devman

上の図は、同じドライバを、PCIカードと宙に浮いたドライバの2つインストールした場合の表示です。SYSファイルはひとつですが、どちらも独立して動いています。

さて、親ドライバの種類は、IoGetDeviceProperty()という関数を発行することで調べられるようです。この関数の引数にDevicePropertyEnumeratorNameを与えると、親ドライバの名前が返ってきます。これをAddDeviceの中でやることで、自分が何ドライバなのかを知ることができるというわけです。


status = IoGetDeviceProperty(pdo,
DevicePropertyEnumeratorName,
sizeof(tmp),
(PVOID)&tmp[0],
&length);
if(status == STATUS_SUCCESS)
{
if(!TkWStrCmp(tmp,L"Root"))
{
DebugPrint("Enumerator type is ROOT. This is a generic I/O driver.");
}
else if(!TkWStrCmp(tmp,L"PCI"))
{
DebugPrint("Enumerator type is PCI. This is a PCI device driver.");
isPci = TRUE;
}
else
{
DebugPrint("Enumerator type is unknown... Failed !");
return STATUS_UNSUCCESSFUL;
}
}

それがL"PCI"であれば、PCIバスドライバが親ドライバになっていることがわかります。
それがL"Root"であれば、親ドライバはなく、単独で存在しているドライバとなります。

PCIならば、IoGetDeviceProperty()をさらに使って、バス番号やデバイス番号を調べられます。
NT形式のドライバは、目的のアドインカードを探すため、バス番号とかを変えてスキャンしなければなりませんでした。WDMではPnPですから、ドライバの起動時にすでにバス番号・デバイス番号・ファンクション番号がわかっています。それは、下位のドライバに問い合わせることによって得られます。

また、NTドライバは、I/Oポートの0xCF8番とかを直接叩いてPCIのコンフィギュレーション空間にアクセスしていましたが、WDMではそれは良い方法とはされていません。

WDMでは、PCIバスドライバに対してIRP_MN_READ_CONFIGやIRP_MN_WRITE_CONFIGを発行することによって、PCIのコンフィギュレーション空間にアクセスするのが正当な方法のようです。やってみたら、確かにうまくいきました。コンフィグレジスタが読み出せています。
Config_rd_2

PCIコンフィグレジスタの読み書きは、デバイスドライバ内で自分でI/O操作をやるのではなく、下位のドライバに処理を依頼するようです。

このようにPCIのコンフィグ空間は隠蔽され仮想化されてしまいました。できる限りユーザにハードウェアをアクセスさせたくないようです。デバイスドライバなのに。

ところで、PCIのコンフィギュレーション空間は256バイトでしたが、PCI Expressでは4096バイトに拡張されました。上記の関数でPCI Express拡張コンフィギュレーションにアクセスできるかと思いましたが、だめなようです。

どうせ仮想化してくれるのであれば、拡張コンフィギュレーション空間にアクセスする方法も提供してほしかったです。もしかしたらそういう関数がすでにあるのかもしれません。

| | コメント (0)

2009.03.12

PCIeコアを更新(バースト転送対応)

本日、PCI Expressコアを更新しました。

今回の主な更新点は、バースト転送をサポートしたことです。
パソコン→PCIeボードへの転送は、毎秒70MBytes/secが出るようになりました。大抵のアプリケーションは十分ではないかと思います。
この速度を出すため、汎用PCIデバッグライブラリで、128bit書き込み関数を用いています。128bit書き込み関数では、SSE2命令を使って16バイトの書き込みを発行していますが、チップセットによって64bitのトランザクション2個に分けられています。

一方、PCIeボード→パソコン方向の転送は、あまり速くありません。6~8MBytes/sec程度です。Athron64x2のパソコン(DELL DimensionC521)で試したところ、128bitの読み出しは、32bitの読み出しトランザクション4個に分けられてしまいました。
これは、もっと高速にするならアドインカード上にDMAコントローラを乗せてバスマスタにするしかないですね!

興味深いことにAthron64x2のマシンでは、アドレス(BAR0 + 4)番地から連続する16バイト読み出そうとしたら、+0C,+10,+04,+08というアドレスの順序で4個のトランザクションが発行されました。SSE2命令による1つのバスサイクルのはずなのに、アドレスの順序まで変わってしまっています。キャッシュのしくみとかが関係しているのでしょうか。

それから、制御信号のタイミングを厳密に規定しました。

次の図は、メモリ・ライト・トランザクションを発行したときのローカルバスの波形です。
Memwr

コアはbar*_wr信号をアサートするので、dvalid信号が出るタイミングでユーザ回路にデータを書き込みます。
bar*_wr信号は、アドレスやBEを確定てから1クロック後に有効になるようにしました。

次の図は、メモリ・リード・トランザクションを発行したときのローカルバスの波形です。
Memrd

メモリ・リードの場合、コアはbar*_rdreq信号をアサートします。
ただし、メモリがすぐに使える状態になっていないこともある(リフレッシュ中だったり、低速メモリをつないでいるような場合)ので、ユーザ回路はbar_rdack信号をHにして、準備ができたことを知らせられるようになっています。

コアはbar_rdackを確認したら、数クロック後にuser_dreq信号をパチパチと出してくるので、そのタイミングでユーザが出したデータが取り込まれます。

このローカルポートインタフェースは、ものすごくシンプルですが、ほとんどのアプリケーションにとって十分なことができるようになっています。
要するに、IPコアは複雑なPCI Expressのプロトコルと単純なローカルバスのプロトコルを変換しているわけです。

また、PCI Expressのサンプルアプリケーションも更新しました。いままでのサンプルアプリでは、LEDがチカチカするのをただ眺めていただけでしたが、今回のバージョンアップで、BAR0~BAR2空間や、コンフィグ空間に対して読み書きするデータを、簡単なスクリプトで記述できるようにしました。

Sampleapp

たとえば、
  mwr long bar1+40 12345678
のように記述すると、BAR1空間のオフセットアドレス0x40に、longサイズで、0x12345678というデータを書き込みます。
  mrd longlong bar0+3C
のように記述すると、BAR0空間のオフセットアドレス0x3Cから、8バイトを読み出して画面に表示します。

このような簡単な手順でやりたい操作をどんどん書くことで、コアにつながったユーザ回路の動作を確認することができます。

コアやサンプルアプリ、マニュアルは下記のページに用意しました。
http://www.tokudenkairo.co.jp/pcie/index.html

ようやく実用的なレベルに近づいてきたのですが、今の最大の問題点は、オリジナルなデバイスドライバがないこと!
WDMのを急いで作っていますので、もうしばらくお待ち下さい。

| | コメント (3)

2009.03.10

32bitを超えるメモリアクセス

今夜は、特電PCI Expressコアに、32bitを超えるメモリアクセスの機能を実装しています。

これができると、バーストリード・ライトができるようになり、パフォーマンスが大きく向上するはずです。

PCIデバッグライブラリ2.0.0には
_MemReadBlock128

_MemWriteBlock128
という関数があります。これを使えばSSE2命令を発行して、128bit単位での効率の良いリード・ライト・トランザクションを発行してくれるはずです。

しかし、実際にやってみると、64bit単位でしか発行されていませんでした。原因はよくわかりませんが、チップセットが、128bitのメモリアクセスを64bit 2回のアクセスに分割してしまっているのでしょうか。
メモリライト(PC→アドインカード)時の波形は次のようになりました。

Pcie_memwr_128

この波形を観察すると、1個のTLPに32bitのデータが2個乗っているのがわかります。そして、それが約120~150nsの周期で送られてきます。128bitのデータが64bitのデータ2個に分けられますが、その間には論理IDLEが挟まれず、前のENDシンボルの直後に次のSTPシンボルが来ています。やはり、チップセットによって分割されているのだと思います。
128バイトのデータを送信するのに1.94μ秒要しているので、スループットは65MBytes/secです。

次はメモリリード(PC←アドインカード)です。
とても悲惨な波形になりました。
Pcie_memrd_128

トランザクションはだいたい1μ秒に1回しか発行されていません。その1回1回が64bitの転送なので、平均すると8MBytes/sec程度のスループットしか出ないということになります。これがPIOモードの実力なのでしょう。

これはIPコアが悪いのではなく、チップセットが原因なのでしょう。
IPコアはメモリリードに対して即答していますから。

1個のメモリリードは、
①ルートコンプレックスがトランザクション発行
②エンドポイントがAckを返す
③エンドポイントがCompletion(読み出しデータ)を返す
④ルートコンプレックスがAckを返す
という4つのパケットで行われます。

③と④の間は216ns程度かかっています。つまり、PCI Expressの遅延が片道100nsほどあることになります。1つのシンボルが4nsですから、25シンボルほどの遅延時間がかかっていることになります。PCI Expressはマザーボードに直接挿しているようでも、途中でブリッジやスイッチが入るので、そのFIFO分の遅延時間を考えると妥当な時間です。

そして、もっとも遅いのは④の後の次の①までが470nsくらいですが、これはルートコンプレックス(チップセット)の中での待ち時間です。なぜこんなに時間がかかっているのかわかりません。

何はともあれ、任意の長さのリード・ライト機能が実装できました。
ライトはそこそこの速度が出ますが、リードについては改善の余地がありそうです。


| | コメント (0)

2009.03.08

汎用PCI Expressデバイスドライバを作りたい

タイトルのとおりです。
Windows2000やXPで動く、汎用的なPCI Expressデバイスドライバを作ろうと思っています。

PCIであれば、すばらしい汎用ドライバが既にあります。
(「PCIバス&PCI-Xバスの徹底研究」に収録されている)

車輪の再発明といわれそうですが、せっかく作るのだから、
・PCI Express拡張コンフィギュレーション空間に対応させる
・WDM形式で作る
・HalAssignSlotResuorces関数を呼び出せるようにして、PCIのリソース再割り当てをいつでも実行できるようにする。

ようにしたいと思っています。

WDMというのは、Windows98やWindows2000以降のデバイスドライバで、INFファイルを使ってインストールするものです。
それに対して、NT形式というのもあります。NT形式は本来WindowsNT4.0のためのドライバですが、Windows2000やXPでも動きます。

どちらが良いかというのは一概にはいえません。

WDMのほうがもちろん高機能なのですが、インストールするのが面倒です。プラグアンドプレイ検出してもらわないと、インストールができません。(というか、やりかたがよくわかりません) なので、「汎用のI/Oポートアクセスドライバ」のようなドライバはインストールが難しそうです。

それに対して、NT形式のドライバは、ユーザーモードのプログラムの中から関数を2つか3つ呼び出してやれば、いつでも好きなときにインストール、アンインストールができます。ユーザの見えないところでいつのまにかカーネルモードのプログラムがインストールされて動くという、とてもとても恐ろしいことができてしまうのです。

ですが、WDMの汎用ドライバをやってみたいと思います。

それから、HalAssignSlotResuorces関数というのは、"WDMデバイスドライバプログラミング完全ガイド"という本を読んでいて見つけた関数なのですが、PCI(に限らず)のコンフィグ空間のレジスタを再設定してくれる便利な関数のようです。
パソコンが稼動中にPCI Expressのカードをいきなり差し込んだとしても、BAR0などのレジスタが初期化されていないのでアプリケーションはそのカードを認識できませんが、HalAssignSlotResuorces関数を呼び出してやるとBAR0などのレジスタが再設定されるそうなのです。

普通、FPGAを再コンフィギュレーションしたら、FPGA内のレジスタはすべて消えてしまいますので、この関数を使わないと、「FPGAを再コンフィギュレーションしたらパソコンを再起動してください」なんていう悲しいことになってしまうかもしれません。
この関数の呼び出しはぜひともデバイスドライバに組み込んで、ユーザモードからいつでも再認識プロセスを発行できるようにしてあげたいものです。

| | コメント (0)

2009.03.07

特電PCI Expressコアを更新(V0.4)

IPコアを更新しました。

主な更新点は
・SKPパケットを送るようにしたこと
・BAR0~BAR2の設定とIDについて、コンフィグ空間のカスタマイズを可能にしたこと
・BE[3:0]信号を出力するようにしたこと
です。

SKPパケットというのは、PCI Expressのシステムボードとアドインカード間でのクロック速度の違いを吸収するためのパケットです。定期的に意味のないパケットを送信しあうことで、双方のクロック速度に多少の差があっても調整することができるようにというものです。

『普通、アドインカードはパソコンのマザーボードに挿して使うので同じクロック源を使うから、速度は一致するはず。だからSKPパケットなんて送らなくてもいいだろう。むしろ、SKPに占有される時間を本来の通信に割り当てればスループットが向上する』、なんて考えていました。
しかし、SKPパケットを送らないようにしてしまうと、通信がない場合には論理アイドル(D0.0)しか送られません。パソコンとアドインカード間で通信するだけなら問題ないのですが、PCIeアナライザをつないだ場合に問題がありました。通信をしないとCOMシンボルがずっと送られないので、同期が取れないらしく、うまく解析できないのです。このため、SKPパケットはやはり必要だろうという結論になりました。

次に、BAR0~BAR2のサイズをカスタマイズできるようにしました。

main.vhdの中に以下のような長い(160ビット)の定数が定義されています。この定数の各ビットがコアの中まで伝わっていって、コンフィグ空間のしかるべきビットにセットされるというしくみです。
今後、より細かい設定ができるようになった場合にも、コアのポートを増やすことなく、このような定数のビット数を増やすだけで対応できます。


constant customize_c : std_logic_vector(159 downto 0) :=
x"FF000000" -- [159:128] bar2 addr space (16MBytes)
& x"FFF00000" -- [127:96] bar1 addr space (1MBytes)
& x"FFFFC000" -- [95:64] bar0 addr space (16kBytes)
& x"110000" -- [63:40] class code
& x"01" -- [39:32] revision id
& x"xxxx" -- [31:16] device id
& x"xxxx" ; -- [15:0] vendor id

BAR0に8192バイト、BAR1に1Mバイト、BAR2に16Mバイトの空間を割り当てたところ、ちゃんと、要求したサイズのメモリ空間が確保されました。
Resource

最後の変更点は、BE[3:0]を出力するようにしたことです。やはり、charやshortサイズでアクセスしたいものです。これで、FPGAの中に構成したメモリを1バイト単位で操作できるようになりました。
すこしずつですが、まともなコアになりつつあります。

修正版のコアと、アプリケーション、マニュアルは本日更新しました。
下記のページからダウンロードできます。
http://www.tokudenkairo.co.jp/pcie/

次はオリジナル・デバイスドライバの開発と、バースト転送の実装、アプリの強化などを行おうと思います。
皆様、どうぞご支援のほどよろしくお願いします。

| | コメント (0)

2009.03.06

PCI Expressのプロトコルアナライザ

Agilent Technologies社のPCI Expressプロトコルアナライザをお借りして、特電IPコアの動作を検証することにしました。

まず、パソコンからPCI Expressのバスをケーブルで引っ張り出します。
Pcie_analyzer1

そして3メートルのケーブルで延長して、特電PCI Expressボードをつなぎ、プロトコルアナライザのプローブをつなぎます。
Pcie_analyzer2

カップリングコンデンサの部分にプローブを半田付けして、信号を引き出します。
Pcie_analyzer3

コンフィギュレーション空間のReadを行ってみると、PCIeのやりとりが見えました。
このアナライザはプロトコルのエラーなどをみつけると、赤で表示して知らせてくれる素晴らしい解析機能があります。とりあえず、ざっと見た感じではプロトコルの違反はないようでした。
Pcie_analyzer4

PCI Expressのプロトコルには、規格書や解説書を読んでもよくわからない部分がいくつかあります。
この機械をお借りしている間にバースト転送やDMA、割り込みを正しく実装できるようにしたいと思います。

| | コメント (0)

2009.03.05

パートナーになりたい

XILINXの代理店をしている某社から、営業の電話がかかってきました。

「XILINXの一次代理店をしている○○ですが、XILINXで何かお困りのことはないでしょうか?」

これは絶好の機会と思い、日ごろの思いを素直に伝えてみました。

「XILINXのパートナーになりたいのですが、どうすればよいでしょうか?」
「わかりました。お調べしてまた連絡します。」

なれるといいな。

| | コメント (0)

2009.03.04

PCI Express IPコアのマニュアルを作成しました

昨日、コアの第3版をリリースしました。
そして本日とても簡易的なものではありますが、PCI Express IPコアのマニュアルを作成しました。

マニュアルは下記のページに用意しております。
特電PCI Express 互換IPコアの使い方

また、オンラインショップでも取り扱いを始めました。

まだまだ不備な点はございますが、ようやく製品としての形が整ってきたので、今週から、これまでにご注文いただいていたお客様に出荷を行いました。

製品は次の写真のような梱包で出荷しています。
Shukka

プチプチの中にはPCI Express基板と、ピンヘッダ・コネクタ類が入っています。
Puchipuchi

上の写真では基板は四角いままですが、ご注文の際にご指定いただければ、PCI Expressのカード形状になるように「割って」納品いたします。

今週から来週にかけて、朝から晩までどっぷりとPCI Express漬けです。今日はオリジナルデバイスドライバも作り始めました。この勢いで、DMA転送の実装やMITOUJTAG特別版の開発まで一気にやってしまおうと思います。

皆様、どうぞよろしくお願いします。

| | コメント (0)

« 2009年2月 | トップページ | 2009年4月 »