FPGA開発日記

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

自作RISC-V CPUコア実装(LDQ/STQ間のフラッシュインタフェースの実装)

自作CPUの実装、ロードストア命令の物理アドレスが決まらないうえでのハザードが性能ボトルネックになっているのを見た。

ずいぶんと時間が空いてしまったが実装のデバッグを行う。以下のようなプログラムを作成して検証する。

loop:

    div     x3, x3, x4

    sd          x10,  0(x3)
    sd          x11,  8(x3)
    sd          x12, 16(x3)
    sd          x13, 24(x3)
    sd          x14, 32(x3)
    sd          x15, 40(x3)
    sd          x16, 48(x3)
    sd          x17, 56(x3)

    ld          x20,  0(x1)
    ld          x21,  8(x1)
    ld          x22, 16(x1)
    ld          x23, 24(x1)
    ld          x24, 32(x1)
    ld          x25, 40(x1)
    ld          x26, 48(x1)
    ld          x27, 56(x1)

    addi        x10, x10, 0x67
    addi        x11, x11, 0x33
    addi        x12, x12, 0xbb
    addi        x13, x13, 0x67
    addi        x14, x14, 0x33
    addi        x15, x15, 0xbb
    addi        x16, x16, 0x67
    addi        x17, x17, 0x33

ストア命令が先行しており、次にロード命令が続く。ストア命令のベースアドレスはdiv命令によって算出されるため非常に遅い。従ってロード命令が先行して投機実行されてほしいが、SD命令のアドレスが解決するまではLD命令は実行できない。

このため、とりあえずLD命令を投機的に実行させて、SD命令を実行時に同じアドレスを先行したLD命令が踏んでいる場合はそのLD命令をフラッシュさせるという作戦にでる。このフラッシュ信号をANOTHER_FLUSHと名付け新たに定義する。

まずSD命令とLD命令でアクセスする領域が被らなかった場合、投機的に実行されたLD命令はフラッシュされずにそのままコミットされる。

ANOTHER_FLUSHを適用した場合は、4サイクル、ANOTHER_FLUSHを適用しなかった場合は15サイクル必要となる。うまく行ったようだ。

4558 : 20 : PC=[0000000080000050] (05,01) 0241c1b3 div     gp, gp, tp
GPR[03](37) <= 0000000080000200
4558 : 21 : PC=[0000000080000054] (05,02) 00a1b023 sd      a0, 0(gp)
MW8(0x0000000080000200)=>0000000000000567
4558 : 22 : PC=[0000000080000058] (05,04) 00b1b423 sd      a1, 8(gp)
MW8(0x0000000080000208)=>0000000000000233
4558 : 23 : PC=[000000008000005c] (05,08) 00c1b823 sd      a2, 16(gp)
MW8(0x0000000080000210)=>00000000000000bb
4566 : 24 : PC=[0000000080000060] (06,01) 00d1bc23 sd      a3, 24(gp)
MW8(0x0000000080000218)=>0000000000011567
4566 : 25 : PC=[0000000080000064] (06,02) 02e1b023 sd      a4, 32(gp)
MW8(0x0000000080000220)=>0000000000011233
4566 : 26 : PC=[0000000080000068] (06,04) 02f1b423 sd      a5, 40(gp)
MW8(0x0000000080000228)=>00000000000110bb
4566 : 27 : PC=[000000008000006c] (06,08) 0301b823 sd      a6, 48(gp)
MW8(0x0000000080000230)=>0000000000022567
4570 : 28 : PC=[0000000080000070] (07,01) 0311bc23 sd      a7, 56(gp)
MW8(0x0000000080000238)=>0000000000022233
4570 : 29 : PC=[0000000080000074] (07,02) 0000ba03 ld      s4, 0(ra)
MR8(0x0000000080000100)=>0000000000000000
GPR[20](69) <= 0000000000000000
4570 : 30 : PC=[0000000080000078] (07,04) 0080ba83 ld      s5, 8(ra)
MR8(0x0000000080000108)=>0000000000000000
GPR[21](101) <= 0000000000000000
4570 : 31 : PC=[000000008000007c] (07,08) 0100bb03 ld      s6, 16(ra)
MR8(0x0000000080000110)=>0000000000000000
GPR[22](133) <= 0000000000000000
4574 : 32 : PC=[0000000080000080] (08,01) 0180bb83 ld      s7, 24(ra)
MR8(0x0000000080000118)=>0000000000000000
GPR[23](38) <= 0000000000000000
4574 : 33 : PC=[0000000080000084] (08,02) 0200bc03 ld      s8, 32(ra)
MR8(0x0000000080000120)=>0000000000000000
GPR[24](70) <= 0000000000000000
4574 : 34 : PC=[0000000080000088] (08,04) 0280bc83 ld      s9, 40(ra)
MR8(0x0000000080000128)=>0000000000000000
GPR[25](102) <= 0000000000000000
4574 : 35 : PC=[000000008000008c] (08,08) 0300bd03 ld      s10, 48(ra)
MR8(0x0000000080000130)=>0000000000000000
GPR[26](134) <= 0000000000000000
4578 : 36 : PC=[0000000080000090] (09,01) 0380bd83 ld      s11, 56(ra)

