FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io

AWS上で動作するRISC-VチップFireSimのチュートリアルを試す 4. Single Core Roctket-Chipの動作

FireSimのチュートリアルを試す続き。前回はなぜかFireSimが起動せずに終わった。

色々調べていくと、FireSimを立ち上げる際のF1インスタンスの設定で、t1.nanoで生成したVPCとサブネットを使用しなければならなかった。

AWSコンソール上でこれを確認してF1インスタンスを作り直すと、正しくFireSimの設定ができるようになった。

前回失敗したところからやり直して、ソフトウェアのビルド、コンフィグレーションの設定を行う。

次に、Farmの立ち上げをやり直す。

$ firesim launchrunfarm
FireSim Manager. Docs: http://docs.fires.im
Running: launchrunfarm

Waiting for instance boots: f1.16xlarges
Waiting for instance boots: m4.16xlarges
Waiting for instance boots: f1.2xlarges
i-09d74249c75ecbc8e booted!
The full log of this run is:
/home/centos/firesim/deploy/logs/2018-06-22--14-16-08-launchrunfarm-BFTH799Z7TMEBRWK.log

これは何をしているのかというと、上記で設定したrunfarmの設定を実行している。これにより、EC2でのRocket-Chipの実行が始まる。上記の設定により、f1_2xlargesが使用されるシングルノードのRocket-Chipが実行される。

  • runfarmの設定(firesim/deploy/config_runtime.iniより抜粋)
[runfarm]
runfarmtag=mainrunfarm

f1_16xlarges=0
m4_16xlarges=0
f1_2xlarges=1

runinstancemarket=ondemand
spotinterruptionbehavior=terminate
spotmaxprice=ondemand

次に、シミュレーションインフラを立ち上げる。Rocket-Chipに必要なソフトウェアインフラやネットワークの設定が行われる。

$ firesim infrasetup
FireSim Manager. Docs: http://docs.fires.im
Running: infrasetup


Building FPGA software driver for FireSimNoNIC-FireSimRocketChipQuadCoreConfig-FireSimDDR3FRFCFSLLC4MBConfig
[192.168.1.98] Executing task 'instance_liveness'
[192.168.1.98] Checking if host instance is up...
[192.168.1.98] Executing task 'infrasetup_node_wrapper'
[192.168.1.98] Copying FPGA simulation infrastructure for slot: 0.
[192.168.1.98] Installing AWS FPGA SDK on remote nodes.
[192.168.1.98] Unloading EDMA Driver Kernel Module.
[192.168.1.98] Copying AWS FPGA EDMA driver to remote node.
[192.168.1.98] Clearing FPGA Slot 0.
[192.168.1.98] Flashing FPGA Slot: 0 with agfi: agfi-0eaa90f6bb893c0f7.
[192.168.1.98] Loading EDMA Driver Kernel Module.
The full log of this run is:
/home/centos/firesim/deploy/logs/2018-06-22--14-18-25-infrasetup-MRHU7BID9CAQJA1E.log

最後に、シミュレーションを実行しよう。

$ firesim runworkload

これによりFireSimが立ち上がりシミュレーションが始まるのだが、正常に立ち上がると以下のような画面が表示され、F1インスタンスでRocket-Chipが実行中であるということが示される。 これは常に表示されており、常に自動的に状態が更新されるようになっている。

f:id:msyksphinz:20180622233120p:plain

といわけでコンソールが埋まってしまったので、別のコンソールを立ち上げてc1.4xlargeインスタンスにログインする。 firesimディレクトリに移りsource sourceme-f1-manager.sh で再び環境を呼び込み、F1インスタンス上で実行しているRocket-Chipに対してログインを実行する。

上記の画面では、 192.168.1.98 に対してRocket-Chipが生成されたので、

ssh 192.168.1.98

おっしてscreenを立ち上げてシリアルコンソールを確認する。

screen -r fsim0

とすると、Rocket-Chip上でLinuxが立ち上がっておりログイン画面が表示されている。 ブート成功だ!

f:id:msyksphinz:20180622233054p:plain

さっそくログインする。ログイン名はroot, パスワードはfiresimだ。 ログインできるところまで来ると、シミュレーションは成功だ。

後片付け

シミュレーションを終了するときは、Linux上でpoweroff -fを実行する。

するとRocket-Chipがシャットダウンし、F1インスタンス上で表示されたRocket-Chipの状態を示すコンソールも自動的に閉じられる。

