FPGA開発日記

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

IBM POWER7の論文を読む (1. イントロダクション)

マイクロアーキテクチャに関する論文を読んでいる。今回はIBM POWER7に関する論文。

ieeexplore.ieee.org


本論文では、POWER7プロセッサの主な機能について説明する。

  • 各コアは4-wayの同時マルチスレッディングが可能な8コアのプロセッサが搭載されている。
  • IBMの45nm SOIテクノロジで製造
  • メモリサブシステム : 3レベルのオンチップキャッシュが搭載されている

イントロダクション

Power7の目標は、1つのテクノロジ世代で以下のすべてを達成することである。

  • Power6と同じ電力で、プロセッサチップ上に4倍のコアを配置できるようにする。
    • 消費電力を削減するため、Power7では周波数を下げるが、マイクロアーキテクチャの改善を行う。
      • アウトオブオーダ実行
      • 4-wayマルチスレッド(SMT)
      • 高度な分岐予測
      • 高度なプリフェッチ
      • キャッシュとメモリのレイテンシ削減
    • Power7チップをPower6と同じソケットで設計する。
    • 以前の設計で使用していた外部L3キャッシュを削除する。
    • 浮動小数点演算能力をPower6と比較して2倍とした。
  • 図1はPower7チップの外観を示している。8個のプロセッサコアを持ち、各コアは4つのスレッドを実行できる。
  • Power7はチップの両側に2つのメモリコントローラを搭載しており、4チャネルのDDR3メモリをサポートしている。
  • プロセッサチップ上の8つのコアは、32個の同時実行スレッドを持っており、高いスループットを実現する。
画像は本論文より引用

POWER7コアの概要

  • 図2は、プロセッサコアのフロアプランを示している。以下の機能により、Single Thread性能を大幅に向上させる。
    • 高度な分岐予測機能
    • プリフェッチ機能
    • アウトオブオーダ機能
  • 1コアあたり4スレッドを効率的にサポートする。
図は本論文中より引用
  • 図3は、プロセッサコアの命令フローを示したものである。
  • いくつかのスレッドモードを備えている。
    • ST (Single Thread)
    • SMT2 (Two-way SMT)
    • SMT4 (Four-way SMT)
図は本論文中より引用
  • 2スレッドが1つの汎用レジスタ(GPR)を共有する。これにより、Power5に対してより少ない物理GPRファイルエントリ数で、2倍のスレッドのレジスタを効率的にリネームすることができる。
  • Power7では、整数レジスタ(GPR)、浮動小数レジスタ(FPR)、ベクトルレジスタ(VR)のレジスタリネームが別々だったが、これらをすべて統合した。これにより、面積と消費電力が大幅に低下した。
  • 浮動小数点命令と固定小数点命令(FX)の命令発行キューは別々だったが、これを統一した。
    • これをUnified Issue Queue (UQ)と呼んでいる。
    • 1ユニットで24エントリのキューを、2ユニット実装している。
  • 浮動小数点ユニット(FPU)とベクトルメディア拡張(VMX)ユニットをベクトルスカラユニット(VSU)に統合した。
  • L1命令キャッシュとデータキャッシュをバンク化し、キャッシュへの読み込みと書き込みの同時アクセスが可能となった。

    • 1サイクルで2-readもしくは1-writeのみサポート
  • 図4は、命令が様々な発行キューに流れ、その後機能ユニットに送られる様子を示している。

  • 殆どのVSU命令は、すべてのモード(Single Thread, SMT2, SMT4)でUQ0もしくはUQ1にディスパッチ可能だが、以下の例外がある。
    1. VMX浮動小数点演算命令、スカラおよび複合整数演算はUQ0にのみディスパッチ
    2. Permute(PM)演算、10進数浮動小数点演算、128ビットストア演算はUQ1にのみディスパッチ可能
    3. UQ0に割り当てられたVSU命令は常にベクトルスカラパイプライン0(VS0)で実行
    4. UQ1に割り当てられたVSU命令は常にベクトルスカラパイプライン1(VS1)で実行
  • コアは以下のユニットから構成されている。
    • 命令フェッチユニット (IFU)
      • 32KB命令キャッシュ (I-Cache)
    • 命令シーケンスユニット(ISU)
    • ロードストユニット(LSU)
      • 32KBデータキャッシュ (D-Cache)
    • 固定小数点ユニット(FXU)
    • ベクトル演算ユニット(VSU)
    • 10進数FPU (DFU)
  • 1サイクルで最大8命令フェッチ、最大6命令ディスパッチ、最大8命令発行。
  • 12個の機能ユニットから構成されている
    • 2つの固定小数点ユニット
    • 2つのLSUユニット
      • さらに、単純なFX演算も実行可能となっている。
    • 2つの倍精度浮動小数点演算(DP)ユニット
    • 1つのベクトルユニット
    • 1つの分岐ユニット
    • 1つの条件レジスタ論理ユニット
    • 1つの10進数浮動小数点ユニット