一方で、ANOTHER_FLUSHを実装しない場合はLD命令の完了が大きく遅れる。

4946 : 75 : PC=[0000000080000054] (19,02) 00a1b023 sd      a0, 0(gp)
MW8(0x0000000080000200)=>0000000000000635
4946 : 76 : PC=[0000000080000058] (19,04) 00b1b423 sd      a1, 8(gp)
MW8(0x0000000080000208)=>0000000000000299
4946 : 77 : PC=[000000008000005c] (19,08) 00c1b823 sd      a2, 16(gp)
MW8(0x0000000080000210)=>0000000000000231
4946 : 78 : PC=[0000000080000060] (19,16) 00d1bc23 sd      a3, 24(gp)
MW8(0x0000000080000218)=>0000000000011635
4954 : 79 : PC=[0000000080000064] (20,01) 02e1b023 sd      a4, 32(gp)
MW8(0x0000000080000220)=>0000000000011299
4954 : 80 : PC=[0000000080000068] (20,02) 02f1b423 sd      a5, 40(gp)
MW8(0x0000000080000228)=>0000000000011231
4954 : 81 : PC=[000000008000006c] (20,04) 0301b823 sd      a6, 48(gp)
MW8(0x0000000080000230)=>0000000000022635
4954 : 82 : PC=[0000000080000070] (20,08) 0311bc23 sd      a7, 56(gp)
MW8(0x0000000080000238)=>0000000000022299
4962 : L1D Stq Store : 80000200(16) : ____________________________________00000000_00000299_00000000_00000635
4966 : L1D Stq Store : 80000210(16) : 00000000_00011635_00000000_00000231____________________________________
4970 : L1D Stq Store : 80000220(17) : ____________________________________00000000_00011231_00000000_00011299
4974 : L1D Stq Store : 80000230(17) : 00000000_00022299_00000000_00022635____________________________________
5030 : 83 : PC=[0000000080000074] (21,01) 0000ba03 ld      s4, 0(ra)
MR8(0x0000000080000100)=>0000000000000000
GPR[20](48) <= 0000000000000000
5030 : 84 : PC=[0000000080000078] (21,02) 0080ba83 ld      s5, 8(ra)
MR8(0x0000000080000108)=>0000000000000000
GPR[21](78) <= 0000000000000000
5030 : 85 : PC=[000000008000007c] (21,04) 0100bb03 ld      s6, 16(ra)
MR8(0x0000000080000110)=>0000000000000000
GPR[22](109) <= 0000000000000000
5030 : 86 : PC=[0000000080000080] (21,08) 0180bb83 ld      s7, 24(ra)
MR8(0x0000000080000118)=>0000000000000000
GPR[23](141) <= 0000000000000000
5038 : 87 : PC=[0000000080000084] (22,01) 0200bc03 ld      s8, 32(ra)
MR8(0x0000000080000120)=>0000000000000000
GPR[24](49) <= 0000000000000000
5038 : 88 : PC=[0000000080000088] (22,02) 0280bc83 ld      s9, 40(ra)
MR8(0x0000000080000128)=>0000000000000000
GPR[25](79) <= 0000000000000000
5038 : 89 : PC=[000000008000008c] (22,04) 0300bd03 ld      s10, 48(ra)
MR8(0x0000000080000130)=>0000000000000000
GPR[26](110) <= 0000000000000000
5038 : 90 : PC=[0000000080000090] (22,08) 0380bd83 ld      s11, 56(ra)
MR8(0x0000000080000138)=>0000000000000000
f:id:msyksphinz:20211206004716p:plain

RISC-Vの新拡張仕様概観1

2021年11月に新拡張仕様がかなりの数Ratifiedされた。その情報自体については知っていたけれども、あまり詳細を調査していなかったので見てみることにする。

RISC-Vの拡張仕様は通常MISAレジスタによってA-Zに対応付けて表記されるが、拡張が増えすぎてしまってそれでは収まらなくなっている。従ってZを接頭語とする拡張命令の表記方法がいくつか登場している。

  • PMPエンハンスメント、つまりSmepmpPMPの拡張によりマシンモードでもPMPが使うことが出来るようになるモード。
  • Cache Management Operationはキャッシュ操作命令。
  • Bit-Manipulation はビット操作命令。PopCount系など。
  • Cryptography Extension は暗号化系の拡張命令
  • RISC-V Vector Extensionはベクトル拡張命令
  • RISC-V Instruction Set Manual Volume II: Priviledged Architecture はRISC-V命令本体に入る新仕様。
  • Standard Extensions for Half-Precision Floating Point は半精度浮動小数点命令
  • Standard Extensions for Floating-Point in Integer Registers は整数レジスタ浮動小数点命令を使うための仕様

とこの辺までは把握しているがそれ以外はあまり把握していない。特にState Enable Extensionとかよく意味が分からない。いろいろ調査していこう。

「Chiselで始めるデジタル回路設計」を献本いただきました

RISC-V Day Tokyo 2021 Autumnで配布されたChisel本、「Chiselで始めるデジタル回路設計」を献本いただきました。 RISC-V Day Tokyo幹事団の皆様、ありがとうございます。

本書の翻訳の進捗については私は全く関わっていないのですが、Slack上で議論が行われているのは見ていたし、RISC-V勉強会でのレビューも参加したので最終成果物まで進んでよかったなと思っています。

f:id:msyksphinz:20211125003720p:plain

Chiselは本当に普及するか

RISC-V Day Tokyo で配布となったChisel、当日の発表などを見ていると、大学の先生などはChiselに期待しているところもあるようだ。 Chiselはデジタル回路設計用のハードウェア記述言語だ。Scalaをベースにしており、SiFiveなどが実プロダクト向けに使用している。

私もChiselはある程度使った。Scalaの良いところとを使えば、ある程度上手くハードウェアを記述できることは間違いない。 例えば、Queueを実装しているとどうしてもmapやReduceに相当する機能が欲しくなる。SystemVerilogのlogicstructにそのような機能が欲しくなる時があるが、Scalaはこれらをカバーできる。

しかし、実際問題「言語として優れている」という問題と、「その言語が普及するか」というのは全くの別問題だ。 例えば、Scalaはそもそもハードウェア屋さんが基本的に学んでいるであろう共通プログラミング言語には入らないと思う(Chiselが登場する前、どれだけのハードウェアエンジニアが"Scala"というプログラミング言語を熟知していたか想像してほしい)。その人たちにいきなりScalaを渡してこの上でハードウェアを記述しろというのは結構非現実的だ。 さらにIntelliJがないとChiselの実装はかなり苦しい。補完が効いてくれないとScalaをベースとした実装はかなり苦痛な作業となる。

学生にウケるか?というのは分からない。学生がScalaを使いこなせるかどうかは私は良く知らないし、Verilogに対して一枚皮を被せるという概念はすこしややこしい。 Chiselは最終的にVerilogに変換してASICなりFPGAに起こすし、最終的にはVerilogを読めないと設計にならないし、現在のChiselの記述は「Verilogの気持ちを考えながらScalaを書く」というものになっている。だったらまずVerilog勉強しなよ、となる。 教育のことを考えるなら、Scalaなんて皮を被せないでVerilogを通したテープアウトまでの一連のフローを体験するほうが有益なのでは?


正直な感想を言うと、昨今流行りの「ChiselでCPUを実装する」というのはナンセンスすぎて意味が分からない。

どうしてChiselを使うのか?その理由はScalaの強力な柔軟性を使いたいからだ。 CPUのたった一つのコンフィグレーションを実装するためにChiselを使うのは全くの宝の無駄遣いで、Chiselを使うならば一つの実装から10~100くらいのコンフィグレーションを作り上げられるようにしないと(これがRocketに相当する)、Verilogで作った方がまだマシ、ということになる。 Diplomacyなどのバス結合部分の頻繁に接続が変わりそうなところにこそChiselを適用しなければ、本来の力が発揮できないというものだ。

またテスト環境もイマイチだ。数年前に使ってバグが多すぎて諦めてしまったのだが、多少は改善されたのだろうか。 ポートしか触れないデバッグ環境、UVMに全くかなわないひどいアサーション機能、それなら生成したVerilogに対してUVMを当てまくった方がまだマシだろう(実際過去のプロジェクトではそうした)。

