Posts Tagged with "BSV"

既に発行済みのブログであっても適宜修正・追加することがあります。
We may make changes and additions to blogs already published.

RISC-Vの調査 (7)

posted by sakurai on June 18, 2020 #278

Bsimによるシミュレーション

$ ./exe_HW_sim +v1 +tohost

によりBsimのシミュレーションが実行されます。+v1で命令毎のトレース、+v2でパイプライン内容を含めたトレースが表示されます。さらに波形観測のためには

$ ./exe_HW_sim +v1 +tohost -V dump.vcd

のようにVCDファイルを指定します。これをGtkwaveにより開き、波形を観測してみます。\$80000000から開始しており、キャッシュラインバウンダリでのブロックインが観察されます。

図%%.1
図278.1 シミュレーション波形

2回目に実行する場合は基本的にキャッシュヒットし、1サイクルで動作しています。

図%%.2
図278.2 シミュレーション波形(続き)

BsimにはUARTが実装されているため、UARTに対して、\$68('h'),\$65('e'),\$6c('l'),\$6c('l'),\$6f('o'),\$21('!'),\$0a('lf')と出力されていることが確認できます。

図%%.3
図278.3 シミュレーション波形(続き)

左矢前のブログ 次のブログ右矢

RISC-Vの調査 (6)

posted by sakurai on June 17, 2020 #277

Bsimによるシミュレーション

QemuのシミュレーションからBsimのシミュレーションに切り替えます。ここで、Flute SoCではUARTのアドレスが異なっているため、テストプログラムのUARTアドレスを

<volatile char* UART0_ADDR = (char*)0x10000000;
>volatile char* UART0_ADDR = (char*)0xc0000000;

と変更した上でコンパイルします。

$ riscv32-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostartfiles -Tlink.lds -Wall -O2 hello.c -o hello.elf

ここで、リンカへの指示であるlink.ldsは以下のようにしています。

OUTPUT_ARCH("riscv")
ENTRY(main)

SECTIONS
{
    . = 0x80000000;
    .text.startup : { *(.text.startup) }
    .text   : {
            _start = .;
            *(.text);
            _text_end = .;
            exit = .;
         }
    .rodata : { *(.rodata) }
    .sdata   : { *(.sdata) }
    .bss    : { *.(.bss) }
    /DISCARD/ : { *(.comment);
          *(.riscv.attributes);
          *(.strtab*);
          *(.shstrtab);
        }
    . = ALIGN(8);
     = . + 0x4000;
    sp_top = .;
}

前稿で作成したelfを、Bsimによるシミュレーションが可能なhexファイルに変換します。これはFlute/Tests/elf_to_hex/elf_to_hexで行います。

$ ../Flute/Tests/elf_to_hex/elf_to_hex hello.elf Mem.hex
c_mem_load_elf: hello.elf is a 32-bit ELF file
Section .text.startup   : addr         80000000 to addr         8000006c; size 0x      6c (= 108) bytes
Section .sdata          : addr         80000068 to addr         80000070; size 0x       8 (= 8) bytes
Section .comment        : Ignored
Section .riscv.attributes: Ignored
Section .symtab         : Searching for addresses of '_start', 'exit' and 'tohost' symbols
Writing symbols to:    symbol_table.txt
    No 'exit' label found
    No 'tohost' symbol found
Section .strtab         : Ignored
Section .shstrtab       : Ignored
Min addr:                    80000000 (hex)
Max addr:                    80000073 (hex)
Writing mem hex to file 'Mem.hex'
Subtracting 0x80000000 base from addresses

作成されたhello.hexの内容は以下のようになっています。逆アセンブルリストと比較して概ね良さそうです。

@0000000    // raw_mem addr;  byte addr: 00000000
00c6802306c007130707a68300d7002306500613068006930707a703800007b7    // raw_mem addr 00000000;  byte addr 00000000
0707a70300d7002306f006930707a70300e680230707a68300e680230707a683    // raw_mem addr 00000001;  byte addr 00000020
000057b706c7a703800007b700e7802300a007130707a78300d7002302100693    // raw_mem addr 00000002;  byte addr 00000040
00000000000000000000000010000000001000000000806700f7102355578793    // raw_mem addr 00000003;  byte addr 00000060
@07fffff    // last raw_mem addr;  byte addr: 0fffffe0
000000000000000000000000000000000000000000000000000000000000000    // raw_mem addr 007fffff;      byte addr 0fffffe0

左矢前のブログ 次のブログ右矢

RISC-Vの調査 (5)

posted by sakurai on June 15, 2020 #276

開発環境のインストール