そして、最後に後片付けをして完了だ。

firesim terminaterunfarm

ここまでで、シングルノードのRocket-Chipの実行チュートリアルが完了した。

AWS上で動作するRISC-VチップFireSimのチュートリアルを試す 3. ソフトウェアのビルドと環境設定

f:id:msyksphinz:20180617195844p:plain

AWSで動作するRISC-Vシミュレーション環境FireSimのチュートリアルその3.

F1インスタンスにアクセスするために、生成したキーペアのfiresim.pemをホームディレクトリに配置しておく。 また、sshのキーと同様に、chmod 600 しておく。

$ chmod 600 ~/firesim.pem   # firesim.pemを配置してから実行

リポジトリのダウンロードとビルド

まずはFireSimのリポジトリのダウンロードとビルドを行う。これには少し時間がかかるが、c4インスタンスなので早い。

git clone https://github.com/firesim/firesim
cd firesim
./build-setup.sh fast

次に設定ファイルをsourceする。これはログインするたびに実行しなければならない。

$ source sourceme-f1-manager.sh

次に、FireSimのマネージャを起動する。

$ firesim managerinit

ソフトウェアのビルド

次はソフトウェアのビルドだ。これには約10~15分かかる。

$ cd firesim/sw/firesim-software
$ ./build.sh

次に、FireSimのコンフィグレーションを行う。ここでは、シングルノードで、Rocket-Chip間のネットワークは使用しないため、以下のように設定を書き換える。

  • firesim/deploy/config_runtime.ini
# RUNTIME configuration for the FireSim Simulation Manager
# See docs/Configuration-Details.rst for documentation of all of these params.

[runfarm]
runfarmtag=mainrunfarm

f1_16xlarges=0
m4_16xlarges=0
f1_2xlarges=1

runinstancemarket=ondemand
spotinterruptionbehavior=terminate
spotmaxprice=ondemand

[targetconfig]
topology=no_net_config
no_net_num_nodes=1
linklatency=6405
switchinglatency=10
netbandwidth=200

# This references a section from config_hwconfigs.ini
# In homogeneous configurations, use this to set the hardware config deployed
# for all simulators
defaulthwconfig=firesim-quadcore-no-nic-ddr3-llc4mb

[workload]
workloadname=linux-uniform.json
terminateoncompletion=no

最後に以下を実行してFireSimを立ち上げるのだが、エラーを吐いてしまった。

$ firesim launchrunfarm

Running: launchrunfarm

An error occurred (InvalidKeyPair.NotFound) when calling the RunInstances operation: The key pair 'firesim' does not exist
This probably means there was no more capacity in this availability zone. Try the next one.
An error occurred (InvalidKeyPair.NotFound) when calling the RunInstances operation: The key pair 'firesim' does not exist
This probably means there was no more capacity in this availability zone. Try the next one.
An error occurred (InvalidKeyPair.NotFound) when calling the RunInstances operation: The key pair 'firesim' does not exist
This probably means there was no more capacity in this availability zone. Try the next one.
An error occurred (InvalidKeyPair.NotFound) when calling the RunInstances operation: The key pair 'firesim' does not exist
This probably means there was no more capacity in this availability zone. Try the next one.
An error occurred (InvalidKeyPair.NotFound) when calling the RunInstances operation: The key pair 'firesim' does not exist
This probably means there was no more capacity in this availability zone. Try the next one.
An error occurred (InvalidKeyPair.NotFound) when calling the RunInstances operation: The key pair 'firesim' does not exist
This probably means there was no more capacity in this availability zone. Try the next one.
we tried all subnets, but there was insufficient capacity to launch your instances
only the following 0 instances were launched
[]
Waiting for instance boots: f1.16xlarges
Waiting for instance boots: m4.16xlarges
Waiting for instance boots: f1.2xlarges
Fatal error.
Traceback (most recent call last):
  File "/home/centos/firesim/deploy/firesim", line 300, in <module>
    main(args)
  File "/home/centos/firesim/deploy/firesim", line 249, in main
    globals()[args.task](simconf)
  File "/home/centos/firesim/deploy/firesim", line 137, in launchrunfarm
    runtime_conf.runfarm.launch_run_farm()
  File "/home/centos/firesim/deploy/runtools/run_farm.py", line 198, in launch_run_farm
    wait_on_instance_launches(f1_2s, 'f1.2xlarges')
  File "/home/centos/firesim/deploy/awstools/awstools.py", line 230, in wait_on_instance_launches
    for instance in instances:
TypeError: 'NoneType' object is not iterable
The full log of this run is:
/home/centos/firesim/deploy/logs/2018-06-21--13-18-24-launchrunfarm-WWZ1V5VC1UMFXIY9.log

Ubuntu 18.04でsbtが動かない問題(というか結果的にRocket-Chipが動かない問題)を何とかする

Ubuntuが18.04 LTSになって、さっそくバージョンアップしてRISC-Vの環境を構築しようとしたのだけれども問題にぶち当たった。

RISCV-Toolsは以下のエントリでインストールの試行はしていたし、うまく行くことは確認していたのだけれども、Rocket-Chipの試行はしていなかったので実行してみた。

msyksphinz.hatenablog.com

Ubuntu 18.04 LTSをインストールしたままで実行すると、以下のようなエラーでsbtがダウンロード出来ない。

$ make CONFIG=DefaultConfig
make -C /home/msyksphinz/work/rocket-chip-msyksphinz/firrtl SBT="java -Xmx2G -Xss8M -XX:MaxPermSize=256M -jar /home/msyksphinz/work/rocket-chip-msyksphinz/sbt-launch.jar" root_dir=/home/msyksphinz/work/rocket-chip-msyksphinz/firrtl build-scala
make[1]: Entering directory '/home/msyksphinz/work/rocket-chip-msyksphinz/firrtl'
java -Xmx2G -Xss8M -XX:MaxPermSize=256M -jar /home/msyksphinz/work/rocket-chip-msyksphinz/sbt-launch.jar "assembly"
OpenJDK 64-Bit Server VM warning: Ignoring option MaxPermSize; support was removed in 8.0
Getting org.scala-sbt sbt 1.1.1  (this may take some time)...

:: problems summary ::
:::: WARNINGS
                module not found: org.scala-sbt#sbt;1.1.1

        ==== local: tried

          /home/msyksphinz/.ivy2/local/org.scala-sbt/sbt/1.1.1/ivys/ivy.xml

          -- artifact org.scala-sbt#sbt;1.1.1!sbt.jar:

          /home/msyksphinz/.ivy2/local/org.scala-sbt/sbt/1.1.1/jars/sbt.jar

        ==== local-preloaded-ivy: tried

          file:////home/msyksphinz/.sbt/preloaded/org.scala-sbt/sbt/1.1.1/ivys/ivy.xml

        ==== local-preloaded: tried

          file:////home/msyksphinz/.sbt/preloaded/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.pom

          -- artifact org.scala-sbt#sbt;1.1.1!sbt.jar:

          file:////home/msyksphinz/.sbt/preloaded/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.jar

        ==== Maven Central: tried

          https://repo1.maven.org/maven2/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.pom

          -- artifact org.scala-sbt#sbt;1.1.1!sbt.jar:

          https://repo1.maven.org/maven2/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.jar

        ==== sbt-maven-releases: tried

          https://repo.scala-sbt.org/scalasbt/maven-releases/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.pom

          -- artifact org.scala-sbt#sbt;1.1.1!sbt.jar:

          https://repo.scala-sbt.org/scalasbt/maven-releases/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.jar

        ==== sbt-maven-snapshots: tried

          https://repo.scala-sbt.org/scalasbt/maven-snapshots/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.pom

          -- artifact org.scala-sbt#sbt;1.1.1!sbt.jar:

          https://repo.scala-sbt.org/scalasbt/maven-snapshots/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.jar

        ==== typesafe-ivy-releases: tried

          https://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/sbt/1.1.1/ivys/ivy.xml

        ==== sbt-ivy-snapshots: tried

          https://repo.scala-sbt.org/scalasbt/ivy-snapshots/org.scala-sbt/sbt/1.1.1/ivys/ivy.xml

                ::::::::::::::::::::::::::::::::::::::::::::::

                ::          UNRESOLVED DEPENDENCIES         ::

                ::::::::::::::::::::::::::::::::::::::::::::::

                :: org.scala-sbt#sbt;1.1.1: not found

                ::::::::::::::::::::::::::::::::::::::::::::::