あまり文句を書くと怒られそうなのだが、つまりはせせこましいパイプラインを書くのにChiselを使うのはまだまだ効率が悪く、もっと柔軟に構成を変える必要がある場所にChiselを適用するのは筋が通っていそう、というが現在の私の意見。

と、ここまで書いて、なんかそういう系の話昔も書いたな、って思い出した。「ハードウェア記述言語」の問題なんて半導体設計のほんの一部分の話でしかなくて、設計フロー全体をみたらこんな問題小さすぎる、っていう話。

msyksphinz.hatenablog.com

# あと、コミュニティを眺めていても「変人が自分に箔をつけるためにあえて触って自慢している」くらいにしか見えないのが本音だったりする...

自作RISC-V CPUコア実装(LDQ/STQ間のフラッシュインタフェースの確認)

自作CPUの実装、ロードストア命令の物理アドレスが決まらないうえでのハザードが性能ボトルネックになっているのを見た。

次に検討するのは、LDQ/STQの最適化だ。以前の記事で説明したとおり、LDQとSTQの間でインタフェースを作成し、STQからのパイプラインが流れて段階でLDQのエントリをチェックするように変更する。

一応、単純な物理アドレス比較機構まで実装したので、本当にこの機能でフラッシュを検出したうえで正しく動作できるのか(というか逆に現在の段階でアグレッシブなロード命令発行を行った場合に問題が発生することを確認する。

一応実装してみると、なぜか問題を検出せずに正しく終了してしまった。問題になるのはパイプラインの選択時に常にストア側が選択されるというあまり意味のない状態になっていた。

msrh_pkg::issue_t                     w_ex0_replay_issue;
logic [MEM_Q_SIZE-1: 0] w_ex0_replay_index_oh;
assign w_ex0_replay_issue    = stq_replay_if.valid ? stq_replay_if.issue    : ldq_replay_if.issue;
assign w_ex0_replay_index_oh = stq_replay_if.valid ? stq_replay_if.index_oh : ldq_replay_if.index_oh;

assign ldq_replay_if.conflict = stq_replay_if.valid & ldq_replay_if.valid;
assign stq_replay_if.conflict = 1'b0;

これを修正して、ロードとストア同時にパイプラインに入った場合は古い命令の方を選択するようにする。

assign w_ld_selected = ldq_replay_if.valid & ~stq_replay_if.valid |
                       ldq_replay_if.valid &  stq_replay_if.valid & w_ld_is_older_than_st;

assign w_ex0_replay_issue    = w_ld_selected ? ldq_replay_if.issue    : stq_replay_if.issue   ;
assign w_ex0_replay_index_oh = w_ld_selected ? ldq_replay_if.index_oh : stq_replay_if.index_oh;

msrh_rough_older_check
u_pipe_age
  (
   .i_cmt_id0 (ldq_replay_if.issue.cmt_id),
   .i_grp_id0 (ldq_replay_if.issue.grp_id),

   .i_cmt_id1 (stq_replay_if.issue.cmt_id),
   .i_grp_id1 (stq_replay_if.issue.grp_id),

   .o_0_older_than_1 (w_ld_is_older_than_st)
   );


assign ldq_replay_if.conflict = ~w_ld_selected;
assign stq_replay_if.conflict =  w_ld_selected;
f:id:msyksphinz:20211122001135p:plain

ただしこれでも投機的なロードのミスが発生しない。もう少しアグレッシブなやつが必要かなあ。

という訳で、ストア命令のアドレス生成にdiv命令を挟んでストアの発行をメチャ遅くした。

loop:

    div     x3, x3, x4

    sd          x10,  0(x3)
    sd          x11,  8(x3)
    sd          x12, 16(x3)
    sd          x13, 24(x3)
    sd          x14, 32(x3)
    sd          x15, 40(x3)
    sd          x16, 48(x3)
    sd          x17, 56(x3)

    ld          x20,  0(x1)
    ld          x21,  8(x1)
    ld          x22, 16(x1)
    ld          x23, 24(x1)
    ld          x24, 32(x1)
    ld          x25, 40(x1)
    ld          x26, 48(x1)
    ld          x27, 56(x1)

この結果無事に投機的なロードに失敗することが出来た。さあここからフラッシュ機構の実装だ。

4558 : 22 : PC=[0000000080000058] (05,02) 00c1b823 sd      a2, 16(gp)
MW8(0x0000000080000110)=>00000000000000bb
4558 : 23 : PC=[000000008000005c] (05,04) 00d1bc23 sd      a3, 24(gp)
MW8(0x0000000080000118)=>0000000000011567
4566 : 24 : PC=[0000000080000060] (06,01) 02e1b023 sd      a4, 32(gp)
MW8(0x0000000080000120)=>0000000000011233
4566 : 25 : PC=[0000000080000064] (06,02) 02f1b423 sd      a5, 40(gp)
MW8(0x0000000080000128)=>00000000000110bb
4566 : 26 : PC=[0000000080000068] (06,04) 0301b823 sd      a6, 48(gp)
MW8(0x0000000080000130)=>0000000000022567
4566 : 27 : PC=[000000008000006c] (06,08) 0311bc23 sd      a7, 56(gp)
MW8(0x0000000080000138)=>0000000000022233
4570 : L1D Stq Store : 80000100(08) : ____________________________________00000000_00000233_00000000_00000567
4570 : 28 : PC=[0000000080000070] (07,01) 0000ba03 ld      s4, 0(ra)
MR8(0x0000000080000100)=>0000000000000567
==========================================
Wrong GPR[20](37): RTL = 0000000000000000, ISS = 0000000000000567
==========================================
===============================
SIMULATION FINISH : FAIL (CODE=100)
===============================

自作RISC-V CPUコア実装(LDQ/STQ間のフラッシュインタフェースの実装)

自作CPUの実装、ロードストア命令の物理アドレスが決まらないうえでのハザードが性能ボトルネックになっているのを見た。

次に検討するのは、LDQ/STQの最適化だ。以前の記事で説明したとおり、LDQとSTQの間でインタフェースを作成し、STQからのパイプラインが流れて段階でLDQのエントリをチェックするように変更する。

f:id:msyksphinz:20211110002323p:plain
// LDQ Speculative Load Hazard Check
assign ldq_haz_check_if.ex2_valid  = r_ex2_issue.valid & (r_ex2_issue.cat == decoder_inst_cat_pkg::INST_CAT_ST);
assign ldq_haz_check_if.ex2_paddr  = r_ex2_paddr;
assign ldq_haz_check_if.ex2_cmt_id = r_ex2_issue.cmt_id;
assign ldq_haz_check_if.ex2_grp_id = r_ex2_issue.grp_id;

ストア命令において、LDQに対して検索を行う。一方でLDQエントリはサーチ要求に対して当該物理アドレスがヒットするかをチェックする。

  // STQ -> LDQ Hazard check
  for (genvar p_idx = 0; p_idx < msrh_conf_pkg::LSU_INST_NUM; p_idx++) begin : st_ld_haz_loop
    logic ld_is_younger_than_st;
    msrh_rough_older_check
    st_pipe_ldq_older_check
      (
       .i_cmt_id0 (w_ldq_entries[l_idx].cmt_id),
       .i_grp_id0 (w_ldq_entries[l_idx].grp_id),

       .i_cmt_id1 (ldq_haz_check_if[p_idx].ex2_cmt_id),
       .i_grp_id1 (ldq_haz_check_if[p_idx].ex2_grp_id),

       .o_0_older_than_1 (ld_is_younger_than_st)
       );

    logic   w_ex2_same_dw;
    assign w_ex2_same_dw = |(msrh_lsu_pkg::gen_dw(ldq_haz_check_if[p_idx].ex2_size, ldq_haz_check_if[p_idx].ex2_paddr[2:0]) &
                             msrh_lsu_pkg::gen_dw(w_ldq_entries[l_idx].size, w_ldq_entries[l_idx].paddr[2:0]));
    assign w_ex2_ldq_stq_haz_vld[p_idx][l_idx] = ldq_haz_check_if[p_idx].ex2_valid &
                                                 ld_is_younger_than_st &
                                                 (ldq_haz_check_if[p_idx].ex2_paddr[riscv_pkg::PADDR_W-1: 3] == w_ldq_entries[l_idx].paddr[riscv_pkg::PADDR_W-1: 3]) & w_ex2_same_dw;
  end // block: st_ld_haz_loop

rough_older_checkとういうのはどちらの命令が若いかをチェックするためのモジュールで、cmt_id, grp_idの大小比較で命令の順序を判定している。STQのエントリの方がLDQのエントリよりも古ければ、LDQは投機的に実行されたことになり、さらに物理アドレスの範囲が一致していればLDQの投機実行結果を取り消すことになる。

そのとき、LDQの投機実行命令の内もっとも古い命令を抽出してその命令IDをSTQに伝える。 STQはその命令IDをROBに伝え、それらの命令から以降をフラッシュしてもらうように伝えるという仕組みで、誤った投機結果を打ち消すようになっている。

// ==================
// LDQ Flush Hazard
// ==================
generate for (genvar p_idx = 0; p_idx < msrh_conf_pkg::LSU_INST_NUM; p_idx++) begin : ldq_stq_haz_loop
  msrh_entry_selector
    #(
      .ENTRY_SIZE (msrh_conf_pkg::LDQ_SIZE)
      )
  u_entry_selector
    (
     .i_oh_ptr       (w_out_ptr_oh),
     .i_entry_valids (w_ex2_ldq_stq_haz_vld[p_idx]),
     .o_entry_valid  (w_ex2_ldq_stq_haz_vld_oh[p_idx])
     );

  ldq_entry_t w_sel_ldq_entry;

  bit_oh_or
    #(
      .T     (ldq_entry_t),
      .WORDS (msrh_conf_pkg::LDQ_SIZE)
      )
  u_flush_sel
    (
     .i_oh   (w_ex2_ldq_stq_haz_vld_oh[p_idx]),
     .i_data (w_ldq_entries),
     .o_selected(w_sel_ldq_entry)
     );

  always_ff @ (posedge i_clk, negedge i_reset_n) begin
    if (!i_reset_n) begin
      ldq_haz_check_if[p_idx].ex3_haz_valid <= 1'b0;
    end else begin
      ldq_haz_check_if[p_idx].ex3_haz_valid  <= |w_ex2_ldq_stq_haz_vld[p_idx];
      ldq_haz_check_if[p_idx].ex3_haz_cmt_id <= w_sel_ldq_entry.cmt_id;
      ldq_haz_check_if[p_idx].ex3_haz_grp_id <= w_sel_ldq_entry.grp_id;
    end
  end