開発環境のインストールを行います。最初にct-ngをインストールしようとしたのですが、RV32ではうまく行きませんでした。そこで、この記事を参考にインストールを実施しました。make中にインストールもしているので、あらかじめ/opt/riscvを作成しておき、記事中にもあるように自分が書き込み可能にしておくのが良さそうです。

qemuについてもこの記事のとおりにインストールしますが、

ERROR: glib-2.48 gthread-2.0 is required to compile QEMU

このようなエラーが出ました。私はFedora32を使用しているので、

$ sudo dnf install glib2 glib2-devel pixman-devel

と不足しているパッケージを追加したところ、configureが通ったので、makeします。

テストプログラム実行

早速、テストプログラムhello.cをコンパイルして実行してみます。Qemuを前提とするプログラムで、QemuのUartとシミュレーション停止アドレスが定義されています。

volatile char* UART0_ADDR = (char*)0x10000000;
volatile short* VIRT_TEST_ADDR = (short*)0x100000;

void _start(void) __attribute__((section(".text.startup")));

void _start(void) {
  *UART0_ADDR = 'h';
  *UART0_ADDR = 'e';
  *UART0_ADDR = 'l';
  *UART0_ADDR = 'l';
  *UART0_ADDR = 'o';
  *UART0_ADDR = '!';
  *UART0_ADDR = '\n';
  *VIRT_TEST_ADDR = 0x5555;
}

gccによりコンパイルし、qemuによりシミュレーションを行います。

$ riscv32-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostartfiles -Tlink.lds -Wall -O2 hello.c -o hello.elf
$ qemu-system-riscv32 -nographic -M virt -m 4096 -serial mon:stdio -bios none -kernel hello.elf
hello!

Qemuによりシミュレーションができました。また、逆アセンブラはobjdumpであり、以下の方法でアセンブルリストが確認できます。

$ riscv32-unknown-elf-objdump -D hello.elf
hello.elf:     ファイル形式 elf32-littleriscv

セクション .text.startup の逆アセンブル:

80000000 <_start>:
80000000:       800007b7                lui     a5,0x80000
80000004:       0707a703                lw     a4,112(a5) # 80000070 
80000008:       06800693                li      a3,104
8000000c:       06500613                li      a2,101
80000010:       00d70023                sb      a3,0(a4)
80000014:       06c7a683                lw      a3,108(a5)
80000018:       06c00713                li      a4,108
8000001c:       00c68023                sb      a2,0(a3)
80000020:       06c7a683                lw      a3,108(a5)
80000024:       00e68023                sb      a4,0(a3) 
:

左矢前のブログ 次のブログ右矢

RISC-Vの調査 (4)

posted by sakurai on June 12, 2020 #275

シミュレーションモデルの波形を観測してみます。テストプログラムは、\$80000000から始まっていますが、シミュレーションは\$1000から始まっています。これはBootROMのようです。BootROMのブロックインが19サイクルかかっています。

図275.1はGtkwaveによるシミュレーション波形で、pcをオレンジ色で塗っています。

図%%.1
図275.1 シミュレーション波形
BootROMの処理が終わり、\$80000000にジャンプし、すぐに\$8000004cにジャンプしています。ここでpcだけを見ると、\$80000000が20サイクル、\$8000004cが24サイクルも継続しています。Linuxが動作するMMUを持つプロセッサであるためやむを得ないとはいえ、キャッシュアクセスレイテンシが大きいように思われます。
図%%.2
図275.2 シミュレーション波形(続き)
図275.2はテストプログラム実行中で、このような波形が続きますが、24サイクルのブロックインに引き続き、8命令を1サイクル実行しています。

左矢前のブログ 次のブログ右矢

RISC-Vの調査 (3)

posted by sakurai on June 11, 2020 #274

前々稿の記事において、シミュレーションモデルが生成できましたが、これを実行させてみます。

テストプログラムは、add命令単体テストで、

80000000 <_start>:
80000000:       04c0006f                j       8000004c 
8000004c :

8000004c:       f1402573                csrr    a0,mhartid
80000050:       00051063                bnez    a0,80000050 
80000054:       00000297                auipc   t0,0x0
80000058:       01028293                addi    t0,t0,16 # 80000064 
8000005c:       30529073                csrw    mtvec,t0
80000060:       18005073                csrwi   satp,0
:

80000100 :
80000100:       00000093                li      ra,0
80000104:       00000113                li      sp,0
80000108:       00208f33                add     t5,ra,sp
8000010c:       00000e93                li      t4,0
80000110:       00200193                li      gp,2
80000114:       4ddf1663                bne     t5,t4,800005e0 

