会社案内 (近況 2008/04)

先月   翌月

2008/04/29 PCIe IP コア開発 (川井)

CRC 計算の高速化は実は非常に困難だということが判明した。開発当初は「タ イミングが間に合わなかったら pipeline 化すればいいじゃんアハハン」と安 易に考えていたが、良く考えるとそれでは実用にならない。pipeline 化して も最終段の結果がわかるまで次の計算を開始できないので、n 段にわけたら最 終結果を求めるのに n clk かかってしまう。つまり n 個の TLP をバッファ しておき、n clk かけてゆっくり処理する必要がある。原理的には可能だがメ モリ資源が大量に必要だし回路が複雑化する。

Web を漁ったところ、fast-crc という pipeline 化された CRC 回路を opencores.com でみつけた。が、よく調べたところ、この回路は複数の TLP を並列処理する場合にしか throughput が出ないことが分かった。要するに上 記の方法で作った pipeline が複数同時に動くだけの回路だった。

さらに Web を漁り続けたところ、今度は本当に使えそうなアルゴリズムをみ つけた ("Pipelined Cyclic Redundancy Check (CRC) Calculation", M. Walma, IEEE Computer Communications and Networks, 2007)。論文が有料 だったのでよほどのことがない限りここで読むのを諦めるわけだが、よほどの ことなので 35 ドル払って買ってみた。

アルゴリズムの原理をちゃんと理解するにはガロア拡大体とかの算数の知識が 必要だが、原理はわからなくともアルゴリズムが正しいことを信じて実装して みることは可能そうだった。実際エミュレータを書いてみると正しく計算でき ているように見える。ガロア恐るべし。

アルゴリズムの概要:

  1. CRC はガロア拡大体 GF(2) のうえで線型性を持つ。つまり入力を A、 その CRC を CRC(A) と書くとき、CRC(A) = CRC(A0 + A1) = CRC(A0) + CRC(A1) が成り立つ。ここで + は GF(2) 上の加算、つまり排他論理和 (xor)。 (この理由がわからない。これ以降は算数的な操作だけなので大して難しくない)
  2. A の前にゼロをつけても CRC の値は変わらない。つまり CRC('0' & A) = CRC(A)。
  3. A の後ろにゼロを 1 個つけた値の CRC は、A の CRC に行列 H を左か ら乗じることで求められる。つまり CRC(A & '0') = H x CRC(A)。ここで H は GF(2) 上の (つまり各成分が 0 ないし 1 の) 32 x 32 行列で、手計算で 簡単に求められる。
  4. 1〜3 を使うと、長いビット幅をもつ入力 A を短い幅の A0..Ai..An-1 の n 個に分割して、それぞれを別個の pipeline 回路で計算し、最後に合成 することが可能。各 pipeline は 1 段目で Ai の CRC を計算し、残りの段で は Ai の後ろに必要な個数ぶんの 0 を追加するために H をひたすら乗じる。
入力データ (TLP) は最大でも 4kbyte ちょいなので、pipeline が Ai に乗ず る H の個数は最大でも 4k x 8 個強 (= 約 215個)。あらかじめ H8,H16, ... ,H32768の値をテーブル化し て持っておけば、どんな場合でも乗算はたかだか 12 回で済む。Arria GX で も pipeline 1 段につき乗算を 2 回行って 125MHz で充分まわるので (頑張 れば 3 回できるかも)、pipeline delay は 12/2 段 + 1〜2 段くらい。これ なら充分実用になる。

実装したらほんとに動いた。これで pipeline 本数と latency (pipeline delay) さえ調節すれば、入力のビット幅がどんなに大きくなっても 125MHz で動かせるようになった。ただしロジックはかなり多量に消費する (pipeline 1 本に LE を 3k 個くらい)。

Altera は Arria GX で x8 をサポートしていない。その理由がもし「CRC 計 算のタイミングが間に合わないから」というだけなら、このアルゴリズムを使 って x8 PCI Express コアを Arria GX 上で動作させられるかも知れない。

2008/04/23 PCIe IP コア開発 (川井)

x4 向けの拡張は全部実装したつもりなのに全然動かない (ホストから認識さ れない)。コアから送った TLP に対して Nak が返ってきてしまう。コアが送 出している TLP の内容をよくみると、LCRC が間違っていた。ロジックの間違 いではなく、64-bit 入力では CRC 生成回路のタイミングが厳しいために間違 うようだ。ついに開発当初からの懸念が現実 のものにっ! どうする? 待て次号!

2008/04/21 PCIe IP コア開発 (川井)

レーン幅が増えると x1 では必要なかった機構があちこちに必要になる。 予想以上に面倒だ。例えば:

2008/04/19 PCIe IP コア開発 (川井)

x1 の target 機能がだいたい動くようになったので、本日より x4 の開発に 突入。master 機能 (DMA) は x4 の target が出来てから作ろう。

2008/04/18 ボードコンピュータ展 (川井)

幕張メッセで開かれているボードコンピュータ展に行ってきた。コア開発を始 めるにあたってアドバイスを頂いた内藤さんに挨拶した。あっちも小さな会社 のようだが、製品が豊富で広報活動もしっかりしていてすげぇ。

2008/04/17 PCIe IP コア開発 (川井)

以下のようにいろんなところでハマって結局 1 週間かかったが、Memory Read/Write が動くようになった。

