FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Rocket Chip on ZedBoardはどこまで周波数を上げられるのか (2. SmallConfigの場合)

f:id:msyksphinz:20170925003926p:plain

Rocket Chipの動作周波数を上げるべくいろいろ試行していたが、ZynqSmallConfigの場合はどれくらい周波数が上がるのだろう?

ZedBoardでFPGA向けに合成をする場合にはいくつかオプションがあり、デフォルトではZynqConfig、小さめの構成でZynqSmallConfigというのがある。 ZynqSmallConfigは具体的にどのような構成なのかというと、以下のChiselの資料を見るとなんとなく分かる。

class WithSmallCores extends Config (
  topDefinitions = { (pname,site,here) => pname match {
    case MulDivKey => Some(MulDivConfig())
    case FPUKey => None
    case UseVM => false
    case NTLBEntries => 4
    case BtbKey => BtbParameters(nEntries = 0)
    case NAcquireTransactors => 2
    case _ => throw new CDEMatchError
  }},
  knobValues = {
    case "L1D_SETS" => 64
    case "L1D_WAYS" => 1
    case "L1I_SETS" => 64
    case "L1I_WAYS" => 1
    case "L1D_MSHRS" => 0
    case _ => throw new CDEMatchError
  }
)
  • MUL/DIV演算器の簡素化 (アンローリングの数などを減らしている)
  • FPUを除去
  • VMモードを削除
  • キャッシュ系のエントリ数を変更

このZynqSmallConfigを使って動作周波数を上げていき、33MHz, 50MHz, 75MHz でFPGAインプリメントを実行し、タイミングを見てみた。

WNS[ns] Slice LUT Sliced Registers
25MHz 2.010 35201 15591
25MHz 17.706 13250 7123
33MHz 6.182 13247 7123
50MHz 0.728 13245 7123
75MHz -5.299 13285 7123

せいぜいZynqSmallConfigでも50MHzでしか動作しない。というか、Rocket Core の外側のアービタの部分でViolationが出てしまった。

Max Delay Paths
--------------------------------------------------------------------------------------
Slack (VIOLATED) :        -5.299ns  (required time - arrival time)
  Source:                 top/FPGAZynqTop/adapter/addr_reg[4]/C
                            (rising edge-triggered cell FDRE clocked by host_clk_i  {rise@0.000ns fall@6.667ns period=13.333ns})
  Destination:            top/FPGAZynqTop/DefaultCoreplex/L2BroadcastHub/iacqTrackerList_0/data_buffer_6_reg[59]/D
                            (rising edge-triggered cell FDRE clocked by host_clk_i  {rise@0.000ns fall@6.667ns period=13.333ns})
  Path Group:             host_clk_i
  Path Type:              Setup (Max at Slow Process Corner)
  Requirement:            13.333ns  (host_clk_i rise@13.333ns - host_clk_i rise@0.000ns)
  Data Path Delay:        18.330ns  (logic 7.544ns (41.157%)  route 10.786ns (58.843%))
  Logic Levels:           35  (CARRY4=18 LUT2=1 LUT3=4 LUT4=4 LUT5=2 LUT6=6)
  Clock Path Skew:        -0.247ns (DCD - SCD + CPR)
    Destination Clock Delay (DCD):    5.926ns = ( 19.260 - 13.333 )
    Source Clock Delay      (SCD):    6.546ns
    Clock Pessimism Removal (CPR):    0.373ns
  Clock Uncertainty:      0.086ns  ((TSJ^2 + DJ^2)^1/2) / 2 + PE
    Total System Jitter     (TSJ):    0.071ns
    Discrete Jitter          (DJ):    0.158ns
    Phase Error              (PE):    0.000ns

Rocket Chip on ZedBoardはどこまで周波数を上げられるのか?(1. DefaultFPGAConfigの場合)

f:id:msyksphinz:20170923120007p:plain

Rocket ChipをFPGAインプリメントして動作させているが、動作周波数が25MHzに設定されているというのに驚愕した。 ZedBoard自体は100MHzで動くはずだし、HiFive1だって320MHzで動作するような設計だ。 なぜこんなに遅い周波数で設計されているのだろう?

調査してみると、Rocket Chipの動作周波数をコンフィグレーションで変更できることが分かった。 では、Violationが出ない程度に周波数を上げていくとどこまで動作させることができるのだろう?

ZedBoard向けコンフィグレーションにて、Rocket Chipコアの動作周波数を決定しているのはfpga-zynqリポジトリの以下のファイルになる。

`ifndef _clocking_vh_
`define _clocking_vh_


`define ZYNQ_CLK_PERIOD 10.0

`define RC_CLK_MULT     10.0