80000118 :
80000118:       00100093                li      ra,1
8000011c:       00100113                li      sp,1
80000120:       00208f33                add     t5,ra,sp
80000124:       00200e93                li      t4,2
80000128:       00300193                li      gp,3
8000012c:       4bdf1a63                bne     t5,t4,800005e0 
:

のように構成されています。Bsimによるシミュレーションは、+v1で命令トレースが、+v2でパイプラインステージの内容を表示させるようになっています。+v2で実行させた結果をgrepにより計数してみると、表274.1のようになりました。パイプラインの各ステージの出力ステータスの意味は、以下のとおりです。

  • EMPTY --- 入力が来ないためアイドルとなっている
  • BUSY --- 入力があるが、処理中で出力がレディではない
  • PIPE --- 入力があり、パイプラインは正常な出力を行っている
  • NONPIPE --- 入力があり、出力は例外的な場合(トラップ等)

表274.1 ステージ毎のパイプラインの状態
状態 StageF StageD Stage1 Stage2 Stage3
BUSY 1,118 0 23 26 0
EMPTY 22 1,094 959 1,373 1,409
PIPE処理 743 789 901 484 474
合計 1,883

この表だけでもいろいろなことが推測されます。
  • 試験命令数は473命令だったので、CPI(Cycles Per Instruction)は3.98となりました。命令キャッシュミスのためにかなり大きく(悪く)なっています。
  • マシンサイクル1,883サイクルのうち、実際に処理しているのは半分くらいであり、主なパイプラインストールは命令キャッシュによるものだと考えられます。
  • StageFがBUSYの分だけStageD以降がEMPTYとなり、空いています。すなわち約1,100サイクルがパイプラインバブルとなっています。
  • Stage1に対してStage2のPIPE処理が半分なのは、ロードストア命令が半分(レジスタとロードストアが1:1)くらいだからかもしれません。
  • StageDに対してStage1のPIPE処理が増加しているのは、マルチサイクル命令のためかもしれません。

これだけ見るとかなり効率が悪そうですが、対象がテストプログラムでループが無いため、基本的にキャッシュミスが頻発します。一般のアプリケーションのようにループがあれば、ずっと効率が向上するはずです。


左矢前のブログ 次のブログ右矢

RISC-Vの調査 (2)

posted by sakurai on June 10, 2020 #273

Fluteの階層構造

CPUの階層構造を図273.1に示します。5段のパイプラインは図273.1に示すように、"F"(命令Fetchステージ), "D"(命令デコードステージ), "1"(命令実行ステージ), "2"(メモリアクセス、長レインテンシステージ), "3"(ライトバックステージ)とステージ名が付けられています。なぜステージ"1”,"2",...,"5"でないかと言えば、3段パイプラインのPiccoloとステージを共用しているため書かれていました。Piccoloではパイプラインステージは”1", "2", "3"と名付けられており、Fluteでは、Piccoloの"1"を"F", ”D”, "1"に分解したようです。

図%%.1
図273.1 Flute CPU階層構造

各モジュールがmk〇〇と名付けられているのはBSVのお作法です。モジュールとそのインスタンスは一対nの関係にあり、モジュール名はいわばテンプレート名を意味するため、モジュールからインスタンスがmakeされるということを表しています。例えば上図のmkMMU_Cacheはどちらも同じモジュールですが、それぞれ命令用とデータ用に2個インスタンシエートしています。

キャッシュは上記のように、パラメタライズ可能な命令キャッシュ、データキャッシュの2つがあります。その他に、分岐予測器があります。


左矢前のブログ 次のブログ右矢

RISC-Vの調査

posted by sakurai on June 9, 2020 #272

BluespecのFluteプロセッサ

BluespecのBSVが読めるようになったところで、引き続いてFluteプロセッサの調査を行います。FluteはBluespecの開発したRISC-Vアーキテクチャの5段パイプラインRISC CPUです。それだけでなく、仮想記憶をサポートしているため、Linuxが動作します。(ページ)テーブルウォークはMMU内のハードウェアが実行します。ソースではステートベース設計のFSMで実装されていました。

Githubからダウンロード

$ git clone https://github.com/bluespec/Flute.git

としてGithubからダウンロードします。

RV32ACIMUアーキテクチャのBsimモデルの作成

アーキテクチャには各種ありますが、比較的軽いもの、例えば32bit、Floating無しのマイクロアーキテクチャを選択します。

$ cd builds/RV32ACIMU_Flute_bluesim

として、ターゲットディレクトリに移行します。

$ make all

と実行すると、

:
Simulation shared library created: exe_HW_sim.so
Simulation executable created: ./exe_HW_sim
INFO: linked bsc-compiled objects into Bluesim executable
$ 

