FPGA開発日記

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

RISC-Vにおける動作モードの管理

前回の日記で、RISC-Vにおける動作モードの管理が良く分からないと書いたが、良く考えるとちゃんと仕様書に書いてあった。

RISC-Vでは、システムレジスタであるmstatusを使って複数の動作モードの遷移を管理している。

f:id:msyksphinz:20160308020511p:plain

f:id:msyksphinz:20160308020824p:plain

このルールに基いて、特権モードを移行すれば良い。さっそく実装していこう。

1. RISC-Vにおける特権モードの遷移

単純にmstatusを遷移させていく。

  PrivMode current_priv = m_env->GetPrivMode ();
  UDWord_t mstatus;
  uint32_t csr_status = m_env->CSRRead (SYSREG_ADDR_MSTATUS, &mstatus);

  PrivMode next_priv_field = static_cast<PrivMode>((mstatus >> 4) & 3);
  UDWord_t next_mstatus = static_cast<PrivMode>((mstatus & 0x0ffff) >> 3) | (mstatus & 0xffff0000);
  m_env->CSRWrite (SYSREG_ADDR_MSTATUS, next_mstatus);

また、これ以外にも遷移を示すメッセージを表示させていたのだが、これもmstatusに応じて表示を変える必要がある。これは時間が無いので、あとでやろう。

2. MMUの動作記述

MMUの実装も進めている。ページテーブルのVビットとDビットについても実装をしないと、テストはパスしない。

ストアにおいて、

という部分の実装がうまくいっていなかった。ちゃんと修正しなければ。あとコードが汚いので改善する。

  Addr_t  phy_addr = vaddr & 0x0fff;
  DWord_t pte_val;
  if (GetVmMode() == Vm_Sv39) {
    int level;
    Addr_t pte_addr;
    for (level = 2; level >= 0; level--) {
      Byte_t *p_pte;
      p_pte = reinterpret_cast<Byte_t *>(&pte_val);
      Addr_t va_vpn_i = vaddr >> (12 + 9 * level * 4);
      pte_addr = pte_base + va_vpn_i;
      LoadMemoryDebug (pte_addr, Size_DWord, p_pte);
      memcpy (&pte_val, p_pte, 4);

      pte_val |= 0x20;

      if (!(pte_val & 0x01)) {
        std::cout << "<Generate Exception from " << GetPrivMode() << '\n';
        GenerateException (Except_InstAccessFault);
        break;
      }
      if ((pte_val & 0x01) &&
        ((pte_val >> 1) & 0x0f) >= 2) {
        if (IsAllowedAccess ((pte_val >> 1) & 0x0f, acc_type, GetPrivMode())) {
          if (acc_type == WriteMemType) {
            pte_val |= 0x40;
          }

          break;
        } else {
          GenerateException (Except_StoreAccessFault);
          break;
        }
        break;
      }
      UWord_t vpn_mask = (0x1ff << (level * 9 + 10));
      phy_addr |= (pte_val & vpn_mask);

    }
    for (; level >= 0; level--) {
      UWord_t vpn_mask = (0x1ff << (level * 9 + 10));
      phy_addr |= (vaddr & vpn_mask);
    }
    Byte_t *pte_byte = reinterpret_cast<Byte_t *>(&pte_val);
    StoreMemory (pte_addr, Size_DWord, pte_byte);

    std::cout << std::hex << " <PyhAddr = " << std::hex << phy_addr << ">\n";
  } else {
    phy_addr = vaddr;
  }