FPGA開発日記

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

リザベーションステーションの1サイクル先出し命令発行

リザベーションステーション(Reservation Station: RS)というのは、アウトオブオーダプロセッサにおける命令待ち合わせ機構の一つで、その命令が必要とするオペランドデータが、リザルトバスに流れてくるの監視している機構である。

リザルトバスに自命令の欲しいオペランドが流れてくるとそれを回収し、命令実行に必要なすべてのオペランドが揃うと命令を発行するという仕組みだ。

Reservation Station - Wikipedia

https://upload.wikimedia.org/wikipedia/commons/thumb/6/64/Intel_Nehalem_arch.svg/800px-Intel_Nehalem_arch.svg.png

(https://upload.wikimedia.org/wikipedia/commons/thumb/6/64/Intel_Nehalem_arch.svg/800px-Intel_Nehalem_arch.svg.png より画像引用)

命令を発行するには、RS内でリザルトバスを監視し、すべてのオペランドが揃わないと発行できないが、一般的にこれを高速化する方法の一つとしてタグの先読みという方法がある。

  • Understanding the detailed Architecture of AMD’s 64 bit Core

http://www.chip-architect.com/news/2003_09_21_Detailed_Architecture_of_AMDs_64bit_Core.html#1

The Tag busses run one cycle in advance of the result data-busses. The Reservation Station does not need to look at all the busses. The Tag’s sub-index identifies which of the three ALU’s will produce the result. It also knows if the data will come from one of the two cache read ports. It can select the Tag bus in advance rather then having to test all the Tags.

なるほど、オペランドのタグのみ1サイクル早く先出しし、タグが一致した時点で命令を発行、実行ユニット内で実際のリザルトデータを受け取ることで、RSから1サイクル命令を先出しすることができる。

とまあ、この機構は現在のRISC-V自作CPUでも使用している。ただし、ALUx2、LSU、BRUのうち使用しているのはALUx2の互いのリザルトバスだけだ。これを拡張してみるとどうなるのか。

f:id:msyksphinz:20170412010736p:plain

これを、LSUとBRU側にも実装した。LSUからのロードデータの先出をし、タグをLSUからも読めるようにする。 また、BRUに対しても先出のデータを参照できるようにした。

f:id:msyksphinz:20170412011007p:plain

前の実装と比べて、1割程度高速化された。しかしこの時点でCoremarkスコアはまだ3.0を切っている。どうにかして改善せねば。。。

f:id:msyksphinz:20170412011402p:plain

命令フェッチラインに基づく最適化

マイコンでは一般的なのかもしれないが、プログラムの最適化の1つの方法として命令フェッチラインに基づく最適化というものがある。

これは早い話が、ループの境界がなるべく命令フェッチの範囲を超えないようにし、フェッチ回数を減らすというものだ。

f:id:msyksphinz:20170411005821p:plain

例えば、4命令をループさせるプログラムがあったとして、

data_ready_loop:
    sw      x2,0x00(x1)
    // addi    x2,x2,0x01
    addi    x1,x1,4
    addi    x4,x4,1
    bltu    x4,x5,data_ready_loop

このプログラムをコンパイルすると、以下のようなアセンブラが生成された。

80000090 <data_ready_loop>:
80000090:       0020a023                sw      sp,0(ra) # 80002000 <_sp+0xffffde38>
80000094:       00408093                addi    ra,ra,4
80000098:       00120213                addi    tp,tp,1 # 1 <_tbss_end+0x1>
8000009c:       fe526ae3                bltu    tp,t0,80000090 <data_ready_loop>

現在、自作RISC-Vプロセッサのフェッチラインは128ビットだ。つまり、これが0x10の境界に入っていれば、一度にフェッチしてこれる。 これが、フェッチ境界を跨いでしまったらどうだろう。

800000c8 <data_ready_loop_hit2>:
800000c8:       0020a023                sw      sp,0(ra) # 80002000 <_sp+0xffffde78>
800000cc:       00408093                addi    ra,ra,4
800000d0:       00120213                addi    tp,tp,1 # 1 <_tbss_end+0x1>
800000d4:       fc526ee3                bltu    tp,t0,800000b0 <data_ready_loop_hit>

この場合、このループを実行するのに、2回のフェッチが必要になる。この2種類のループを、RTLシミュレーションで比較した。

  • フェッチアラインに合わせたもの: 151cycle
  • フェッチ境界を跨いだもの : 239cycle

結構な差が出てきた。これはフェッチを2回したことにより、無駄な命令発行をしているということでもある。

自作RISC-VのSystemVerilog化と命令発行方式の変更

ちまちまRISC-Vの自作プロセッサを改造している。まずは拡張性の向上のためにRISC-Vプロセッサの実装をSystemVerilogに置き換えている。

SystemVerilogの詳細についてはここでは述べないが、いくつか便利な記述があるので紹介。

packed struct

制御信号線周りについて、でコード信号を一度追加すると後でもう一本追加するのが大変だ。そこで、構造体を定義して信号線の拡張を容易にする。

typedef struct packed {
  logic         vld;
  logic [31: 0] inst;
  logic [31: 0] pc;
  logic         pr_en;
  logic         pr_taken;
} ic_type;

また、logic宣言した回路は、reg,wireの識別を行う必要がなく、組み合わせ回路の時にはalways_comb,順序回路の時はalways_ffで記述できるので便利だ。

wire w_signal;
reg  r_signal;

always @ (posedge clk, negedge reset_n) begin
  if (!reset_n) begin
     r_signal <= 1'b0;
  end else begin
     r_signal <= ...
  end
end

assign w_signal = ...

しかし、SystemVerilogの場合は、logic宣言をしていればalways_comb, always_ffのどちらを利用するかで組み合わせ回路か順序回路化を指定できる。

logic w_signal, r_signal;

always_ff @ (posedge clk, negedge reset_n) begin
  if (!reset_n) begin
     r_signal <= 1'b0;
  end else begin
     r_signal <= ...
  end
end

always_comb begin
  w_signal = ...
end

自作CPUの命令発行方式の変更

これらの記述方法を変更して拡張性を上げたのと、命令発行制御方式を変更した。これについては後日詳細を書きたい。

これの影響もあり、Coremarkスコアについては若干下がってしまった。以下はCoremarkのサイクル数だ。

サイクル数 stop_time()-start_time()
通常版 427167
発行方式変更版 472111

全体的にサイクル数が落ちている。これの解析を行う。 青が旧方式で、赤が新方式だ。

f:id:msyksphinz:20170409223813p:plain

レアジョブを初めて200回到達した

f:id:msyksphinz:20170407000125p:plain

英会話講座としてレアジョブを継続している。 レアジョブはSkypeを使った英会話教室だ。 講師はほとんどがフィリピン人だが、非常に格安、時間がどこでも選べるというのが利点だ。

www.rarejob.com

1年半前くらいから始めたのだが、1日30分のレッスンが200回に到達した。 だいたい3日に1回くらいやっている計算になる。

レアジョブを始めてから変わったのは、とりあえず3日に1回程度は英語をしゃべらざるを得ない状況に置かれるため、会話をするにあたる緊張感が減ってきたこと(最初のころは緊張で汗をかきながら話していた)、 顔が見えない状況で、音声だけでどうにか会話する技術がついたことくらいだろうか。

会話スキル自体については、聞き返す方法のバリエーションが増えたくらいで、いまいち向上していない。 まあ、これも継続かな。

Zephyr-RISCVはFreedom E310プラットフォーム(Arty FPGA)をサポートしている

f:id:msyksphinz:20170325154038p:plain

前回少し紹介したZephyr-OSは、実行プラットフォームとしてQEMUだけでなく、Arty FPGA上で動作するFreedom E310もサポートしている。

ただし、OpenOCDによる接続が必要なため、ArtyFPGAに少し配線を追加しないといけないようだ。 手持ちにデバッグ用のケーブルがないので、実機で確かめることはできないが、とりあえず調査してみよう。

msyksphinz.hatenablog.com

Zephyr-OSのRISC-V向けビルド

基本的に以下のgithubにあげてあるREADME.mdが全てだ。

github.com

qemu-riscv32, riscv-gnu-toolchainなどをちゃんとインストールしておくこと。

Zephy-SDKをインストールしておく。これが何に使われるのかはよく分かっていない。 ただし、QEMUでシミュレーションするときに必要になるようだ。

$ sudo ./zephyr-sdk-0.9-setup.run
[sudo] password for msyksphinz:
Verifying archive integrity... All good.
Uncompressing SDK for Zephyr  100%
Enter target directory for SDK (default: /opt/zephyr-sdk/):
Installing SDK to /opt/zephyr-sdk
Creating directory /opt/zephyr-sdk
Success
 [*] Installing x86 tools...
 [*] Installing arm tools...
 [*] Installing arc tools...
 [*] Installing iamcu tools...
 [*] Installing nios2 tools...
 [*] Installing xtensa tools...
 [*] Installing riscv32 tools...
 [*] Installing additional host tools...
Success installing SDK. SDK is ready to be used.

make BOARD=arty_fe310 でArtyFPGA用のバイナリをビルドできる。試しにHifive1で実行できるだろうか。

f:id:msyksphinz:20170404233258p:plain

できなかった。これはシリアルコンソールのレートがあっていないだけのようにも思える。試しにいろいろ変更してみたが上手く行かなかった。 fe310を使うにあたり、drivers/serial/uart_fe310.cのボーレートを利用するようなので、HiFive1のリファレンスをもとに弄ってみたが上手く行かなかった。

UARTの部分にオシロスコープを当てられれば良いのだが、それっぽいピンが無い。ちょっと難しそうだなあ。

ParallelaのオープンソースハードウェアIP

Parallela本体はさすがに無いけれども、Parallelaの周辺回路や制御するための周辺回路はGithubにいくつか公開されている。

github.com

巨大な回路は、使い道がないかもだけれども、FIFOとかデュアルポートメモリインタフェースとか、地味に便利かもね。

github.com

f:id:msyksphinz:20170401171258p:plain

RISC-V on ZedBoard for Parallelaのビルド実験

試しに、RISC-V on Parallela ボードのRISC-Vデザインをビルドしてみた。

リポジトリは以下に存在している。

https://github.com/eliaskousk/parallella-riscv

ここでは、Vivadoを用いて合成するので、LinuxマシンにVivadoがインストールされているのが前提だ。

まずはデザインをcloneしてくる。

git clone https://github.com/eliaskousk/parallella-riscv.git

以降で、必要なsubmodulesはビルドスクリプトが勝手にフェッチしてくるので問題ない。

コンフィグレーションの変更を行う。今回は、RISC-VをZedBoardで動かしたいのと、Vivadoを2016.4で動作させるので、以下のような変更を行う。

  • scripts/settings.sh
diff --git a/scripts/settings.sh b/scripts/settings.sh
index 7f7dca2..e651f48 100644
--- a/scripts/settings.sh
+++ b/scripts/settings.sh
@@ -1,10 +1,10 @@
 #!/bin/bash

-BOARD=parallella
+BOARD=zedboard
 export BOARD
 echo "\$BOARD set to ${BOARD}"

-JOBS=8
+JOBS=4
 export JOBS
 echo "\$JOBS set to ${JOBS}"

@@ -12,7 +12,7 @@ VIVADO_PATH=/opt/Xilinx/Vivado
 export VIVADO_PATH
 echo "Vivado path set to ${VIVADO_PATH}"

-VIVADO_VERSION=2015.4
+VIVADO_VERSION=2016.4
 export VIVADO_VERSION
 echo "Vivado version set to ${VIVADO_VERSION}"

次に、ホストのソフトウェアをビルドする。もしかしたらこれは必要ないかもしれない。

sudo apt install openssl-dev  # 必要なファイルのインストール
./scripts/build.host.software.sh

RISC-Vデザインのビルドを行う。Vivadoが立ち上がり、合成が始まる。

./scripts/build.host.software.sh

しばらく待っているとビルドが終了するので、dcp(design check point)のファイルを読み込んで何がどうなっているのか確認してみよう。

vivado ./zedboard/fpga/zedboard_riscv/system.runs/impl_1/system_wrapper_opt.dcp

f:id:msyksphinz:20170401165650p:plain

当たり前だがほとんどがRISC-Vのデザインとなり、いくつかAXIのインタフェースを備えている。