Ubuntu 22.04 LTSでのRISC-Vツールチェイン試行 (2. QEMUのバージョン試行)

実際にはM1 Mac上で構築したLima上のUbuntu 22.04 LTS上でのビルドの試行だけれども。QEMUでのLinuxブート環境が構築したくて、RISC-V Getting Started Guideをそのまま試してみることにした。

なんかうまく行かないので、QEMUのバージョンを2種類用意してみた。

QEMU emulator version 7.0.0
Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers

カーネルを指定しても、ファイルシステムの読み込みの段階で失敗してしまう。何がおかしいのかなあ。

qemu-system-riscv64 -nographic -machine virt -kernel linux/arch/riscv/boot/Image -append "root=/dev/vda ro console=ttyS0" -drive file=busybox/busybox,format=raw,id=hd0 -device virtio-blk-device,drive=hd0
...
[    0.266059] Key type dns_resolver registered
[    0.268047] debug_vm_pgtable: [debug_vm_pgtable         ]: Validating architecture page table helpers
[    0.301143] List of all partitions:
[    0.301395] fe00             950 vda 
[    0.301425]  driver: virtio_blk
[    0.301739] No filesystem could mount root, tried: 
[    0.301763]  ext3
[    0.302051]  ext2
[    0.302153]  ext4
[    0.302189]  vfat
[    0.302534]  msdos
[    0.302818] 
[    0.302985] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(254,0)
[    0.303340] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.18.0-rc7-00181-geaea45fc0e7b #1
[    0.303825] Hardware name: riscv-virtio,qemu (DT)
[    0.304008] Call Trace:
[    0.304341] [<ffffffff80004a28>] dump_backtrace+0x1c/0x24
[    0.304483] [<ffffffff80640dda>] show_stack+0x2c/0x38
[    0.304570] [<ffffffff80645df8>] dump_stack_lvl+0x40/0x58
[    0.304653] [<ffffffff80645e24>] dump_stack+0x14/0x1c
[    0.304730] [<ffffffff806410e6>] panic+0x106/0x2b0
[    0.304809] [<ffffffff80801462>] mount_block_root+0x202/0x21e
[    0.304927] [<ffffffff80801666>] mount_root+0x1e8/0x218
[    0.305012] [<ffffffff808017d8>] prepare_namespace+0x142/0x184
[    0.305089] [<ffffffff80800ffc>] kernel_init_freeable+0x1ee/0x20a
[    0.305236] [<ffffffff8064a20c>] kernel_init+0x1e/0x10a
[    0.305330] [<ffffffff800030b6>] ret_from_exception+0x0/0xc
[    0.305938] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(254,0) ]---
QEMU: Terminated

QEMU 5.0.0のときは、そもそも-biosオプションがないと怒られてしまった。

QEMU emulator version 5.0.0
Copyright (c) 2003-2020 Fabrice Bellard and the QEMU Project developers
$ qemu-system-riscv64 -nographic -machine virt -kernel linux/arch/riscv/boot/Image -append "root=/dev/vda ro console=ttyS0" -drive file=busybox/busybox,format=raw,id=hd0 -device virtio-blk-device,drive=hd0
qemu-system-riscv64: warning: No -bios option specified. Not loading a firmware.
qemu-system-riscv64: warning: This default will change in a future QEMU release. Please use the -bios option to avoid breakages when this happens.
qemu-system-riscv64: warning: See QEMU's deprecation documentation for details.
QEMU: Terminated