end
endgenerate

「はてなブロガーに10の質問」で振り返る「FPGA開発日記」の6年間

はてなブログ10周年特別お題「はてなブロガーに10の質問

これははてなブログの企画です。別に記念日でも何でもありませんが、せっかくなので乗っかることにしました。

FPGA開発日記は2015年からおおよそ6年間続いています。10の質問に答えることでこの6年間を振り返ります。

ブログ名もしくはハンドルネームの由来は?

このハンドル名「msyksphinz」は、大昔に生まれて初めて購入したデスクトップPCのマシン名として使っていたものです。 読み方はありません。最近登壇する機会が増え、もっと読み方が分かりやすいハンドルネームにしておく必要があったなあと思っています...

FPGA開発日記」は、あこがれのハードウェアブログ、marsee101さんの「FPGAの部屋」へのリスペクトが半分くらい入っています。つまり、開設当初から毎日更新しようという気概があったことが分かりますね。

ただし私自身は最近FPGAを全く触っていません(ブログ開設当初は結構触っていましたが)。 久しぶりにFPGAへの回帰したいですね。

はてなブログを始めたきっかけは?

趣味でFPGAを触ったり、自作でハードウェアを作ることはまあまああったのですが、 まとめることが出来ずに何度も同じ失敗を繰り返すことがありました。 また、自分は「文章としてまとめ上げることで本当に理解する」タイプの(つまり非常に効率の悪い)人間であることが分かっていたので、 自分で勉強したこと、体験したことを文章に起こそう、という意味を込めてブログをはじめました。