このように、bsvで書かれたソースファイルがbscによりコンパイルされ、シミュレーションモデルであるexe_HW_simが生成されます。

RV32ACIMUアーキテクチャのBsimモデルの試験

$ make test

により、出来上がったシミュレーションモデルが、RISC-Vのテストスイートによりテストされます。

$ make test
make -C  ../../Tests/elf_to_hex
make[1]: Entering directory '/home/sakurai/src/bsv/riscv/Flute/Tests/elf_to_hex'
make[1]: 'elf_to_hex' is up to date.
make[1]: Leaving directory '/home/sakurai/src/bsv/riscv/Flute/Tests/elf_to_hex'
../../Tests/elf_to_hex/elf_to_hex  ../../Tests/isa/rv32ui-p-add  Mem.hex
c_mem_load_elf: ../../Tests/isa/rv32ui-p-add is a 32-bit ELF file
Section .text.init      : addr         80000000 to addr         80000604; size 0x     604 (= 1540) bytes
Section .tohost         : addr         80001000 to addr         80001048; size 0x      48 (= 72) bytes
Section .riscv.attributes: Ignored
Section .symtab         : Searching for addresses of '_start', 'exit' and 'tohost' symbols
Writing symbols to:    symbol_table.txt
    No 'exit' label found
Section .strtab         : Ignored
Section .shstrtab       : Ignored
Min addr:                    80000000 (hex)
Max addr:                    80001047 (hex)
   :
================================================================
Bluespec RISC-V standalone system simulation v1.2
Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
================================================================
INFO: watch_tohost = 1, tohost_addr = 0x80001000
1: top.soc_top.mem0_controller_axi4_deburster::AXI4_Deburster.rl_reset
2:top.soc_top.rl_reset_start_initial ...
3: Core.rl_cpu_hart0_reset_from_soc_start
================================================================
CPU: Bluespec  RISC-V  Flute  v3.0 (RV32)
Copyright (c) 2016-2020 Bluespec, Inc. All Rights Reserved.
================================================================
6: D_MMU_Cache: cache size 8 KB, associativity 2, line size 32 bytes (= 8 XLEN words)
6: I_MMU_Cache: cache size 8 KB, associativity 2, line size 32 bytes (= 8 XLEN words)
512: top.soc_top.core.cpu.rl_reset_complete: restart at PC = 0x1000
514: Near_Mem_IO_AXI4.set_addr_map: addr_base 0x2000000 addr_lim 0x200c000
514: Core.rl_cpu_hart0_reset_complete
515: Mem_Controller.set_addr_map: addr_base 0x80000000 addr_lim 0x90000000
515:top.soc_top.rl_reset_complete_initial
instret:0  PC:0x1000  instr:0x297  priv:3
instret:1  PC:0x1004  instr:0x2028593  priv:3
instret:2  PC:0x1008  instr:0xf1402573  priv:3
  :
instret:471  PC:0x80000044  instr:0xfc3f2023  priv:3
instret:472  PC:0x80000048  instr:0xff9ff06f  priv:3
instret:473  PC:0x80000040  instr:0x1f17  priv:3
2396: Mem_Controller.rl_process_wr_req: addr 0x80001000 () data 0x1
PASS
2397: top:.rl_terminate: soc_top status is 0x1 (= 0d1)
Simulation speed: 2396 cycles, 60915008 nsecs  = 39333 cycles/sec

テストスイートのelfを、elf_to_hexにより、asciiのメモリイメージファイルに変換しています。

これはadd命令単体のテストですが、全てのテストを行うには、以下のように実行します。

$ make isa_tests

と実行すると、

 :
Worker 1: Test: rv32um-p-mul PASS [So far: total 67, executed 34, PASS 34, FAIL 0]
Worker 0 executed 33 tests, of which 33 passed
Worker 1 executed 34 tests, of which 34 passed
Total tests: 67 tests
Executed:    67 tests
PASS:        67 tests
FAIL:        0 tests
Finished running regressions; saved logs in Logs/

のように出力され、67個全てのテストが実行され、全てパスしたことが表示されます。


左矢前のブログ 次のブログ右矢

posted by sakurai on June 4, 2020 #270

QSPIフラッシュへの書き込み

通常ではVivadoからPROGRAM AND DEBUG⇒Open Hardware Manager⇒Open Target⇒Auto Connectとし、Program DeviceによりJTAG経由でFPGAにビットストリームを焼きこみます。しかしながらこれだと電源断によりFPGAのSRAM内容が消えてしまいます。また、FPGAプログラミング用のPCが常に必要です。オンボードFlashにデータを焼きこめばPCを持ち運ぶ必要がなく、電源onでアプリケーションが立ち上がるため、Flashのプログラミングを行います。

