FPGA開発日記

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

Freedom-U-SDKで生成したLinuxバイナリを自作RISC-Vシミュレータで実行 (4. 命令セットシミュレータの高速化検討)

RISC-Vの自作シミュレータでLinuxを立ち上げることに成功したが、まだかなりシミュレーション速度が遅いことが心配だ。

原因はすでに解析済み。メモリアクセスが何度も発生しているためにそこが最適化できていない。 以下は前回Google Perftoolsで解析したシミュレータのボトルネック部分。 LoadMemoryと言う、メモリアクセスをつかさどっている部分にかなりのアクセスが集中している。 WalkPageTableと言うこちらも仮想アドレスから物理アドレスへの変換を行う機構もやはりかなりのアクセスが必要になっている。

この辺りを改良するためには、TLBを実装するとよい。 という訳で、非常に簡単なTLBを実装してメモリアクセスの高速化の検討を行った。

(pprof) top
Total: 19415 samples
    3750  19.3%  19.3%     5083  26.2% Memory::LoadMemory
    3088  15.9%  35.2%     6920  35.6% RiscvPeThread::WalkPageTable
    1457   7.5%  42.7%    19198  98.9% RiscvPeThread::StepExec
    1384   7.1%  49.9%     9034  46.5% RiscvPeThread::FetchMemory
    1185   6.1%  56.0%     1185   6.1% CsrEnv::Riscv_Read_CSR
    1173   6.0%  62.0%     1173   6.0% RiscvDec::DecodeInst
     872   4.5%  66.5%      872   4.5% __memmove_avx_unaligned_erms
     768   4.0%  70.4%      768   4.0% TraceInfo::RecordTrace
     543   2.8%  73.2%     2056  10.6% RiscvPeThread::CheckInterrupt
     397   2.0%  75.3%     3561  18.3% EnvBase::LoadMemoryDebug

RISC-VにTLBはある?

誤解を招きそうなのだが、RISC-VにTLBの仕様は明記されていない。 それはRISC-VがISAの仕様だからであり、わざわざTLBの構成まで規定する必要はないからだ。 なので、Spikeを含めTLBの実装方法はかなりバリエーションがある。 ここでは、Spikeの実装を参考にしながらTLBを実装した。

  static const uint32_t tlb_width = 1 << 8;
  bool   m_tlb_en[tlb_width];
  Addr_t m_tlb_tag[tlb_width];
  Addr_t m_tlb_addr[tlb_width];

ここで、さらにTLBの参照及びアップデートの機能を追加していく。 基本的には、上記で実装しているページWalkをつかさどる関数 WalkPageTable() に追加してけば良い。

  //===================
  // Simple TLB Search
  //===================
  Addr_t  vaddr_vpn = (vaddr >> 12);
  uint8_t vaddr_tag = vaddr_vpn & (tlb_width-1);
  if (m_tlb_en[vaddr_tag] && m_tlb_tag[vaddr_tag] == vaddr_vpn) {

TLBにヒットしなかったときはTLBをアップデートする。

  //==========================
  // Update Simple TLB Search
  //==========================
  DebugPrint("<Info: TLB[%d] <= 0x%016lx(0x%016lx)>\n", vaddr_tag, vaddr_vpn, *paddr & ~0x0fff);
  m_tlb_en  [vaddr_tag] = true;
  m_tlb_tag [vaddr_tag] = vaddr_vpn;
  m_tlb_addr[vaddr_tag] = (*paddr & ~0x0fff) | (pte_val & 0x0ff);

Spikeの実装はどのようになっているのか

Spikeも似たような実装になっている。 Spikeの方は少し複雑で、命令フェッチ、Load、Storeで別々のタグが用意されている。

  • riscv-isa-sim/riscv/mmu.h
  // If a TLB tag has TLB_CHECK_TRIGGERS set, then the MMU must check for a
  // trigger match before completing an access.
  static const reg_t TLB_CHECK_TRIGGERS = reg_t(1) << 63;
  tlb_entry_t tlb_data[TLB_ENTRIES];
  reg_t tlb_insn_tag[TLB_ENTRIES];
  reg_t tlb_load_tag[TLB_ENTRIES];
  reg_t tlb_store_tag[TLB_ENTRIES];

測定結果

Linuxのブートプロセスで、どれくらい高速化されるか観測した。まあまあ速くなった。

1000000000命令実行時間 実行時間
TLB実装前 195.823
TLB実装後(256-entries) 128.412
TLB実装後(512-entries) 128.101
f:id:msyksphinz:20180902183620p:plain