読者です 読者をやめる 読者になる 読者になる

FPGA開発日記

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

OpenSPARC T2の調査(1)

RISC-Vプロセッサの実装もちょっと手詰まりになってきたので、少し気分転換に、他のプロセッサはどのように実装されているんだろう、というのが気になった。

フリーで入手できるそこそこ高性能なプロセッサとしては、OpenSPARCか、RISC-VのBOOMが存在するが、内部を解析するということを考えるとBOOMは高位合成言語で記述されているということもあり不向きであると考えた。

そこで、ちょっとした気分転換にOpenSPARC T2を触ってみようと考えた。

OpenSPARCとは

Sun Microsystems(現在はOracle)のSPARCプロセッサのオープンコア版。2006年に公開されたT1、2008年に公開されたT2が存在しているが、今回はT2に焦点を当てる。

  • 8スレッドSMT
  • 最大8コア搭載で、合計64スレッド実行
  • 各コアについては
    • 整数ユニットは2
    • 浮動小数点ユニットは1
    • ロードストアユニットは2
    • 8-way 16kB命令キャッシュ
    • 4-way 8kBデータキャッシュ
    • 8 ステージ整数パイプライン
    • 12ステージ浮動小数点パイプライン

f:id:msyksphinz:20170223012639p:plain

OpenSPARCをダウンロードして中身を調査する

OpenSPARCは以下のサイトからダウンロードできる。

Overview of OpenSPARC Resources

で、肝心の中身なのだが、ほぼ10年前のデザインということもあり、趣味で扱うにはなかなか障壁が高い。

  • VCSとVERAが動作すること前提
  • Perlのバージョンも古い
  • cshで実行すること前提

などなど、実行させるまでにかなり時間がかかる。どうにかしてModelsimで動作させることが出来ないかと調査していたのだが、よくよく観察するとファイルリストがきちんと収録されている。

~/work/t2
$ find . -name "*flist"
./design/fpga/gate/fpga_gate_tb.flist
./design/fpga/gate/xilinx_lib.flist
./design/fpga/rtl/fpga_rtl.flist
./design/fpga/rtl/fpga_rtl_synth.flist
./design/sys/iop/ccu/ccu.flist
./design/sys/iop/ccx/ccx.flist
./design/sys/iop/ccx/ccx_rtl.flist
...

なるほど、これらを連結していって、Modelsimでオリジナルの環境を構成すればよい気がしている。./verif/env/cmp/cmp.flistがどうやら中心のファイルになるかな?

// Testbench Files for cmp Bench

+define+OPENSPARC_CMP+
+incdir++
+incdir+../common/verilog/checkers+
+incdir+../common/verilog/misc+
+incdir+../common/coverage+
+libext+.v+
cmp_top.v
verif_args.v
-y ../../../libs/analog/n2_esd_core_cust_l/n2_esd_core_cust/rtl
-y ../../../libs/analog/n2_pcmb_cust_l/n2_pcmb_cust/rtl
-y ../../../libs/analog/n2_pcma_cust_l/n2_pcma_cust/rtl
-y ../../../libs/analog/n2_tmpd_cust_l/n2_tmpd_cust/rtl
-y ../../../libs/analog/n2_rng_cust_l/n2_rng_cust/rtl
-y ../../../libs/analog/n2_revid_cust_l/n2_revid_cust/rtl
-y ../../../libs/clk/n2_clk_gl_cust_l/n2_clk_gl_cust/rtl
-y ../../../libs/clk/n2_clk_pgrid_cust_l/n2_clk_spc_cmp_cust/rtl
-y ../../../libs/n2sram/mp/n2_frf_mp_256x78_cust_l/n2_frf_mp_256x78_cust/rtl
-y ../../../libs/tisram/core/n2_icd_sp_16p5kb_cust_l/n2_icd_sp_16p5kb_cust/rtl
-y ../../../libs/tisram/core/n2_ict_sp_1920b_cust_l/n2_ict_sp_1920b_cust/rtl
-y ../../../libs/n2sram/dp/n2_dva_dp_32x32_cust_l/n2_dva_dp_32x32_cust/rtl
-y ../../../libs/n2sram/tlbs/n2_tlb_tl_64x59_cust_l/n2_tlb_tl_64x59_cust/rtl
-y ../../../libs/n2sram/mp/n2_irf_mp_128x72_cust_l/n2_irf_mp_128x72_cust/rtl