:::: ERRORS
        Server access Error: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty url=https://repo1.maven.org/maven2/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.pom

        Server access Error: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty url=https://repo1.maven.org/maven2/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.jar

        Server access Error: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty url=https://repo.scala-sbt.org/scalasbt/maven-releases/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.pom

        Server access Error: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty url=https://repo.scala-sbt.org/scalasbt/maven-releases/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.jar

        Server access Error: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty url=https://repo.scala-sbt.org/scalasbt/maven-snapshots/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.pom

        Server access Error: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty url=https://repo.scala-sbt.org/scalasbt/maven-snapshots/org/scala-sbt/sbt/1.1.1/sbt-1.1.1.jar

        Server access Error: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty url=https://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/sbt/1.1.1/ivys/ivy.xml

        Server access Error: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty url=https://repo.scala-sbt.org/scalasbt/ivy-snapshots/org.scala-sbt/sbt/1.1.1/ivys/ivy.xml

GitHubのIssueでも聞いてみた。その結果sbtの問題であることが分かり、リンク先をたどっていた結果以下のページに到達。

git.mikael.io

つまり、SuperUserで以下の2行を実行する。

/usr/bin/printf '\xfe\xed\xfe\xed\x00\x00\x00\x02\x00\x00\x00\x00\xe2\x68\x6e\x45\xfb\x43\xdf\xa4\xd9\x92\xdd\x41\xce\xb6\xb2\x1c\x63\x30\xd7\x92' > /etc/ssl/certs/java/cacerts
/var/lib/dpkg/info/ca-certificates-java.postinst configure

これで再びRocket-Chipのビルドを実行する。これでうまく行くことが確認できた。

f:id:msyksphinz:20180428120515p:plain

Spectre & Meltdown を防ぐマイクロアーキテクチャSafeSpecの論文を読む

Hisa Ando氏のブログで知ったのだが、Spectre & Meltdownを防ぐマイクロアーキテクチャとしてSafeSpecという技術が発表されたので、これを読んでみることにした。

https://arxiv.org/pdf/1806.05179.pdf

Hisa Ando氏の記事にも書いてある通り、SafeSpecでは投機的実行の副作用を削減するために、Shadowキャッシュを設けている。 今回はL1Dキャッシュ、L1Iキャッシュ、TLBキャッシュに対してShadowキャッシュを適用して、投機的実行が最終的に完了するまで、完了前の投機実行の命令結果を格納しておき、投機実行が完了すると命令結果を実キャッシュに書き込む。

下記の図において、各キャッシュの横にShadowキャッシュを置き、実キャッシュが投機実行により汚れてしまうことを防いでる。

f:id:msyksphinz:20180619234056p:plain

ここでおそらく誰もが想像するのが、このShadowキャッシュが汚れてしまったことによる副作用によりSpectre攻撃が起こされるのではないか?ということ。 その問題について言及しているのが、おそらく本文中に入っている"Transient Speculative Attack"という問題だと思う(あってるかな?)。

(これ、日本語の資料をあさっても、「さらに発見された脆弱性」としているけど、そりゃキャッシュをさらに追加したんだから発生するのは当然だろう... 別に彼らが新しく発見した脆弱性でも何でもない)。

この追加されたShadow キャッシュ、問題はどれくらいの大きさであるかというところである。 もしこれが小さいと、ShadowキャッシュがFullになったことにより発生するストールなどを観測すれば再びSpectreのような攻撃化可能になることが考えられる。十分に大きくすることが求められるが、そうすると面積としてオーバヘッドがある。

f:id:msyksphinz:20180619233222p:plain
図. 命令キャッシュ・データキャッシュのアクセスの99.99%をフィットさせるために必要なエントリ数
f:id:msyksphinz:20180619233631p:plain
図. iTLB / dTLBのアクセスの99.99%をフィットさせるために必要なエントリ数

彼らの論文によると、命令キャッシュについては25ライン、i-TLBについては10ライン以下、d-TLBについては25ライン以下となっている。さらにグラフを見るとDキャッシュについては最大で60エントリ超が必要だとしている。 結構な面積が必要だと思うがどうだろう...

ついでに言うと、これはベンチマークプログラムを使ったことによる解析なので参考にはならないと思う。

攻撃プログラムというのはベンチマークプログラムのような特性ではないのだし、特殊な特性を持つプログラムに対してもこの防御手段が有効かどうかを考える必要がある。 これは論文をざっくり読んだ感じだとNoではないのだろうか。

検証方法については、サイクルベースのシミュレータを改造することにより実現している。 MARSSx86と呼ぶ。これは初めて知った。