`define RC_CLK_DIVIDE   40.0

コメントにも書いてあったが、このパラメータを変えることで、Rocket Chipの動作周波数を変えることが出来る。

 \text{Rocket Chipの動作周波数} = \dfrac{1000}{\text{ZYNQ_CLK_PERIOD}}\times\dfrac{\text{RC_CLK_MULT}}{\text{RC_CLK_PERIOD}}

従って、現在は  \text{Rocket Chipの動作周波数} = \dfrac{1000}{10.0}\times\dfrac{10.0}{40.0} で、25MHzに設定されている。では、RC_CLK_DIVIDEを30.0に設定するとどうなるだろう(つまり33MHzで動かす)?

結論としては、Rocket CoreのFMAパイプラインが盛大にViolationした。こりゃ、25MHzの動作でもギリギリなんだろうな。

Max Delay Paths
--------------------------------------------------------------------------------------
Slack (VIOLATED) :        -5.617ns  (required time - arrival time)
  Source:                 top/FPGAZynqTop/DefaultCoreplex/tiles_0/fpuOpt/FPUFMAPipe/in_in2_reg[61]/C
                            (rising edge-triggered cell FDRE clocked by host_clk_i  {rise@0.000ns fall@15.000ns period=30.000ns})
  Destination:            top/FPGAZynqTop/DefaultCoreplex/tiles_0/fpuOpt/FPUFMAPipe/_T_205_data_reg[53]/S
                            (rising edge-triggered cell FDSE clocked by host_clk_i  {rise@0.000ns fall@15.000ns period=30.000ns})
  Path Group:             host_clk_i
  Path Type:              Setup (Max at Slow Process Corner)
  Requirement:            30.000ns  (host_clk_i rise@30.000ns - host_clk_i rise@0.000ns)
  Data Path Delay:        34.976ns  (logic 17.118ns (48.943%)  route 17.858ns (51.057%))
  Logic Levels:           55  (CARRY4=29 DSP48E1=3 LUT2=2 LUT3=3 LUT4=2 LUT5=3 LUT6=12 MUXF7=1)
  Clock Path Skew:        -0.125ns (DCD - SCD + CPR)
    Destination Clock Delay (DCD):    6.163ns = ( 36.163 - 30.000 )
    Source Clock Delay      (SCD):    6.792ns
    Clock Pessimism Removal (CPR):    0.505ns
  Clock Uncertainty:      0.087ns  ((TSJ^2 + DJ^2)^1/2) / 2 + PE
    Total System Jitter     (TSJ):    0.071ns
    Discrete Jitter          (DJ):    0.159ns
    Phase Error              (PE):    0.000ns

ただし、FMAだけならば、浮動小数点を使わないデザインだったらさらに高速化できる気がする。64bitでFPUを除去したデザインとか、Chiselのコンフィグレーションを変えると作れないかしら。

Rocket ChipのChiselを使ってアクセラレータを作る(Dot Productアクセラレータの性能評価)

前回、Chiselを使ってDot Productアクセラレータを作ったが、まだ正しく答えが一致していなかった。

msyksphinz.hatenablog.com

一応デバッグしてプログラムを正しく走ることが出来るようにして、まずは16x16のサイズで正しく問題が計算されていることを確認した。

f:id:msyksphinz:20170923021716p:plain

肝心の性能だが、

構成 サイクル数
Dot Productアクセラレータによる計算 15917
(16x16に決め打ちした)アクセラレータによる計算 11359
ソフトウェアによる計算 46396

と、決め打ちのアクセラレータに比べたら遅いのは仕方がない。でも一応汎用化できたので、次はFPGAに乗せて動作確認をする。

具体的には、以下のようなアクセラレータを呼び出す関数を組んで、行列積を計算させた。

void matrixmul_hw1 (int64_t *A, int64_t *B)
{
  int64_t output_data[N*K];
  uint64_t start_cycle, stop_cycle;

  printf ("Hardware Start <MatrixMul TwoRequester>\n");
  start_cycle = read_csr(mcycle);
  uint64_t dummy;
  matrixmul_setlen (dummy, M);
  for (int j = 0; j < N; j++) {
    for (int i = 0; i < K; i++) {
      matrixmul_two (output_data[j*N+i], &(input1_data[j*N]), &(input2_data[i]));
    }
  }
  stop_cycle = read_csr(mcycle);
  printf ("Hardware Finished. %ld-%ld=%ld\n", stop_cycle, start_cycle, stop_cycle-start_cycle);
  show_result(output_data);
}

matrixmul_setlen()が、行列積を計算する場合の計算の長さを決める(行列Aの列数=行列Aの行数)を設定している。今回は、行列 A(50\times 203), B(203\times 172) を用意して計算する。 matrixmul_two() が、実際にDot Productを計算する部分だ。計算したい (i,j) の該当する行と列のポインタを入力して、 matrixmul_setlen()で設定した回数だけDot Productを計算する。

かなりでかいので、シミュレーションでは測定できない。とりあえずFPGAに焼いて確認かな。

Dot Product アクセラレータをFPGAに焼いて動作確認

とりあえず前回と同じ方法でFPGAに書き込んで動作させてみた。なんだかソフトウェアと答えが合わないので要検算だが、だいたい、

構成 サイクル数
Dot Productアクセラレータによる計算 22700378
ソフトウェアによる計算 52869863

速度的にはせいぜい2倍程度になってしまったなあ。何が悪いんだろう?要解析。

f:id:msyksphinz:20170923031737p:plain

Rocket ChipのChiselを使ってアクセラレータを作る (より大きな行列に対応したDot Productハードウェア)

Chiselを使って行列積を計算するハードウェアを開発してきたが、これには弱点がある。 小さな行列に焦点を当てたので、大きな行列(16行以上)に対応できないことだ。

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com

しかしこの方法には利点もある。16行に限定したため内部にレジスタキャッシュを持っており、同じアドレスをフェッチする場合はフェッチリクエストを省略し、高速化した。 しかしより大きな行列を計算する場合はそうもいかない。例えば数千行の行列を計算するのにすべて内部レジスタに保持しておくわけにもいかないので、別の方法を考える必要がある。

今回より行列サイズを一般化して、レジスタファイルキャッシュを省略し、どのような行列サイズでも実現できるアクセラレータを作ってみる。

イデアとしては、これまで行と列を順番にフェッチしてきたが、これを編み込む。

f:id:msyksphinz:20170922030906p:plain

これにより、最初にフェッチした行を全て記憶しておく必要が無いので、いくらでも行列サイズを大きくすることが出来る。 この場合L1の局所性が心配になる。改善策としてはある程度の塊で一気に取ってくる方法があるが、まあ列方向だとそれは効きそうにないので、今回は省略。

で、とりあえず今回はアイデア出しと、簡単な実装のみ実現した。

命令としてはカスタム命令に対してFunct=0, Funct=1の2種類を用意した。

  • Funct=0、行列サイズ(行列Aの縦方向 = 行列Bの横方向)を指定する。
  • Funct=1、メモリをフェッチしDot Productを実行する

の2種類を作った。最初にFunct=0はレジスタ戻り値を使わないので適当に返していたところRocket Coreが固まってしまい原因を突き止めるのに相当な時間がかかってしまった。

  when (io.cmd.fire() && setLength) {
    printf("MatrixMulTwoRequester: SetLength Request. %x\n", io.cmd.bits.rs1)
    r_matrix_max := io.cmd.bits.rs1
    r_recv_state := s_recv_finish
  }

  // たとえ使わなくてもきちんと初期化して返すこと!
  when (io.cmd.fire()) {
    r_total      := UInt(0)
    r_resp_rd := io.cmd.bits.inst.rd
  }

  when (io.cmd.fire() && doCalc) {
    printf("MatrixMulTwoRequester: DoCalc Received. %x, %x\n", io.cmd.bits.rs1, io.cmd.bits.rs2)

    r_v_addr     := io.cmd.bits.rs2
    r_h_addr     := io.cmd.bits.rs1

    r_recv_count := UInt(0)
    r_cmd_count  := UInt(0)
    r_tag        := UInt(0)

    r_cmd_state  := s_mem_fetch
    r_recv_state := s_mem_recv
  }

とりあえず今日はここまで。まだ答えはあっていないが、ステートマシンは動作しているみたいだ。きちんとデバッグしていこう。

OpenVX実装についての調査とチュートリアル試行

ザイリンクスのreVISIONスタックは、コンピュータビジョン、機械学習、コネクティビティ & センサー サポート に注力してFPGA上で動作させることのできるハードウェアデザインやソフトウェアスタックを提供している。

このなかでコンピュータビジョンに注目してみると、コンピュータビジョン向けの最も有名なライブラリ群OpenCVをベースにしたザイリンクスのxfOpenCVを提供している。

  • 高い応答性とリコンフィギャラブルなビジョン システム

https://japan.xilinx.com/products/design-tools/embedded-vision-zone.html#computer

この中で、提供しているライブラリは、上記のウェブサイトによると、

  • OpenCV 3.1 ライブラリの数百以上の関数が、Zynq の ARM Cortex™-A9 および Cortex A53 コアで実行するために利用可能
  • 約 45 個の OpenCV 関数 (OpenVX のサブセット) が、ザイリンクス SoC 向けに RTL に最適化された関数ライブラリとして利用可能
  • バイス使用率と性能を示したライブラリ ユーザー ガイド
  • ほとんどの関数は、1 および 8 ピクセルのパラレル バージョンに対応

OpenCVはARM Cortexで動作させ、OpenVXのサブセットはSoCやRTL実装されているということだろうか?興味がある。 そもそもOpenVXというのは画像認識のためのAPIで、現在はバージョン1.2まで定義されているが実装としてはnVIDIAAMDによる実装しか存在していない。

www.khronos.org

VisionWorks | NVIDIA Developer

AMD OpenVX (AMDOVX) - GPUOpen

とりあえずWindows上で手っ取り早く試してみる方法として、他者が提供している仮想マシンをダウンロードして実行してみる方法がある。

GitHub - rgiduthuri/openvx_tutorial: Khronos OpenVX Tutorial Material

VirtualBoxの仮想イメージをダウンロードして、実行してみると、いくつかプロジェクトが立ち上がってOpenVXのサンプルを実行できる。

f:id:msyksphinz:20170920231102p:plain

それ以外にも、日本語のチュートリアルも存在する。これらをいくつか触ってみて、OpenVXの理解を深めていこうかな。

github.com

RoCCアクセラレータを乗せたRocket ChipのFPGA論理合成(4. 自作アクセラレータの論理合成)

RISC-Vの実装、Rocket Chipのアクセラレータ向けインタフェースにハードウェアを実装し、RTLシミュレーションを行った。 次に、Rocket ChipをFPGA向けに論理合成するところまで上手く行ったので、今回開発したRoCCアクセラレータをFPGAに実装してみよう。 これで自作アクセラレータが正しく動作すれば、成功だ。

fpga-zynq で Rocket Chipリポジトリを差し替える

実際、Rocket Chipは結構な頻度で仕様や実装が変更されている。今回は割と最近のRocket Chipのリポジトリをベースにアクセラレータを開発した。 具体的には以下のコミットをベースに、ブランチを作って拡張を行っている。

github.com

しかし、fpga-zynqのリポジトリが現在サブリポジトリとして指定しているのはかなり古いrocket-chipのリポジトリだ。実際、半年前のRocket Chipの実装を指しており、このサブモジュールを最新までアップデートして論理合成を試行してみたのだが、上手く行かなかった。

github.com

そこで、fpga-zynqが指しているRocket Chipのサブモジュールのリビジョンに、今回開発した自作アクセラレータの差分のみ適用し、そのまま論理合成を実行する。 この場合、RoCCアクセラレータのインタフェースとインスタンスの方法が少し変わっているため、そのままアクセラレータを乗せることはできなかったが、僅かな改変で一応、論理合成は上手く行ったようだ。

f:id:msyksphinz:20170920010910p:plain

気になる動作周波数の方だが、そもそもRocket Chipの動作周波数が20MHzとなるようにクロック制約が組まれているため(!)、余裕でMetしている。 きちんと実装した行列演算アクセラレータも実装されているようだ。

あとは、これをFPGAに実装して動作確認してみるだけとなる。いつものテストプログラムをSDカードに読み込んで、FPGALinuxをブートさせた。 そしてRocket Chipのフロントエンドサーバを起動させる。

root@zynq:~# ./fesvr-zynq test-matrixmul.riscv

f:id:msyksphinz:20170920013000p:plain

よし!動いた! FPGAでRoCCアクセラレータの動作に成功したぞ!パフォーマンスも想定通りだ。

「ディジタル回路設計とコンピュータアーキテクチャ 第2版」が出版されていた

ディジタル回路設計とコンピュータアーキテクチャ 第2版

ディジタル回路設計とコンピュータアーキテクチャ 第2版

  • 作者: デイビッド・マネーハリス,サラ・L.ハリス,David Money Harris,Sarah L. Harris,天野英晴,鈴木貢,中條拓伯,永松礼夫
  • 出版社/メーカー: 翔泳社
  • 発売日: 2017/09/11
  • メディア: 単行本
  • この商品を含むブログを見る

いわゆるパタヘネ、ヘネパタと比較して、より実践より(Verilog-HDLでの実装とか)に重きを置いている「ディジタル回路設計とコンピュータアーキテクチャ」の第2版の日本語版が出版されていた。

第1版は、内容をちょっと確認して、基礎的なところで終わりそうだったので購入していなかったのだが、第2版ではどこが更新されたのだろう。

そんな、マイクロプロセッサを作りながら学ぶ『ディジタル回路設計とコンピュータアーキテクチャ』が、System Verilogに対応してバージョンアップしました。

なるほど、実装がSystem Verilogに対応したというところがメインか。System Verilog対応により、実装記述がよりシンプルになっているのならチェックする価値があるのかもしれない。 とりあえずは、本屋で立ち読みしてみて、購入するかどうか考えようかな。 内容はあまり第1版と変わってなさそう。