定義やオプションなども一通り含まれているので、とりあえずこれを使えば大丈夫そうだ。

TOEICの結果と感想2017

1/29に受験したTOEIC試験の結果が公開された。結果としては、前回受験よりも10点ダウン。 下がってしまったのはしょうがないけれども、思ったより下がり幅が小さかったので一安心。

TOEICの新形式になり始めての受験だったが、問題形式が変更になったことと、少し難易度が上がったような気がしていたのだが、TOEICはおそらく全体平均で配点が変わっているので、それで点が思ったより下がらなかったのかもしれない。

msyksphinz.hatenablog.com

前回の記事を眺めていると、結構いろいろやっていたんだな、と思った。 そもそも仕事で英語を使う機会がかなり減ってしまったのだが、

  • レアジョブ

継続中。週2回。

電車に乗る機会が減ってしまい止めてしまった。

  • 英会話カフェ

付近に無く止めてしまった。

あれー、ほとんど英語関係の活動を止めてしまっている。また何か再開するかなあ。

マラソン用イヤホン購入

今までマラソンに出るときは、ソニーBluetoothを使っていたのだけれど、レシーバが大きくてちょっと不便だった。

www.sonymobile.co.jp

そこでマラソン用のイヤホンを購入した。Amazonでたくさん調べて、ヨドバシカメラをぐるぐる回って、結局ヨドバシカメラのそれなりに安いやつにした。 Amazonで調査した意味とはいったい。。。

www2.elecom.co.jp

この色よ(笑)。

付け心地は、デフォルトのイヤーピースが大きくて痛かったので、付属の小型のイヤーピースに交換し、ちょうど良い。 実はこれつけてまだ走っていないんだけど、一日部屋の中で使い続けてみて、別に不自由は感じない。

ただ、バッテリーの持ちが4時間程度なので、マラソン限定かな。。。

f:id:msyksphinz:20170218124557j:plain

RISC-Vテストパタン VMモードの解析

RISC-V

前回に引き続き、Sv32で動作させるときのテストパタンの動作について解析している。

RISC-Vのテストパタンは、基本的なテストを行うxxx.Sのアセンブラリストと、それを囲むプロローグとエピローグから構成されている。

中心となる検証用アセンブラリストは、以下のようなものだ。

riscv-tests/add.S at master · riscv/riscv-tests · GitHub

  #-------------------------------------------------------------
  # Arithmetic tests
  #-------------------------------------------------------------

  TEST_RR_OP( 2,  add, 0x00000000, 0x00000000, 0x00000000 );
  TEST_RR_OP( 3,  add, 0x00000002, 0x00000001, 0x00000001 );
  TEST_RR_OP( 4,  add, 0x0000000a, 0x00000003, 0x00000007 );

  TEST_RR_OP( 5,  add, 0xffffffffffff8000, 0x0000000000000000, 0xffffffffffff8000 );
  TEST_RR_OP( 6,  add, 0xffffffff80000000, 0xffffffff80000000, 0x00000000 );
  TEST_RR_OP( 7,  add, 0xffffffff7fff8000, 0xffffffff80000000, 0xffffffffffff8000 );

  TEST_RR_OP( 8,  add, 0x0000000000007fff, 0x0000000000000000, 0x0000000000007fff );
  TEST_RR_OP( 9,  add, 0x000000007fffffff, 0x000000007fffffff, 0x0000000000000000 );
  TEST_RR_OP( 10, add, 0x0000000080007ffe, 0x000000007fffffff, 0x0000000000007fff );

  TEST_RR_OP( 11, add, 0xffffffff80007fff, 0xffffffff80000000, 0x0000000000007fff );
  TEST_RR_OP( 12, add, 0x000000007fff7fff, 0x000000007fffffff, 0xffffffffffff8000 );

こんな感じのテストパタンが、ずらっと並んでいる。これを囲むのがプロローグ関数とエピローグ関数なのだが、まずはプロローグから見ていこう。

Sv32の変換モードを設定するプロローグ関数vm_boot()

env/v/vm.cに記述されているvm_boot()は、仮想メモリアドレスの変換設定をしている中心となコードだ。