また、その数か月前に「RISC-V」という単語を聞き、面白そうだとおもったのでRISC-Vの調査結果を書いていこうと思ったのもきっかけの一つです。 ちなみに日本で初めてRISC-Vについて言及したのは「竹下世界塔の計算機よもやま話」だと思っています。私も以下の記事を読んでRISC-Vを知りました。

keisanki.at.webry.info

自分で書いたお気に入りの1記事はある?あるならどんな記事?

最近は年末に書く技術と全く関係ない記事がお気に入りです。っていうかコテコテの技術系のブログを書くよりも、フワッとしたスプリチュアルなものを書いた方が伸びるんですよね。

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com

ブログを書きたくなるのはどんなとき?

基本的に毎日書いていますが、毎日の理由は「期間を設定しないとサボりそうだから」。それだったら1週間に1本とかでもいいけど、無駄にハードルを高くして毎日にしてしまいました。暇だったときは良いけど、今は四苦八苦しています。。。

私のブログは、「解説記事」ではなく、「自分の作業の備忘録」というところが大前提にあります。つまり、毎日記事を書くためには毎日成果を出さなければなりません。これが非常に苦しいところで、仕事が終わってから、頭をひねりながら作業したりブログを書いたりしています。

仕事中でも、「あれ、xxxはブログ記事にできるかな?」と考えることもあり、毎日ブログを書くのは本当に大変ですね。

下書きに保存された記事は何記事? あるならどんなテーマの記事?

最近は非常にその日暮らしが多いです。昔は20記事くらいためているときがあったけど、最近は忙しすぎて下書きが全く溜まりません。。。

自分の記事を読み返すことはある?

調べ物をしていると検索結果に自分のブログが出てくるときがあります。 その時はがっかりするとともに、情報発信者としての責任を再認識します。 以前のブログを読むと、「適当なコト書いてんなー」となることは多いですね。


だから逆に言うと、エンジニアは好き勝手に技術ブログを書いていいと思う。で、「ここ違いますよ」という指摘は基本的に受け入れる体制でないといけない。 「ここが違うかそうでないか」というのは、技術的な議論は結論が出やすい(幸運なことに。政治的な話やスピリチュアルな話はそうもいかない)。だからそういう議論は常にオープンであるという体制で臨まないと、疲弊してしまう。

そして、万が一指摘「ここ間違ってますよ」という指摘に備えて「~である(結論)」論調の場合基本的に「自分の手を動かした結果」から導き出さなければならないかなと思っている。 そういうポリシーが、「FPGA開発日記」は「解説ブログ」ではなく「自分の備忘録」という立ち位置をキープしているゆえんです。

好きなはてなブロガーは?

人のブログを読むことがあまり無いし、はてなブログもあまりたくさんの人のブログを読んでいるわけではないのですが、私が好きなブロガー(はてな以外)を列挙します。

言わずと知れたFPGA界隈の技術ブログ最高峰です。毎日続けて15年は本当に頭が下がります。昔からのmarseeさんのファンで、FPGAの部屋解説当初のDDR-SDRAMのアクセスシーケンスとか波形とか、趣味の開発にかなり読み込んでいた記憶があります。

大学をご退官されてからもFPGAについて積極的に調査しているのは本当にすごくて、しかも過去のデザインや設計手法に固執するのではなく、高位合成などの新しい手法に積極的に挑戦されているのは凄すぎる、と思っています。


つまり何が言いたいかというと、コテコテに技術のことを語るブログは好き。スピリチュアルなポエムばかり載せるブログは嫌い。 (Youtuberとかも含めてね)

はてなブログに一言メッセージを伝えるなら?

うーん、やっぱりCUIからの操作をもっとサポートしてほしいですね。GitHubとの連携とか(記事の管理:Zennとかではすでにできるのかな?)、画像などの引用や貼り付けなどをもう少し簡単にできるようにしてほしい。

10年前は何してた?

10年前も似たようなことをしていた気がしますが、明らかに技術力は上がったと思います。

同じことを繰り返しているような気もしますが、その中で繰り返しの中で少しずつクオリティを上げることが出来始めたかな、と思ってますね。 まあその分成長の度合いが緩やかで、自分で手を動かさないと頭に入らない、ということだと思いますが。。。

この10年を一言でまとめると?

ひたすらエンジニアとして技術力を鍛えた10年間だった気がします。10年前もコンピュータアーキテクチャの勉強はしていましたが、 少しずつ実践を取り込みつつ少しずつ技術力が上がってきたかな? ブログを使って継続的な発信と、ちょっとだけ界隈で有名人になれたのでそれはうれしいです。 そういう意味でははてなブログに感謝ですね。

RISC-VのePMP(PMP Enhancements)仕様について概観する

RISC-VのPrivileged仕様Version 1.12 ではePMPレジスタ群(PMP Enhancements)についての仕様が検討されている。 これについて調査してみよう。

仕様書は以下に置いてある。誰でもダウンロードできる。

https://raw.githubusercontent.com/riscv/riscv-tee/main/Smepmp/Smepmp.pdf

そもそものモチベーションは、現状のPMPはスーパバイザモードについてのみ適用されており、マシンモードでは適用することが出来ない。これではマシンモードと非特権モードを使った攻撃を防ぐことが出来ない。

現在のスーパバイザモードを使った攻撃手法として以下が紹介されている:

  • SMAP(Supervisor memory Access Prevention) : 特定のコードパスをたどらない限り、OSが非特権プロセスのメモリにアクセスできないようにするもの
  • SMEP(supervisor Memory Execution Prevention) : OSが常に非特権プロセスのメモリを実行できないようにするもの

これについては、sstatus.SUMビットによるSMAPのサポートと、Uビットでマークされた仮想メモリページの実行を常に拒否するSMEPのサポートが、Privilege Specで義務付けられているSupervisor mode (OS)の特権ですでに行われている。

これに対して、マシンモードでもさらにPMPが適用できるようにするのがePMPの主たる目的だ。このために、以下のレジスタを定義する。

  • Machine Security Configuration (mseccfg)

    • Machine modeでのみアクセス可能
    • 64ビット幅。RV64では0x747、RV32では0x747(下位32ビット)、0x757(上位32ビット)のアドレスに割り当てられている。
  • mseccfgでは、ビット2にRule Locking Bypass (mseccfg.RLB)と呼ばれるフィールドを導入し、以下の機能を持たせている

      1. mseccfg.RLBが1の場合、ロックされたPMPルールの削除/変更が可能で、ロックされたPMPエントリの編集も可能
      1. mseccfg.RLBが0で、いずれかのルールまたはエントリ(無効化されたエントリを含む)でpmpcfg.Lが1の場合、mseccfg.RLBは0のままであり、mseccfg.RLBへのさらなる変更はPMPリセットまで無視される
  • mseccfgでは、ビット1にMachine Mode Whitelist Policy (mseccfg.MMWP)というフィールドを導入している

    • これはスティッキービットで、一度設定するとPMPをリセットするまで解除できない。
    • このビットがセットされると、MモードでマッチするPMPルールを持たないメモリ領域にアクセスする際のデフォルトのPMPポリシーが、無視ではなく拒否に変更されれる。
  • mseccfgでは、ビット0にMachine Mode Lockdown (mseccfg.MML)というフィールドを導入している

    • これはスティッキービットで、一度設定するとPMPのリセットがかかるまで解除できない
    • mseccfg.MMLが設定されると、システムの動作は次のように変わる
        1. pmpcfg.Lの意味が変わり、ルールをロックしてすべてのモードで強制するのではなく、セットされているときは M モード専用、セットされていないときは S/U モード専用としてルールをマークする
      • これまで予約されていたエンコーディングpmpcfg.RW=01とエンコーディングpmpcfg.LRWX=1111は、Shared-Regionをエンコードする
      • M-mode-onlyルールはMモードで強制され、SupervisorモードやUserモードでは拒否される。また、mseccfg.RLBが設定されていない限り、PMPのリセットが行われるまで、関連するコンフィグレーションやアドレスレジスタの変更が無視されるようにロックされる。S/U-mode-onlyルールはSupervisorおよびUserモードに適用され、Mモードでは拒否される。Shared-Regionルールはすべてのモードに適用されるが、pmpcfg.Lビットおよびpmpcfg.Xビットによる制限がある。
      • pmpcfg.Lが設定されていないShared-Regionルールは、MモードとS/Uモード間のデータ共有に使用されるため、実行は許可されない。Mモードはその領域への読み書きのアクセス権を持ち、S/Uモードはpmpcfg.Xが設定されていなければ読み書きのアクセス権、pmpcfg.Xが設定されていれば読み書きのアクセス権を持つ。
      • pmpcfg.Lが設定されているShared-Regionルールは、M-modeとS/U-modeの間でコードを共有するために使用することができるので、書き込みはでききない。M-modeとS/U-modeの両方が領域の実行アクセス権を持ち、pmpcfg.Xが設定されていればM-modeも読み取りアクセス権を持つ。ルールはロックされたままなので、関連するコンフィグレーションやアドレス・レジスタを変更しても、mseccfg.RLBが設定されていない限り、PMPのリセットまで無視される。
      • pmpcfg.LRWX=1111のエンコーディングは、MモードとS/Uモードの間でデータを共有する場合に使用できる。この場合、どちらのモードもその領域に対して読み取り専用のアクセスしかできない。ルールはロックされたままなので、関連するコンフィグレーションやアドレス・レジスタが変更されても、mseccfg.RLBが設定されていない限り、PMPのリセットまで無視される。
        1. 実行可能権限でMモード専用またはロックされた共有領域ルールを追加することはできず、そのようなpmpcfgの書き込みは無視され、pmpcfgは変更されまない。の制限は、起動時などに mseccfg.RLB を設定することで一時的に解除できる。
        1. マシンモードの特権を持つコードの実行は、実行可能な特権を持つ M モードオンリールールまたはロックされた Shared-Region ルールが一致するメモリ領域からのみ可能。
        1. mseccfg.MMLが設定されていない場合、pmpcfg.RW=01の組み合わせは、将来の標準的な使用のために予約されたままとなる。