FPGA開発日記

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

RISC-VのAtomic Operation命令について

RISC-VにはいくつかのAtomic演算命令が定義されている。

riscv.org

Load-Reserved/Store-Conditional 命令

f:id:msyksphinz:20180818215746p:plain
図. Load-Reserved/Store Conditional命令

LR命令はアドレスrs1からのデータをレジスタrdにロードし、その時のアドレスをRegister Reservationに記憶する。 SC命令はレジスタrs2のデータをアドレスrs1にストアするのだが、Register Reservationに格納されているアドレスがrs1と一致していない場合、そのストア処理は無効となる。

  • ストアが無効の場合 : rdに非ゼロを格納される。
  • ストアが有効な場合 : rdにゼロを格納される。

使いどころなのだが、以下のようなサンプルプログラムが掲載されている。 lr.wで値をロードし、sc.wでアップデートする。

# a0 holds address of memory location
# a1 holds expected value
# a2 holds desired value
# a0 holds return value, 0 if successful, !0 otherwise
cas:
  lr.w  t0, (a0)     # Load original value.
  bne   t0, a1, fail # Doesn’t match, so fail.
  sc.w  a0, a2, (a0) # Try to update.
  jr    ra           # Return.
fail:
  li    a0, 1        # Set return to failure.
  jr    ra           # Return.

Load-Reserved / Store-Conditional の Memory Orderingビット

上記の図のように、LR/SC命令ではaqビットとrlビットが付加されている。 aqはacquire、rlはreleaseと考えるとよい。

  • aq : Acquire操作命令の後続のメモリアクセス命令はその命令を追い越すことができない。
  • rl : Release操作命令はその命令よりも前のメモリアクセス命令よりも前に実行することができない。
  • aqrlが両方1 : 完全に前後のメモリアクセス命令を追い越したり追い越されたりすることはできない。
f:id:msyksphinz:20180818231932p:plain

Atomicメモリ操作命令

f:id:msyksphinz:20180818224603p:plain
図. Atomicメモリ操作命令

Atomicメモリ操作は、rs1レジスタで指定されるアドレスからのデータをメモリから読み込み、rs2レジスタとの操作を行ったうえで、rs1レジスタで指定されるアドレスにデータをストアする。

Atomicメモリ操作の例外

Atomicメモリ操作命令は、2種類の例外が発生する可能性がある。

  • メモリアドレスMisaligned例外 : メモリアドレスが操作するデータ型のサイズに合っていない。
  • メモリページ例外 : 参照するメモリアドレスのページ変換処理に失敗する。

AMO命令にはロード処理・ストア処理が入るのだが、どちらもページ変換処理に失敗したときにはStore Page Faultが発生するらしい。 何故だ?

  • riscv-isa-sim/riscv/mmu.h
  // template for functions that perform an atomic memory operation
  #define amo_func(type) \
    template<typename op> \
    type##_t amo_##type(reg_t addr, op f) { \
      if (addr & (sizeof(type##_t)-1)) \
        throw trap_store_address_misaligned(addr); \
      try { \
        auto lhs = load_##type(addr); \
        store_##type(addr, f(lhs)); \
        return lhs; \
      } catch (trap_load_page_fault& t) { \
        /* AMO faults should be reported as store faults */ \
        throw trap_store_page_fault(t.get_tval()); \
      } catch (trap_load_access_fault& t) { \
        /* AMO faults should be reported as store faults */ \
        throw trap_store_access_fault(t.get_tval()); \
      } \
    }