さらに今まで PLDA のコアで使っていたソフトウェア (デバイスドライバとユー ザライブラリ) を自作コア向けに変更して、テストコードが一部動くようになった。 テストコードを使って PIO write の速度を測ってみた:

without write combining, 2B burst.
kawai@gb2[24]>./hibtest 13 1
# hib[0] PIO write (host -> HIB)
size: 256 DMA read: 1.161963 sec  68.849009 MB/s
size: 512 DMA read: 1.146817 sec  69.758298 MB/s
size: 1024 DMA read: 1.139276 sec  70.220033 MB/s
size: 2048 DMA read: 1.135452 sec  70.456536 MB/s
size: 4096 DMA read: 1.136034 sec  70.420427 MB/s

with write combining, 64B burst.
# hib[0] PIO write (host -> HIB)
size: 256 DMA read: 0.453611 sec  176.362513 MB/s
size: 512 DMA read: 0.438551 sec  182.418842 MB/s
size: 1024 DMA read: 0.431044 sec  185.595858 MB/s
size: 2048 DMA read: 0.427309 sec  187.218133 MB/s
size: 4096 DMA read: 0.438372 sec  182.493450 MB/s

PLDA core, write combining, 64B burst.
kawai@gb2[6]>./hibtest  13 1
# hib[0] PIO write (host -> HIB)
size: 256 DMA read: 0.453607 sec  176.364089 MB/s
size: 512 DMA read: 0.438513 sec  182.434811 MB/s
size: 1024 DMA read: 0.431037 sec  185.598938 MB/s
size: 2048 DMA read: 0.427364 sec  187.194006 MB/s
size: 4096 DMA read: 0.438373 sec  182.492954 MB/s

PLDA のコアとだいたい同じ速度が出ている。 転送内容が正しいかどうかについてはまだちゃんと確認できていないが w

2008/04/11 PCIe IP コア開発 (川井)

ソースコードの場合分けだとか文字列マクロ機能が VHDL の仕様にもいちおう あるにはある (if generate とか constant とか) が、きわめて貧弱なので嫌 気がさして、代わりに cpp を使うことにした。例えばこんな記述:

entity ifpga is
  port (
#if (SIMTYPE > 0) // simulation only
    i_rx                 : in  std_logic_vector(NLANE*18-1 downto 0);
    o_tx                 : out std_logic_vector(NLANE*18-1 downto 0);
#endif

をしておいて、合成前に cpp -P を通して普通の VHDL に変換する。変換は make でやれば大した手間ではない。

SIMTYPE = 1  # 0:hardware 1:phy 2:dll 3:tl 4:app
CPP   = cpp -P -DSIMTYPE=$(SIMTYPE)
VHDL  = hoehoe.vhd auau.vhd
VHDL0 = $(patsubst %.vhd,%.vhd0,$(VHDL))

all:	$(VHDL)

%.vhd:	./templates/%.vhd0
	$(CPP) $< -o $@

2008/04/10 PCIe IP コア開発 (川井)

Configuration Read/Write を扱えるようになったので、次は Memory Read/Write。特に Memory Read の Completion には今までサボっていた面倒な規則の実装が必須。さらに payload 先頭と末尾の 4-byte word に関しては byte enable も考慮が必要だった。めんどくせー

2008/04/08 PCIe IP コア開発 (川井)

細かいとこがいろいろおかしかったが、地道に直して、ついにホストから PCI device として認識されるようになった! /sbin/lspci でちゃんとリストされ ている。カンドーした。

2008/04/05 PCIe IP コア開発 (川井)

上流からの request に対する completion を返すには、completion TLP の生 成の他にもいろいろやるべきことがあった:

など。結局ひととおり作るのに 4 日かかった。Completion と Configuration Register を実装したので、Configuration cycle が正しく動けばホストの boot 時に認識されるはずだが、実際は boot しない。なんでー?

2008/04/01 PCIe IP コア開発 (川井)

上流からの TLP を受け取れるようになったので、次はそれに返事を返す機能、 つまり Completion パケットの送出機能を実装する。仕様を読んだところ、 Completion TLP はデータの切れ目 (boundary) やサイズにいろいろ決まりご とがあって、作るのがひじょーに面倒だということがわかった。

Completion を生成するために必要な情報:

fmt & type    : TPTYPE_Cpl/TPTYPE_CplD
tc            : 受け取った TLP の tc。
td            : 0 (当面 ECRC はつけない)。
ep            : 0 (当面 data poisoning は行わない)。
attr          : 受け取った TLP の attr。
completer id  : 初めて CfgWr が来たときに自分の BDF を記録しておいて、以降はそれを返す。
cpl status    : CPLSTAT_SC (常に成功)。
bcm           : 0 (only for PCI-X. always 0 for PCIe).
requester id  : 受け取った TLP の requester id。
tag           : 受け取った TLP の tag。
length        : 受け取った TLP の length。
byte count    : 残りの byte 数。1 回の completion で全部返すので常に 0。
lower address : 受け取った TLP の address + 0 if 1st DW be="1111"
                                          + 1 if 1st DW be="1110"
                                          + 2 if 1st DW be="1100"
                                          + 3 if 1st DW be="1000"

Completion の満たすべき規則:

Configuration Read/Write の completion は payload 長さがそれぞれ 4byte, 0byte で固定なので、比較的簡単。まずはこれらを実装する。


先月   翌月
近況トップ