marss86.org

こういうのすごく面白いと思う。マイクロアーキテクチャの研究にはこういうシミュレータを使いこなすことが重要だなあ。

いずれにしろ、ざっくりと呼んだだけなので、もう少し深く読み進めていきたい。

AWS上で動作するRISC-VチップFireSimのチュートリアルを試す 2. インスタンスの設定とAWSの起動

f:id:msyksphinz:20180617195844p:plain

AWSで動作するRISC-Vシミュレーション環境FireSimのチュートリアルその2.

[高度な詳細]を開き、テキストボックスに付録に示すようなテキストファイルを入力する。

f:id:msyksphinz:20180617184019p:plain

...

f:id:msyksphinz:20180617184457p:plain

次にストレージを追加する。デフォルトでは75GBと設定されているが、これを300GBまで拡張する。 Vivadoでの合成を含めると、とても75GBでは足りないからだ。

f:id:msyksphinz:20180617185048p:plain

設定が完了すると、インスタンスを起動する。

インスタンスの起動

FireSimのマニュアルにはbashではなくmoshを使うことを推奨している。使ったことがないけど、必要ならば移行することも考えよう。

ログインして、起動状態を確認する。ログインにはAWSで設定したpemファイルを指定する必要がある。これはAWS 上記で設定した起動スクリプトが正しく動作しているかを確認するためだ。

centos@ip-192-168-4-15.ec2.internal:~$ cat machine-launchstatus
machine launch script started
machine launch script completed

machine launch script completed と表示されていれば、起動処理が完了していることを示している。

次にfiresimのリポジトリをcloneする前に、AWSにログインするのに使用したpemファイルをホームディレクトリに配置する。

Now that our manager instance is started, copy the private key that you downloaded from AWS earlier (firesim.pem) to ~/firesim.pem on your manager instance. This step is required to give the manager access to the instances it launches for you. また、パーミッションを変えておく。

$ chown 600 ~/firesim.pem

FireSimリポジトリの起動

FireSimリポジトリをダウンロードして、初期化スクリプトを立ち上げる。

$ git clone https://github.com/firesim/firesim
$ cd firesim
$ ./build-setup.sh fast

つぎに、環境変数などを設定する。下記の操作は、FireSimを動作させるマシンにログインするときは毎回必ず実行しなければならない。

$ source sourceme-f1-manager.sh
success: firesim.pem available in ssh-agent

FireSimの設定を完了する。

$ firesim managerinit
FireSim Manager. Docs: http://docs.fires.im
Running: managerinit

Running aws configure. You must specify your AWS account info here to use the FireSim Manager.
[localhost] local: aws configure
AWS Access Key ID [None]: 【AWSアクセスキーを入力する】
AWS Secret Access Key [None]: 【AWSシークレットアクセスキーを入力する】
Default region name [None]: us-east-1
Default output format [None]: json
Backing up initial config files, if they exist.
Creating initial config files from examples.
If you are a new user, supply your email address [abc@xyz.abc] for email notifications (leave blank if you do not want email notifications): 【Notification用のメールアドレスを入力する】
You should receive a message at
masayuki.kimura.1986@gmail.com
asking to confirm your subscription to FireSim SNS Notifications. You will not
receive any notifications until you click the confirmation link.
FireSim Manager setup completed.
The full log of this run is:
/home/centos/firesim/deploy/logs/2018-06-17--10-55-14-managerinit-8OCEHY65NGAE34J7.log

付録: [高度な詳細]でテキストボックスに追加するコード

#!/bin/bash
echo "machine launch script started" > /home/centos/machine-launchstatus
sudo yum install -y mosh
sudo yum groupinstall -y "Development tools"
sudo yum install -y gmp-devel mpfr-devel libmpc-devel zlib-devel vim git java java-devel
curl https://bintray.com/sbt/rpm/rpm | sudo tee /etc/yum.repos.d/bintray-sbt-rpm.repo
sudo yum install -y sbt texinfo gengetopt
sudo yum install -y expat-devel libusb1-devel ncurses-devel cmake "perl(ExtUtils::MakeMaker)"
# deps for poky
sudo yum install -y python34 patch diffstat texi2html texinfo subversion chrpath git wget
# install DTC. it's not available in repos in FPGA AMI
DTCversion=dtc-1.4.4
wget https://git.kernel.org/pub/scm/utils/dtc/dtc.git/snapshot/$DTCversion.tar.gz
tar -xvf $DTCversion.tar.gz
cd $DTCversion
make -j16
make install
cd ..
rm -rf $DTCversion.tar.gz
rm -rf $DTCversion

# get a proper version of git
sudo yum -y remove git
sudo yum -y install epel-release
sudo yum -y install https://centos7.iuscommunity.org/ius-release.rpm
sudo yum -y install git2u

# bash completion for manager
sudo yum -y install bash-completion

# graphviz for manager
sudo yum -y install graphviz python-devel

# these need to match what's in deploy/requirements.txt
sudo pip install fabric==1.14.0
sudo pip install boto3==1.6.2
sudo pip install colorama==0.3.7
sudo pip install argcomplete==1.9.3
sudo pip install graphviz==0.8.3
# for some of our workload plotting scripts
sudo pip install matplotlib==2.2.2
sudo pip install pandas==0.22.0

sudo activate-global-python-argcomplete

# get a regular prompt
echo "PS1='\u@\H:\w\\$ '" >> /home/centos/.bashrc
echo "machine launch script completed" >> /home/centos/machine-launchstatus

AWS上で動作するRISC-VチップFireSimのチュートリアルを試す 1. 立ち上げと参考文献

参考にしたのは以下。

Welcome to FireSim’s documentation! — FireSim documentation

  1. 初期セットアップ・インストール
    1. 最初にAWSのユーザグループを作成する。AWSのアカウントと支払を行っていればこのステップを実行する必要はない。
    2. アカウントに必要なAWSのリソースを設定する。これによりVPC/サブネット/セキュリティグループなどの、FireSimを実行するために必要な構成を立ち上げる。
  2. 単一ノードのシミュレーションチュートリアル: このチュートリアルでは、1コアのシミュレーションを行い、1つのf1.2xlargeであらかじめビルドされたFireSim AGFIを動作させる。
  3. クラスタシミュレーションのチュートリアル : 個のチュートリアルでは、8ノードのクラスたちシミュレーションを行い、1つのf1.16xlarge上であらかじめビルドされたFireSim AGFIとスイッチモデルを立ち上げる。
  4. オリジナルのハードウェアデザインを立ち上げる(ChiselからFPGAイメージへの変換) : このチュートリアルでは、Rocket ChipのRTLと、Rocket Chipに接続する任意のカスタムRTLを使ってFireSim AGFIを作成してシミュレーションする。ChiselのElaborationと、FAME-1への変更、VivadoのFPGAフローを自動的に実行する。

AWSアカウントのセットアップ

West Virginiaリージョンにて、AWS F1インスタンスを使用するためのセットアップを行う。

2.2. Configuring Required Infrastructure in Your AWS Account — FireSim documentation

キーペアはもともと持っているので、それを使用することにした。F1インスタンスの上限の解除についてもすでに行っているので良しとする。。

t2.nanoインスタンスを作成する

ローカルマシンでパッケージのインストールをするのは面倒なため、t2.nanoインスタンスを立ち上げてAWSを構成するためのコマンドを実行する。 t2.nanoインスタンスAWSの構成を行うためだけに使用し、その後は破棄する。

t2.nanoインスタンスは、Amazon Linux AMIをインストールして立ち上げる。起動してログインする。

f:id:msyksphinz:20180617142219p:plain

AWSのセットアップを行う。

[ec2-user@ip-172-31-17-43 ~]$ aws configure
AWS Access Key ID [None]: [アカウントのアクセスキーID]
AWS Secret Access Key [None]: [アカウントのシークレットアクセスキーID]
Default region name [None]: us-east-1
Default output format [None]: json

さらにAWSのセットアップを行ったが、Failしてしまった。これはどうやって直せばよいのだろう?

$ sudo pip install boto3
$ wget https://raw.githubusercontent.com/firesim/firesim/master/scripts/aws-setup.py
$ python aws-setup.py
Traceback (most recent call last):
  File "aws-setup.py", line 12, in <module>
    avail_zones = list(map(lambda x: x['ZoneName'], client.describe_availability_zones()['AvailabilityZones']))
  File "/usr/local/lib/python2.7/site-packages/botocore/client.py", line 314, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/usr/local/lib/python2.7/site-packages/botocore/client.py", line 612, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (AuthFailure) when calling the DescribeAvailabilityZones operation: AWS was not able to validate the provided access credentials

いろいろ試してみた。ntpupdateをすればよいということで実行してみたが変化なし。

$ sudo emacs /etc/ntp.conf -nw
  • /etc/ntp.conf
# Use and prefer the Amazon Time Service server
# server 169.254.169.123 prefer iburst minpoll 3
server -4 ntp.nict.jp iburst  #おなじものを3つ書く!
server -4 ntp.nict.jp iburst
server -4 ntp.nict.jp iburst

RISC-V SpikeシミュレータでC/C++のprintfを実現する仕組み (5. システムコールの呼び出し)

Hello Worldのプログラムを動かしながら、RISC-V Spikeシミュレータのログを追っていき、RISC-Vのブートシーケンスを追っていく、その2。 今回はRISC-Vプログラムのシステムコールの呼び出し部分

f:id:msyksphinz:20180616132641p:plain

Spikeシミュレータの構造を調べているのだが、printf()などの部分はすべてシステムコールを読んで実現するようになっている。 そして、システムコールの部分はriscv-pk/pk/syscalls.cc で実現されている。HTIFで受け取ったシステムコールをriscv-fesvrで処理している。

github.com

簡単に言うとHTIFで受け取ったシステムコールを、シミュレータに接続されたsystem_proxy関数で処理している。

システムコールはいつ呼ばれるのかというと、Spikeの命令セットを実行するエンジンとは横に、デバイスを動作させている部分があり、この部分がデバイスリストを1サイクル毎にtick()で呼び出している。これにより関数コールが検知されるというわけだ。

int htif_t::run()
{
  start();

  auto enq_func = [](std::queue<reg_t>* q, uint64_t x) { q->push(x); };
  std::queue<reg_t> fromhost_queue;
  std::function<void(reg_t)> fromhost_callback =
    std::bind(enq_func, &fromhost_queue, std::placeholders::_1);

  if (tohost_addr == 0) {
    while (true)
      idle();
  }

  while (!signal_exit && exitcode == 0)
  {
    if (auto tohost = mem.read_uint64(tohost_addr)) {
      mem.write_uint64(tohost_addr, 0);
      command_t cmd(mem, tohost, fromhost_callback);
      device_list.handle_command(cmd);
    } else {
      idle();
    }

    device_list.tick();

    if (!fromhost_queue.empty() && mem.read_uint64(fromhost_addr) == 0) {
      mem.write_uint64(fromhost_addr, fromhost_queue.front());
      fromhost_queue.pop();
    }
  }

  stop();
void htif_t::register_devices()
{
  device_list.register_device(&syscall_proxy);
  device_list.register_device(&bcd);
  for (auto d : dynamic_devices)
    device_list.register_device(d);
}

バイスリストにsyscall_proxybcdが登録されている。このうち、syscall_proxyシステムコールを処理している。 このsyscall_proxyを探ってみると、以下で定義されている。

htif_t::htif_t()
  : mem(this), entry(DRAM_BASE), sig_addr(0), sig_len(0),
    tohost_addr(0), fromhost_addr(0), exitcode(0), stopped(false),
    syscall_proxy(this)  // syscall_proxyを登録
{
...
class htif_t : public chunked_memif_t
{
 public:
...
  syscall_t syscall_proxy;
...
};

syscall_t クラスはシステムコールの関数群をテーブルとして保管している。このtableメンバがシステムコールとして使用する関数を保存しているわけだ。

class syscall_t : public device_t
{
 public:
  syscall_t(htif_t*);

  void set_chroot(const char* where);
  
 private:
  const char* identity() { return "syscall_proxy"; }

  htif_t* htif;
  memif_t* memif;
  std::vector<syscall_func_t> table;
  fds_t fds;
...

システムコール関数は以下のようにテーブルに格納される。

syscall_t::syscall_t(htif_t* htif)
  : htif(htif), memif(&htif->memif()), table(2048)
{
  table[17] = &syscall_t::sys_getcwd;
  table[25] = &syscall_t::sys_fcntl;
  table[34] = &syscall_t::sys_mkdirat;
  table[35] = &syscall_t::sys_unlinkat;
  table[37] = &syscall_t::sys_linkat;
  table[38] = &syscall_t::sys_renameat;
  table[46] = &syscall_t::sys_ftruncate;
  table[48] = &syscall_t::sys_faccessat;
  table[49] = &syscall_t::sys_chdir;
  table[56] = &syscall_t::sys_openat;
  table[57] = &syscall_t::sys_close;
  table[62] = &syscall_t::sys_lseek;
  table[63] = &syscall_t::sys_read;
  table[64] = &syscall_t::sys_write;
  table[67] = &syscall_t::sys_pread;
  table[68] = &syscall_t::sys_pwrite;
  table[79] = &syscall_t::sys_fstatat;
  table[80] = &syscall_t::sys_fstat;
  table[93] = &syscall_t::sys_exit;
  table[1039] = &syscall_t::sys_lstat;
  table[2011] = &syscall_t::sys_getmainvars;
...

ファイルディスクリプタは、同様にhtifで定義されている。fds_tにより、stdin, stdout, stderrが定義される。

syscall_t::syscall_t(htif_t* htif)
  : htif(htif), memif(&htif->memif()), table(2048)
{
...
  int stdin_fd = dup(0), stdout_fd0 = dup(1), stdout_fd1 = dup(1);
  if (stdin_fd < 0 || stdout_fd0 < 0 || stdout_fd1 < 0)
    throw std::runtime_error("could not dup stdin/stdout");

  fds.alloc(stdin_fd); // stdin -> stdin
  fds.alloc(stdout_fd0); // stdout -> stdout
  fds.alloc(stdout_fd1); // stderr -> stdout
...

例えば、printf()を実行するとputs()が実行され、その結果システムコールとしてはsys_writeが呼ばれる。

reg_t syscall_t::sys_write(reg_t fd, reg_t pbuf, reg_t len, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
  std::vector<char> buf(len);
  memif->read(pbuf, len, &buf[0]);
  reg_t ret = sysret_errno(write(fds.lookup(fd), &buf[0], len));
  return ret;
}

write(fds.lookup(fd), &buf[0], len) により、stdoutに対して文字が出力されるという構造だ。

RISC-V fesvrを改造して動作を確認

上記の解析を確認するために、riscv-fesvrにstd::coutを挿入して動作を確認してみた。

$ git diff
diff --git a/fesvr/syscall.cc b/fesvr/syscall.cc
index 6e8baf6..d892851 100644
--- a/fesvr/syscall.cc
+++ b/fesvr/syscall.cc
@@ -149,6 +149,11 @@ reg_t syscall_t::sys_pread(reg_t fd, reg_t pbuf, reg_t len, reg_t off, reg_t a4,

 reg_t syscall_t::sys_write(reg_t fd, reg_t pbuf, reg_t len, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
 {
+  std::cout << "<Info: sys_write (" << std::hex << fd << ", " << std::hex << pbuf << ", "
+            << std::hex << len << ", " << std::hex << a3 << ", "
+            << std::hex << a4 << ", " << std::hex << a5 << ", "
+            << std::hex << a6 << ");>\n";
+
   std::vector<char> buf(len);
   memif->read(pbuf, len, &buf[0]);
   reg_t ret = sysret_errno(write(fds.lookup(fd), &buf[0], len));
@@ -346,6 +351,8 @@ void syscall_t::dispatch(reg_t mm)
   if (n >= table.size() || !table[n])
     throw std::runtime_error("bad syscall #" + std::to_string(n));

+  std::cout << "<Info: table[" << std::dec << n << "] is calling>\n";
+
   magicmem[0] = (this->*table[n])(magicmem[1], magicmem[2], magicmem[3], magicmem[4], magicmem[5], magicmem[6], magicmem[7]);

   memif->write(mm, sizeof(magicmem), magicmem);

改めて作ったプログラムを見てみる。

  • cat test_output.c
#include <stdio.h>

int main ()
{
  printf ("Hello World, C\n");

  return 0;
}

実行結果。Hello World, Cを出力するより前に、いくつかシステムコールが呼ばれている。 sys_writeシステムコールでは、stdout に対して文字列を出力していることが分かる。

spike pk test_output_c &> test_output_c.log
$ <Info: table[2011] is calling>
<Info: table[56] is calling>
<Info: table[67] is calling>
<Info: table[67] is calling>
<Info: table[67] is calling>
<Info: table[67] is calling>
<Info: table[67] is calling>
<Info: table[57] is calling>
<Info: table[80] is calling>
<Info: table[64] is calling>
<Info: sys_write (1, 80829030, f, 0, 0, 0, 0);>
Hello World, C
<Info: table[93] is calling>