void vm_boot(uintptr_t test_addr)
{
...

  write_csr(sptbr, (uintptr_t)l1pt >> PGSHIFT);
  // map kernel to uppermost megapage
  l1pt[PTES_PER_PT-1] = ((pte_t)kernel_l2pt >> PGSHIFT << PTE_PPN_SHIFT) | PTE_V;
  // map user to lowermost megapage
  l1pt[0] = ((pte_t)user_l2pt >> PGSHIFT << PTE_PPN_SHIFT) | PTE_V;
...

sptbrレジスタを指定しており、仮想メモリアドレスモードに入ったときのテーブルウォークを行うベースアドレスを設定している。 ここでは、l1pt = pt[0]を指しており、これはページテーブルの先頭に値する。ここからページテーブルをたどって行き、目的のアドレスに到達するわけだ。

...
  // set up supervisor trap handling
  write_csr(stvec, pa2kva(trap_entry));
  write_csr(sscratch, pa2kva(read_csr(mscratch)));
  write_csr(medeleg,
    (1 << CAUSE_USER_ECALL) |
    (1 << CAUSE_FAULT_FETCH) |
    (1 << CAUSE_FAULT_LOAD) |
    (1 << CAUSE_FAULT_STORE));

mscratchレジスタの値はtrap_vectorから少し先、トラップが発生したときのアドレスを格納しており、これをsscratchにコピーしている。 この際、スーパバイザモードでは仮想アドレスモードで動作するので、あらかじめpa2kvaで物理アドレスから仮想アドレスに値を変換して格納している。

さらに、権限委譲のためのレジスタを設定しており、ユーザモードからのトラップ、メモリアクセス関連の例外はすべてスーパバイザが処理するように設定された。

この後、テストパタンがなぜか指定のページに飛ばない問題をずっと追いかけていたのだが、どうもテストパタンのスタートアドレスを指定する方式に問題があるようだった。

  trapframe_t tf;
  memset(&tf, 0, sizeof(tf));
  tf.epc = test_addr - DRAM_BASE;
  pop_tf(&tf);

これ、test_addrからDRAM_BASE`を減算するだけでなぜOKなんだろう?そもそも仮想アドレスから物理アドレスへの変換は、

riscv-test-env/vm.c at 9e219c9ca70459bfda9067d637bb8bf52c5f0326 · riscv/riscv-test-env · GitHub

#define pa2kva(pa) ((void*)(pa) - DRAM_BASE - MEGAPAGE_SIZE)

で行われるはずだ。じゃあtest_addrの変換も、同様に変換しなければならないだろう。

  trapframe_t tf;
  memset(&tf, 0, sizeof(tf));
  // tf.epc = test_addr - DRAM_BASE;
  tf.epc = pa2kva(test_addr);
  pop_tf(&tf);

が、正解じゃないの?

テストが終了した後のエピローグ関数

テストが終了すると、テストがPassしたかFailしたかを判定しなければならないのだが、それをriscv-testsではコードとして管理している。

まずテストが終了すると、ECALL(Supervisor Trap)を発行させて、マシンモードに行こうする。(mdelegで"Ecall from Supervisor"は有効になっていないので、スーパバイザモードからマシンモードに素直に移行するはずだ。

つぎに最後のコードを見て、最終判断をするわけだが、最後の判定はhandle_trap()にて行われている。

void handle_trap(trapframe_t* tf)
{
  if (tf->cause == CAUSE_USER_ECALL)
  {
    int n = tf->gpr[10];

    for (long i = 1; i < MAX_TEST_PAGES; i++)
      evict(i*PGSIZE);

    terminate(n);
  }
  else if (tf->cause == CAUSE_ILLEGAL_INSTRUCTION)
  {
    assert(tf->epc % 4 == 0);
...

ユーザモードでないとterminateが実行されない仕組みになっている。そんなことないでしょ!これまでずっとスーパバイザモードでテストしてたのに!

という訳で、

  if (tf->cause == CAUSE_USER_ECALL)

は、

  if (tf->cause == CAUSE_SUPERVISOR_ECALL)

では無かろうか?これらを変更すると、無事に自作ISSでテストパタンがパスした。 これらの問題はRISC-Vのメーリングリストに投げて、質問している。

RISC-V ISS Sv32, Sv39の実装

RISC-V

いまいち実装が進んでいないが、Sv32とSv39のISSへの実装しなおしが完了した。

Sv32とSv39については、前回の記事を参考にして欲しい。

msyksphinz.hatenablog.com

github.com

実装方法としては、まずはPTEを参照しながら上位の物理アドレスを生成していき、最後にPTEを参照しないアドレスを付加する。

  for (level = init_level-1; level >= 0; level--) {
    bool is_leaf_achieve = false;
    Byte_t p_pte[4] = {0};
    Addr_t va_vpn_i = static_cast<UWord_t>(vaddr) >> (12 + 10 * level) & 0x3ff;
    pte_addr += va_vpn_i * PTESIZE;
    pte_addr = ExtendSign(pte_addr, 31);

    LoadMemoryDebug (pte_addr, Size_Word, p_pte);
    memcpy (&pte_val, p_pte, 4);

...
    Addr_t mask = ((pte_val >> pte_idx[level]) & ((1 << pte_len[level])-1)) << ppn_idx[level];
    phy_addr |= ((pte_val >> pte_idx[level]) & ((1 << pte_len[level])-1)) << ppn_idx[level];
    pte_addr = ((pte_val >> pte_len[0]) & ((1 << (31-pte_len[0]+1))-1)) * PAGESIZE;
    // str << std::hex << " <Temporary Mask = " << std::hex << mask << ">\n";
    // str << std::hex << " <Temporary PhyAddr = " << std::hex << std::setw(16) << std::setfill('0') << phy_addr << ">\n";
    // DebugPrint ("%s", str.str().c_str());
    if (is_leaf_achieve) break;
  }

  // 下位のアドレスを接続する。
  for (level = level-1; level >= 0; level--) {
    phy_addr |= (vaddr >> vpn_idx[level]) & ((1 << vpn_len[level])-1) << ppn_idx[level];
  }

これで実装をしてみたのだが、テストパタンとしては途中でテーブル参照エラーを起こし、最後は無限ループになってしまった。結局最大サイクル数で停止してしまう。spikeでも同様のようだ。 これで本当にテストパタンはあっているのだろうか?

特権命令の例外権限を委譲するためのRISC-Vシステムレジスタmedeleg, mideleg

RISC-V

テストパタンのチェックをしていて、スーパバイザモードで例外が発生してもスーパバイザモードに留まっているパタンがおり、何が起きているのだろうと調べていると、以下のようなレジスタの仕様を発見した。

目的としては例外が発生した場合のコンテキストスイッチの時間を最小限にし、割り込みの応答性を上げるものらしい。 medelegレジスタmidelegレジスタは、本来スーパーユーザが実行すべき例外プログラムを、別の特権ユーザが変わりに実行するためのレジスタだ。

f:id:msyksphinz:20170215012210p:plain

上記の図だけ見ると何のことだか分からないが、各レジスタのビットフィールドはRISC-Vの割り込み、例外のビットエンコーディングに相当しており、 例えばbit1=命令アクセスフォルトに相当し、bit1=1ならば、この例外が発生した場合にプロセッサはマシンモードに移らず、スーパバイザモードで例外を処理する。

f:id:msyksphinz:20170215012413p:plain

割り込みの場合も同様だ。midelegレジスタを設定することで、割り込みが発生した場合に、マシンモードに遷移せずにより低い特権モードで動作することが出来る。

このようなテストパタンは実際に存在し、例えばriscv-testsのrv32ui-v-addテストパタンでは、最初に以下のような設定をしている。

        42:M:MBar:[8000299c][P8000299c] 30279073 : csrrw      r00,0x302,r15        medeleg=>00000000 r15=>000001a2 medeleg<=000001a2

これは、

  • Instruction Access Fault (bit1)
  • Load Address Fault (bit5)
  • Store/AMO access Fault (bit7)
  • Environment call from U-mode (bit8)

はマシンモードで処理されずに、スーパバイザモードで処理されることを意味する。

Bitnamiで構築したRedmineをBitbucketと同期させる

前回の続きだが、前回はRedmineGithubを同期させた。

msyksphinz.hatenablog.com

同じ仕組みで、RedmineとBitbucketを連携させることが出来る。同じように、bitbucketからdaemonアカウントを使ってgitリポジトリをダウンロードし、Redmineリポジトリを関連付ける。

Bitbucketにdaemonsshキーを登録しておけば、同様にbitbucketにプッシュしただけでRedmineリポジトリを反映させることが出来た。

f:id:msyksphinz:20170212120859p:plain

f:id:msyksphinz:20170212121127p:plain