そこで、一応確認のためにOpenSBIをダウンロードしてビルドし、QEMUブート時に読み込んでみる。

github.com

make CROSS_COMPILE=riscv64-unknown-linux-gnu- PLATFORM=generic FW_PAYLOAD_PATH=../linux/arch/riscv/boot/Image

QEMU 5.0.0での試行だが、やはりまだうまく行かない。もう少し調べてみよう。

qemu-system-riscv64 -nographic -machine virt -bios opensbi/build/platform/generic/firmware/fw_payload.bin -drive file=busybox/busybox,format=raw,id=hd0 -device virtio-blk-device,drive=hd0
[    0.273957] Hardware name: riscv-virtio,qemu (DT)
[    0.274229] Call Trace:
[    0.274524] [<ffffffff80004a28>] dump_backtrace+0x1c/0x24
[    0.274711] [<ffffffff80640dda>] show_stack+0x2c/0x38
[    0.275024] [<ffffffff80645df8>] dump_stack_lvl+0x40/0x58
[    0.275156] [<ffffffff80645e24>] dump_stack+0x14/0x1c
[    0.275289] [<ffffffff806410e6>] panic+0x106/0x2b0
[    0.275465] [<ffffffff808013cc>] mount_block_root+0x16c/0x21e
[    0.275609] [<ffffffff80801666>] mount_root+0x1e8/0x218
[    0.275963] [<ffffffff808017d8>] prepare_namespace+0x142/0x184
[    0.276155] [<ffffffff80800ffc>] kernel_init_freeable+0x1ee/0x20a
[    0.276282] [<ffffffff8064a20c>] kernel_init+0x1e/0x10a
[    0.276417] [<ffffffff800030b6>] ret_from_exception+0x0/0xc
[    0.277147] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---
QEMU: Terminated

Ubuntu 22.04 LTSでのRISC-Vツールチェイン試行

実際にはM1 Mac上で構築したLima上のUbuntu 22.04 LTS上でのビルドの試行だけれども。QEMUでのLinuxブート環境が構築したくて、RISC-V Getting Started Guideをそのまま試してみることにした。

risc-v-getting-started-guide.readthedocs.io

RISC-Vツールチェインの準備

ベアメタルクロスコンパイラ

git clone https://github.com/riscv-collab/riscv-gnu-toolchain.git -b 2022.05.15
cd riscv-gnu-toolchain
mkdir build
./configure --prefix=${RISCV}
make -j$(nproc)
make install
$ ~/riscv64/bin/riscv64-unknown-elf-gcc --version
riscv64-unknown-elf-gcc () 11.1.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Linux用のコンパイラ

M1 Macネイティブでは全然うまく行かなかったのが、あっという間にうまく行った。素晴らしい。

git clone https://github.com/riscv-collab/riscv-gnu-toolchain.git -b 2022.05.15
cd riscv-gnu-toolchain
mkdir build
./configure --prefix=${RISCV}
make linux -j$(nproc)
make install
$ ~/riscv64-linux/bin/riscv64-unknown-linux-gnu-gcc --version
riscv64-unknown-linux-gnu-gcc (g5964b5cd727) 11.1.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

LinuxカーネルQEMUのインストール

次に、Linuxカーネルのビルド試行がしたいので、QEMUのインストールと、Linuxカーネルをビルドする。

risc-v-getting-started-guide.readthedocs.io

QEMUのインストール

QEMUのインストールは、Gitリポジトリからはできなくなっている?v7.0.0を試行したのだが、結局公式ページからダウンロードしてビルドした。

wget https://download.qemu.org/qemu-7.0.0.tar.xz
tar xvJf qemu-7.0.0.tar.xz
cd qemu-7.0.0
./configure --target-list=riscv64-softmmu
make -j$(nproc)
sudo make install
$ qemu-system-riscv64 --version
QEMU emulator version 7.0.0
Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers

Linuxカーネルのビルド

git clone https://github.com/torvalds/linux
cd linux
git checkout v5.4.0
make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- defconfig
make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- -j $(nproc)

BusyBoxのビルド

git clone https://git.busybox.net/busybox
cd busybox
CROSS_COMPILE=riscv64-unknown-linux-gnu- make defconfig
CROSS_COMPILE=riscv64-unknown-linux-gnu- make -j $(nproc)

QEMUの立ち上げ

マニュアルに従って以下のように指定した。

sudo qemu-system-riscv64 -nographic -machine virt      -kernel ../linux/arch/riscv/boot/Image -append "root=/dev/vda ro console=ttyS0"      -drive file=busybox,format=raw,id=hd0      -device virtio-blk-device,drive=hd0
OpenSBI v1.0                                                                                                      
   ____                    _____ ____ _____                                                                       
  / __ \                  / ____|  _ \_   _|                                                                      
 | |  | |_ __   ___ _ __ | (___ | |_) || |                                                                        
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |                                                                        
 | |__| | |_) |  __/ | | |____) | |_) || |_                                                                       
  \____/| .__/ \___|_| |_|_____/|____/_____|                                                                      
        | |                                                                                                       
        |_|                                                                                                       
                                                                                                                  
Platform Name             : riscv-virtio,qemu                                                                     
Platform Features         : medeleg                                                                               
Platform HART Count       : 1                                                                                     
Platform IPI Device       : aclint-mswi                                                                           
Platform Timer Device     : aclint-mtimer @ 10000000Hz                                                            
Platform Console Device   : uart8250                                                                              
Platform HSM Device       : ---                                                                                   
Platform Reboot Device    : sifive_test                                                                           
Platform Shutdown Device  : sifive_test                                                                           
Firmware Base             : 0x80000000                                                                            
Firmware Size             : 252 KB                                                                                
Runtime SBI Version       : 0.3                                                                                   
                                                                                                                  
Domain0 Name              : root                                                                                  
Domain0 Boot HART         : 0                                                                                     
Domain0 HARTs             : 0*                                                                                    
Domain0 Region00          : 0x0000000002000000-0x000000000200ffff (I)                            
Domain0 Region01          : 0x0000000080000000-0x000000008003ffff ()                      
Domain0 Region02          : 0x0000000000000000-0xffffffffffffffff (R,W,X)                        
Domain0 Next Address      : 0x0000000080200000                                                                    
Domain0 Next Arg1         : 0x0000000087000000                                                                    
Domain0 Next Mode         : S-mode                                                                                
Domain0 SysReset          : yes                                                                                   
                                                                                                                  
Boot HART ID              : 0                                                                                     
Boot HART Domain          : root                                                                                  
Boot HART ISA             : rv64imafdcsuh                                                                         
Boot HART Features        : scounteren,mcounteren,time                                                            
Boot HART PMP Count       : 16
...
[    0.363287] [<ffffffff80004a28>] dump_backtrace+0x1c/0x24
[    0.363425] [<ffffffff80640dda>] show_stack+0x2c/0x38
[    0.363498] [<ffffffff80645df8>] dump_stack_lvl+0x40/0x58
[    0.363618] [<ffffffff80645e24>] dump_stack+0x14/0x1c
[    0.363688] [<ffffffff806410e6>] panic+0x106/0x2b0
[    0.363838] [<ffffffff80801462>] mount_block_root+0x202/0x21e
[    0.363991] [<ffffffff80801666>] mount_root+0x1e8/0x218
[    0.364071] [<ffffffff808017d8>] prepare_namespace+0x142/0x184
[    0.364284] [<ffffffff80800ffc>] kernel_init_freeable+0x1ee/0x20a
[    0.364366] [<ffffffff8064a20c>] kernel_init+0x1e/0x10a
[    0.364431] [<ffffffff800030b6>] ret_from_exception+0x0/0xc
[    0.364899] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(254,0) ]---

なんかがおかしいなあ。BusyBoxの作成に失敗したのか?

M1 Macでriscv-gnu-toolchainのビルド

M1 Mac上にRISC-Vのツールチェインを構築したくて色々試行錯誤している。

git clone https://github.com/riscv-collab/riscv-gnu-toolchain.git -b 2022.05.15
cd riscv-gnu-toolchain
mkdir build
export RISCV=${インストールしたいディレクトリ}
../configure --prefix=${インストールしたいディレクトリ}
make -j10
Undefined symbols for architecture arm64:
  "_host_hooks", referenced from:
      c_common_no_more_pch() in c-pch.o
      toplev::main(int, char**) in libbackend.a(toplev.o)
      gt_pch_save(__sFILE*) in libbackend.a(ggc-common.o)
      gt_pch_restore(__sFILE*) in libbackend.a(ggc-common.o)
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

ここを参考にした。

github.com

以下の変更を加えた。

diff --git a/gcc/config.host b/gcc/config.host
index 0a02c33cc80..fd071000eac 100644
--- a/gcc/config.host
+++ b/gcc/config.host
@@ -93,8 +93,8 @@ esac
 case ${host} in
   *-darwin*)
     # Generic darwin host support.
-    out_host_hook_obj=host-darwin.o
-    host_xmake_file="${host_xmake_file} x-darwin"
+    # out_host_hook_obj=host-darwin.o
+    # host_xmake_file="${host_xmake_file} x-darwin"
     ;;
 esac

次のエラーは、/Library/Developer/CommandLineTools/usr/bin/gm4: unrecognized option--gnu'だ。 m4をアップデートしてみよう。/usr/local/bin/m4`に最新版をインストールして、これを置き換えてみる。

sudo mv gm4 gm4.back
sudo ln -s /usr/local/bin/m4 gm4

次のエラーはyaccで出た。

plural.c:184:5: error: conflicting types for 'libintl_gettextparse'
int __gettextparse (void);
    ^

以下のスレッドを参考し、パッチを当ててリビルドしてみた。

lists.gnu.org

  • riscv-gnu-toolchain/riscv-gdb/intl
diff --git a/intl/plural.y b/intl/plural.y
index 3f75cf3dbb..673e73f174 100644
--- a/intl/plural.y
+++ b/intl/plural.y
@@ -43,6 +43,7 @@
 #define YYLEX_PARAM    &((struct parse_args *) arg)->cp
 #define YYPARSE_PARAM  arg
 %}
+%param {void *arg}
 %pure_parser
 %expect 7
 
@@ -67,7 +68,8 @@ static inline struct expression *new_exp_3 PARAMS ((enum operator op,
                                                   struct expression *tbranch,
                                                   struct expression *fbranch));
 static int yylex PARAMS ((YYSTYPE *lval, const char **pexp));
-static void yyerror PARAMS ((const char *str));
+static void yyerror PARAMS ((void *arg, const char *str));
+
 
 /* Allocation of expressions.  */
 
@@ -402,7 +404,8 @@ yylex (lval, pexp)
 
 
 static void
-yyerror (str)
+yyerror (arg, str)
+     void *arg;
      const char *str;
 {
   /* Do nothing.  We don't print error messages here.  */

とりあえずこれでビルド完了した!

 ./riscv64-unknown-elf-gcc --version
riscv64-unknown-elf-gcc (g5964b5cd727-dirty) 11.1.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

riscv-isa-simのメモリモデルを使用してRTLのメモリアクセスを検証する方法の調査

自作RISC-V CPUの検証をするため、メモリアクセスをSpikeで検証することを考える。 L1Dのアップデートのタイミングで、現状のメモリの状態を確認することを考えてみよう。

いくつかのチェックポイントがあるはずだ。

  • L1Dからデータを外部に吐き出すタイミング
  • L1Dにデータをロードするタイミング
  • ストア命令がL1Dをアップデートするタイミング

残りの2つの対応を考えよう。L1Dにデータをロードするタイミングと、ストア命令がL1Dをアップデートするタイミングでは、いくつかの注意が必要となる。

ストア命令がL1Dをアップデートするときは、バイトイネーブルに注意して必要なところのみを比較する。

   bool diff_found = false;
   fprintf(compare_log_fp, "%lld : STQ  ISS Check : %llx        : ", rtl_time, paddr);
   try {
     for (int i = size/8-1; i >= 0; i--) {
       uint64_t iss_ld_data;
       spike_core->read_mem(paddr + i * 8, 8, &iss_ld_data);
       fprintf(compare_log_fp, "%08x_%08x", iss_ld_data >> 32 & 0xffffffff, iss_ld_data & 0xffffffff);
       for (int b = 0; b < 8; b++) {
         if ((be >> ((i * 8) + b) & 0x01) && (((iss_ld_data >> (b * 8)) & 0xff) != (uint8_t)(l1d_data[i * 8 + b]))) {
           diff_found = true;
         }
       }
       if (i != 0) { fprintf (compare_log_fp, "_"); }
     }
     fprintf(compare_log_fp, "\n");
   } catch (trap_t &t) {
     fprintf (compare_log_fp, "Catch exception at record_l1d_evict : PA = %08llx, %s\n", paddr, t.name());
   }

   if (diff_found) {
     fprintf (compare_log_fp, "L1D Update Data Compare Error\n");
     stop_sim (102);
   }

これらのチェッカーを搭載して、とりあえず基本的なフレームワークは完成した。 テストを走らせてみると、一応うまく動いているようだ。

43512 : 16016 : PC=[ffffffffffe0269c] (M,21,01) 000017b7 lui     a5, 0x1
GPR[15](22) <= 0000000000001000
43512 : 16017 : PC=[ffffffffffe026a0] (M,21,02) 00fc0c33 add     s8, s8, a5
GPR[24](28) <= 000000000003e000
43512 : 16018 : PC=[ffffffffffe026a4] (M,21,04) 0b3c0e63 beq     s8, s3, pc + 188
43513 : 16019 : PC=[ffffffffffe026a8] (M,22,01) 00cc5793 srli    a5, s8, 12
GPR[15](52) <= 000000000000003e
43513 : 16020 : PC=[ffffffffffe026ac] (M,22,02) 00479413 slli    s0, a5, 4
GPR[08](23) <= 00000000000003e0
174078 : L1D Load-In     : 800073e0(00927) : ffffffff_ffe07400_00000000_8006f000_00000000_00000000_00000000_00000000
174078 : Load ISS Check  : 800073e0        : ffffffff_ffe07400_00000000_8006f000_00000000_00000000_00000000_00000000
174090 : L1D Evict       : 80005be0(00735) : 00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000
174090 : EVict ISS Check : 80005be0        : 00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000
43525 : 16021 : PC=[ffffffffffe026b0] (M,23,01) 00848733 add     a4, s1, s0
GPR[14](44) <= ffffffffffe073e0
43525 : 16022 : PC=[ffffffffffe026b4] (M,23,02) 00073703 ld      a4, 0(a4)
MR8(0xffffffffffe073e0)=>0000000000000000
GPR[14](11) <= 0000000000000000
43525 : 16023 : PC=[ffffffffffe026b8] (M,23,04) fe0702e3 beqz    a4, pc - 28
43526 : 16024 : PC=[ffffffffffe0269c] (M,24,01) 000017b7 lui     a5, 0x1
GPR[15](60) <= 0000000000001000
43526 : 16025 : PC=[ffffffffffe026a0] (M,24,02) 00fc0c33 add     s8, s8, a5
GPR[24](6) <= 000000000003f000
43526 : 16026 : PC=[ffffffffffe026a4] (M,24,04) 0b3c0e63 beq     s8, s3, pc + 188
174146 : L1D Load-In     : 80007400(00928) : ffffffff_ffe07420_00000000_8004b000_ffffffff_ffe07410_00000000_80057000
174146 : Load ISS Check  : 80007400        : ffffffff_ffe07420_00000000_8004b000_ffffffff_ffe07410_00000000_80057000
174158 : L1D Evict       : 8004bc00(01504) : 00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000
174158 : EVict ISS Check : 8004bc00        : 00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000
43542 : 16027 : PC=[ffffffffffe02760] (M,03,01) 00090513 mv      a0, s2
GPR[10](65) <= 0000000000000001
43542 : 16028 : PC=[ffffffffffe02764] (M,03,02) b41ff0ef jal     pc - 0x4c0
GPR[01](29) <= ffffffffffe02768
174282 : L1D Load-In     : 80001000(00128) : 00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000
174282 : Load ISS Check  : 80001000        : 00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000
174294 : L1D Evict       : 80005800(00704) : 00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000
174294 : EVict ISS Check : 80005800        : 00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000
43574 : 16029 : PC=[ffffffffffe022a4] (M,04,01) fffff717 auipc   a4, 0xfffff
GPR[14](8) <= ffffffffffe012a4
43574 : 16030 : PC=[ffffffffffe022a8] (M,04,02) d5c70713 addi    a4, a4, -676
GPR[14](9) <= ffffffffffe01000

riscv-isa-simのメモリモデルを使用してRTLのメモリアクセスを検証する方法の調査

自作RISC-V CPUの検証をするため、メモリアクセスをSpikeで検証することを考える。 L1Dのアップデートのタイミングで、現状のメモリの状態を確認することを考えてみよう。

いくつかのチェックポイントがあるはずだ。

  • L1Dからデータを外部に吐き出すタイミング
  • L1Dにデータをロードするタイミング
  • ストア命令がL1Dをアップデートするタイミング

まずは一番簡単そうな、L1Dの外部吐き出しのタイミングでSpikeのチェックを実行することを考える。 基本的な方針としては、メモリサブシステムがキャッシュラインの吐き出しをするタイミングでDPI-Cを呼び出し、Spikeのメモリ状態と比較する。

always_ff @ (negedge i_clk, negedge i_reset_n) begin
  if (i_reset_n) begin
    if (l1d_ext_wr_req.valid & l1d_ext_wr_req.ready) begin
      /* verilator lint_off WIDTH */
      record_l1d_evict ($time,
                        l1d_ext_wr_req.payload.addr,
                        l1d_ext_wr_req.payload.addr[$clog2(DCACHE_DATA_B_W) +: DCACHE_TAG_LOW],
                        l1d_array,
                        DCACHE_DATA_B_W);

このタイミングで、Spike経由でメモリの内容を参照する。

  bool diff_found = false;
  fprintf(compare_log_fp, "%lld : EVict ISS Check : %llx        : ", rtl_time, paddr);
  try {
    for (int i = size/8-1; i >= 0; i--) {
      uint64_t iss_ld_data;
      spike_core->read_mem(paddr + i * 8, 8, &iss_ld_data);
      fprintf(compare_log_fp, "%08x_%08x", iss_ld_data >> 32 & 0xffffffff, iss_ld_data & 0xffffffff);
      if ((iss_ld_data >> 32 & 0xffffffff) != l1d_data[i*2+1] |
          (iss_ld_data & 0xffffffff) != l1d_data[i*2+0]) {
        diff_found = true;
      }
      if (i != 0) {
        fprintf(compare_log_fp, "_");
      }
    }
    fprintf(compare_log_fp, "\n");
  } catch (trap_t &t) {
    fprintf (compare_log_fp, "Catch exception at record_l1d_evict : PA = %08llx, %s\n", paddr, t.name());
  }

  if (diff_found) {
    fprintf (compare_log_fp, "Eviction Data Compare Error\n");
    stop_sim (102);
  }

spike_core->read_mem()マッピングしているけれど、実体はsim_tread_chunk()を呼び出している。

  void read_mem(addr_t taddr, size_t len, void* dst) {
    read_chunk (taddr, len, dst);
  }

いろいろ調べてみると、Spikeの環境には2種類のMMUが用意されていて、

  • sim_t::debug_mmu グローバルなMMU。アクセスは物理アドレスで行う
  • sim_t::core::mmu 各コアでのMMU。アクセスは仮想アドレスで行う

という感じがしているので、グローバルなMMUを使用して正確な物理アドレスを使用するのがポイント。

この仕様は、Spikeのインタラクティブモードで物理アドレスを使うことができるので、どこかにそういう機能があるはずだと探して見つかった。

To see the contents of a memory location (physical address in hex):

: mem 2020
To see the contents of memory with a virtual address (0 for core 0):

: mem 0 2020

riscv-isa-simのMMUを使用してRTLのMMUを検証する方法の調査

自作RISC-V CPUの検証をするため、SpikeのMMUを使うことを考える。どうしてもTLB周りとか、VA→PAの検証はデバッグが難しい。 ハードウェアが勝手にPage Table Walkして、万が一L1Dキャッシュにデータを書き損じていた場合、波形を一生懸命眺めないと検証できない。

まずは、基本的にTLBの出力結果がISSとあっていなければどうしようもない。この部分のインタフェースを検証するためのDPIを追加することにした。

SpikeのMMUの実装は以下になっている。

github.com

本当はこのtranslate()を使用すればいいのだが、privateなので簡単には使えない。とりあえずpublicに移動して使ってみることにする。

  reg_t translate(reg_t addr, reg_t len, access_type type, uint32_t xlate_flags);

ハードウェア側の実装で、VA→PA変換を行ったときにDPI-Cを呼び出してSpike側のインタフェースを起動するようにしてみる。

reg_t iss_paddr = mmu->translate(rtl_va, rtl_len, static_cast<access_type>(rtl_acc_type), 0);で変換ルーチンを呼び出す。 ちなみに、try - catchを実装しておかないと、Spikeの実装は変換に失敗すると例外を飛ばしてしまうのでしっかりキャッチしておかなければならない。

`ifdef SIMULATION

import "DPI-C" function void check_mmu_trans
  (
   input longint rtl_time,
   input longint rtl_va,
   input int     rtl_len,
   input int     rtl_acc_type,
   input longint rtl_pa
   );

always_ff @ (negedge i_clk, negedge i_reset_n) begin
  if (i_reset_n) begin
    if (i_tlb_req.valid & !w_tlb_miss) begin
      check_mmu_trans ($time, i_tlb_req.vaddr,
                       i_tlb_req.size, i_tlb_req.cmd,
                       o_tlb_resp.paddr);
    end
  end
end

`endif // SIMULATION

C++側の実装は以下のようになった。

void check_mmu_trans (long long time, long long rtl_va,
                      int rtl_len, int rtl_acc_type,
                      long long rtl_pa)
{
  processor_t *p = spike_core->get_core(0);
  spike_core->set_procs_debug(true);
  mmu_t *mmu = p->get_mmu();

  access_type acc_type;
  switch (rtl_acc_type) {
    case 0 : acc_type = LOAD;  break;
    case 1 : acc_type = STORE; break;
    default :
      fprintf (stderr, "rtl_acc_type = %d is not supported\n", rtl_acc_type);
      stop_sim(1);
  }

  try {
    reg_t iss_paddr = mmu->translate(rtl_va, rtl_len, static_cast<access_type>(rtl_acc_type), 0);
    if (iss_paddr != rtl_pa) {
      char spike_out_str[256];
      sprintf (spike_out_str, "Error : PA->VA different.\nRTL = %08x, ISS=%08x",
               rtl_pa, iss_paddr);
      fprintf (compare_log_fp, spike_out_str);
      fprintf (stderr, spike_out_str);
      stop_sim(101);
    } else {
      // fprintf (compare_log_fp, "MMU check passed : VA = %08x, PA = %08x\n", rtl_va, rtl_pa);
    }
  } catch (trap_t &t) {
    // fprintf (compare_log_fp, "Catch exception at check_mmu_trans : VA = %08x, PA = %08x\n", rtl_va, rtl_pa);
  }

  spike_core->set_procs_debug(false);
}

とりあえずここまでで変換ルーチンで、検証できるようになったことは確認した。

ただし、このアサーションをつけてもまだテストパタンのFailを検出することができない。もう少しチェックが必要だ。