binファイルの作成

最初にFlashへ書き込むデータファイルであるbinファイルを用意します。これは、Tools⇒Setting(歯車マーク)⇒Project Settings⇒Bitstream画面で行います。この画面を開くと複数のチェックボックスが表示されます。その中の、-bin_file*のチェックボックスにチェックします。

図%%.1
図270.1 bin_fileにチェック

これを行ってから、通常どおりPROGRAM AND DEBUG⇒Generate Bitstreamを実施するとWrite Bitstreamが完了しますが、同時にbinファイルが生成されています。場所はbitファイルと同じところで'プロジェクト/プロジェクト.runs/impl_1/'です。

binファイルの焼きこみ

binファイルができたら、Add Configuration Memoryにより、Add Configuration Memory Device画面が開きます。Flashデバイスの選択が可能なので、この中で"s25fl128sxxxxx0"を選択します。Search窓にs25fl128を入力すれば、候補が3つ現れますがその真ん中です。

図%%.2
図270.2 FLASHデバイスの選択
選択したらOKをクリックします。するとプログラミングが始まり、30秒程でプログラミングが完了します。

実行

リファレンスマニュアルにはJP1でプログラミングモードが決まるとあります。JP1の位置がどちらでもJTAGからは書き込めるとのことです。初期状態はJP1はショートで、SPI-FLASHのモードとなっており、そのまま電源のOFF⇒ONでSpace Invadersが立ち上がりました。

図%%.3
図270.3 JTAG接続なしにSpace Invadersが動作

左矢前のブログ 次のブログ右矢

posted by sakurai on June 3, 2020 #269

Arty A7-35ボードの購入

DigilentからArty A7-35ボード(魚拓)を購入しました。このボードはUltra96と比べて本体が約半額と安いだけでなく、(弊社開発の)PMOD変換ボードも不要なので、最も安くSpace Invadersを動かすことができます。

必要な周辺

Space Invadersを動作させるには、Artyボードの他に必要なものは以下のとおりです。

Arty A7-35ボードへの移植

除算器を引き算に変換

FPGAの世代や遅延、容量は違うものの、基本的には同様に動作するはずです。ところが、一部動作がおかしかったので修正しました。まず、除算器にバグがあるようなので引き算方式に修正しました。スコアを表示する箇所において、各桁表示のため1000、100、10で割る場合がありますが、1000で割った商を誤ることがあるようです。除算をやめ、引けなくなるまで1000、100、10を引く方式に変更したところ、回路規模も小さくなり正常に動作するようになりました。

FSM clockを1/10に変更

ゲームFSMクロックを10MHzで設計し、96.4%がウェイトだと判明したので、FSMクロックを1MHzに落としました。自機増加音が無視されることがあるので、クロックを落としたのですが、原因は異なっていました(後述)。

60Hzクロックの生成

この修正により、FSMの待ち時間が影響を受けます。1tick=60HzのタイミングをとるのにFSMクロック数を数えていましたが、FSMクロックの周波数が変わるため、外部から60Hzを入力するように修正します。60Hzクロックは、上記FSMクロックである1MHzクロックをバイナリカウンタで\$411B回カウントすることで生成します。さらにFSM内での60Hzクロックとの同期は以下のように行います。countはtick(=16.67msec)の何倍待たせるかを示す引数です。

         repeat(pack(extend(count))) seq
            await(tick == 0);
            await(tick == 1);
         endseq

60Hzクロックの"L"を待ち、もし"L"であれば次に"H"を待つようにします。これにより60Hzの立ち上がりに同期して動作することになります。

このように変更した結果、FSMの処理時間は10倍の約5msecに増加し、60Hzの周期16.67msecの約30%になりました。図269.2の黄線が60Hzクロック、青線がそれによる実行(Hでウエイト中、Lで実行中)を示します。

図%%.8
図269.8 青線が"H"でウェイト中

左矢前のブログ 次のブログ右矢

posted by sakurai on May 12, 2020 #255

過去ブログの、BSVによるスペースインベーダーの再設計の記事#234~#239, #254をまとめてQiitaに投稿しました。さらに考察を加えています。

BSV (Bluespec SystemVerilog)によるスペースインベーダーの再設計

過去ブログ記事でUltra96ボードを用いた、VerilogHDLによるSpace Invadersゲームの作成を投稿しましたが、その続きです。

図%%.1
図255.1 Qiitaの投稿記事

左矢前のブログ 次のブログ右矢


ページ: