FPGA開発日記

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

FPGA開発日記 カテゴリ別インデックス

RISC-VにおけるRVWMOの仕様について読み直す

続きを読む

RISC-Vの高性能アウト・オブ・オーダCPU XiangShanについての資料を読む (4. Microarchitecture Design and Implementation)

HPCA2024で開催された、RISC-Vのアウト・オブ・オーダCPU XiangShanについての資料が公開されていたので、読んでみることにした。 じっくり読みたいので、1ページずつ要点を抑えていく。

次は、マイクロアーキテクチャのトピックについて。続き。

xiangshan-doc.readthedocs.io


  • バックエンドの構成
  • 命令フュージョン
    • 隣接しているuOPsを結合する。
    • RV64GCとRV64GC-Fusedで4%の命令削減、RV64GCBからRV64GCB-Fusedで2%の命令削減。
  • Rename & Move Elimination
    • 単純なmove命令は、リネームマップの差し替えにより実現する。
  • Reservation Stationの構成
    • 2-input & 2-output / 1-input & 1-outputの構成
    • Age Matrixベースの命令選択
  • FPUの構成
    • IEEE 754互換の浮動小数点実行ハードウェア
  • FPUはCascade FMAを実装。
    • FADDのDelayは5サイクルから3サイクルに削減。
  • MMUの構成
    • STLB向けにNext Line Prefetcherを実装
    • Page Table Walkを並列実行
    • PMP / PMAをサポート
  • メモリ・アクセスのブロック
    • ロードパイプラインは2本用意
    • LDヒットレイテンシ:4サイクル
    • ストアアドレスパイプライン:2
    • ストアデータパイプライン:2
    • ストアデータのバンド幅 : 2x8B/cycle
    • マージバッファ:16キャッシュライン
  • ロード・パイプライン
    • 4ステージ構成
  • L1 Dキャッシュ
    • ライトバック・キューはどういう仕組みだろう?
  • L1 Dキャッシュ
    • バンクを分割し、複数リードポートを持っている。

RISC-Vの高性能アウト・オブ・オーダCPU XiangShanについての資料を読む (3. Microarchitecture Design and Implemenation)

HPCA2024で開催された、RISC-Vのアウト・オブ・オーダCPU XiangShanについての資料が公開されていたので、読んでみることにした。 じっくり読みたいので、1ページずつ要点を抑えていく。

次は、マイクロアーキテクチャのトピックについて。

xiangshan-doc.readthedocs.io


XiangShanには3つの世代がある。

  • YQH : 28nm でテープアウトし1.3GHz。性能的にはSPEC CPU2006 7.01 @1GHz
  • NH : 14nmでテープアウトし2.0GHz。性能的にはSPEC CPU2006 20 @2GHz
  • KMH : Vector拡張とハイパーバイザー拡張を実装。
  • まずは第1世代のYQHについて。
    • 11ステージの6命令デコード・リネーム
    • TAGE-SC-Lの分岐予測器を実装
    • 整数物理レジスタ160エントリ、浮動小数点物理レジスタ160エントリ
    • 192エントリのROB、64エントリのLQ、48エントリのSQ
    • 各機能ユニットには16エントリのRS
    • 16KBのL1命令キャッシュ、128KBのL1plus命令キャッシュ
    • 32KBのL1データキャッシュ
    • 32エントリのITLB / DTLB, 4KエントリのSTLB
    • 1MBのインクルーシブ・L2キャッシュ
  • YQHのマイクロアーキテクチャ(パイプライン)
    • ディスパッチするときにレジスタを読むスタイル。14Read / 8Write
    • 最大コミット数は6命令/1サイクル
  • 第2世代、NHのアーキテクチャ
    • 整数物理レジスタ196エントリ、浮動小数点物理レジスタ192エントリに増強
    • ROBは256エントリに増強、80エントリのLQ、64エントリのSQに増強
    • 算術演算ユニットは各32エントリのRS
    • 64KBのL1命令キャッシュ
    • 64KBのL1データキャッシュ
    • 80エントリのDTLB、32エントリのITLB、2KエントリのSTLB
    • 1MBのノン・インクルーシブL2キャッシュ
    • 6MBのノン・インクルーシブL3キャッシュ
  • NHのフロントエンド
    • フロントエンドをProducerとConsumerに分離。
      • Producer : 分岐予測ユニット
      • Consumer : 命令フェッチユニット
  • 分岐予測について
    • 3ステージの分岐予測
      • ステージ1: uBTB
      • ステージ2: FTB + TAGE (FTB : Fetch Target Bufferのこと)
      • ステージ3: SC + ITAGE + RAS (SCは良く分からないが、Statistical Correctorのこと?)
  • 命令キャッシュ
    • 64KBの4-way VIPTブロッキングキャッシュ
    • TileLinkをサポート
    • PLRUのリプレースメント

Vivado Simulatorを用いてUVMに入門する (3. テストベンチの解析)

UVMに入門したくて、簡単な例を用いて試してみることにした。以下のようなシンプルなデザインをテストしたい。

vlsiverify.com

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com


次にMonitorとScoreboardについてみていこうと思う。

monitorについては、駆動側と同様にseq_item mon_itemを用意し、そこに工藤側から生成された入力値を入れておく。 そしてこのadderは計算結果を出力するのに1サイクル必要なため、@(posedge vif.clk)だけ待ってから、DUTの計算結果を格納する。 そして、その結果をitem_collect_portに格納する。item_collect_portseq_itemを含んだuvm_analysis_portで、これの詳細は正直よくわからない。

  task run_phase (uvm_phase phase);
    forever begin
      wait(!vif.reset);
      @(posedge vif.clk);
      mon_item.ip1 = vif.ip1;
      mon_item.ip2 = vif.ip2;
      `uvm_info(get_type_name, $sformatf("ip1 = %0d, ip2 = %0d", mon_item.ip1, mon_item.ip2), UVM_HIGH);
      @(posedge vif.clk);
      mon_item.out = vif.out;
      item_collect_port.write(mon_item);
    end
  endtask

とにかく、このrun_phaseによって、item_collect_port.write()が実行されすべての情報が格納されるというわけだ。

そして、scoreboardによって検証が行われる。scoreboardrun_phaseはでは、item_qの中から1つの要素が取り出され、計算結果があっているかの検証が行われる。 上記のmonitorが、item_collect_port.write()を呼び出したのは、下記のwrite()と同一なのだろうか?この辺の詳細もよくわからない。

  function void write(seq_item req);
    item_q.push_back(req);
  endfunction

  task run_phase (uvm_phase phase);
    seq_item sb_item;
    forever begin
      wait(item_q.size > 0);

      if(item_q.size > 0) begin
        sb_item = item_q.pop_front();
        $display("----------------------------------------------------------------------------------------------------------");
        if(sb_item.ip1 + sb_item.ip2 == sb_item.out) begin
          `uvm_info(get_type_name, $sformatf("Matched: ip1 = %0d, ip2 = %0d, out = %0d", sb_item.ip1, sb_item.ip2, sb_item.out),UVM_LOW);
        end
        else begin
          `uvm_error(get_name, $sformatf("NOT matched: ip1 = %0d, ip2 = %0d, out = %0d", sb_item.ip1, sb_item.ip2, sb_item.out));
        end
        $display("----------------------------------------------------------------------------------------------------------");
      end
    end
  endtask

つまり、UVMの検証環境は、スコアボードによってうまく一致が検出できるようにテストを構成できるか、というのが肝になるのではなかろうか。 つまり、一致比較が可能なようにテストを構成し、Scoreboardでうまく一致比較することがポイントとなる。

RISC-Vの高性能アウト・オブ・オーダCPU XiangShanについての資料を読む (2. XiangShanのAgile Development)

HPCA2024で開催された、RISC-Vのアウト・オブ・オーダCPU XiangShanについての資料が公開されていたので、読んでみることにした。 じっくり読みたいので、1ページずつ要点を抑えていく。

xiangshan-doc.readthedocs.io


次に、XiangShanを支えるAgile Developmentについて見て行く。

最初のアプローチとしては、Chiselを使って開発するというものである。

新しいハードウェア記述言語を使用することによって、デザインのオブジェクト指向化を行い、よりコンフィギャラブルにすることが目的である。

Agile Developmentにおいて必要な要素は何か?それは検証である。

Agile Developmentを支える検証ツールチェインについて見て行く。

Difftestというテスト環境を用意する。命令がコミットされると、命令セットシミュレータが同じ命令を実行し、結果を比較する。

性能評価のために、RTLシミュレーションとチェックポイントの機能を導入する。 チェックポイントによって、コンパイル20分と、シミュレーションはおよそ5時間で実行することができる。

これには4つのステップが必要である:

  1. 高速なシミュレータを使って、命令群をスライスしながらシミュレーションする
  2. クラスタリングアルゴリズムを用いて、代表的なスライスを選出する
  3. RTLシミュレーションを実行する
  4. 再構築する

この手法によって、17個の新しいツールを作成して、検証を高速化した。この成果はMICRO2022に掲載された。

XiangShanでは、3つのポイントのうち2.5個を達成した。

研究のための理想的なインフラストラクチャについて考える。

  • Imprecise Store Exceptions (ISCA23)で使用された。
  • Fast, Robust and Transferable Prediction for Hardware Logic Synthesis (MICRO23)で使用された。

Vivado Simulatorを用いてUVMに入門する (2. テストベンチの解析)

UVMに入門したくて、簡単な例を用いて試してみることにした。以下のようなシンプルなデザインをテストしたい。

vlsiverify.com

adderとのインタフェースとして、以下を定義する。これによりテストベンチとDUTを接続する。

  • verify/addr_if.sv
interface add_if(input logic clk, reset);

logic [ 7: 0] ip1, ip2;
logic [ 8: 0] out;

endinterface // add_if

次に、SequencerとDriverについて見て行く。Sequencerはテストベンチを作成するところだ。 まず、seq_itemによって、ドライブしたい要素をリストアップしていくらしい。ip1ip2はランダムに駆動するものと思われるが、outは値を受け取るだけなので駆動しない。

  • verify/seq_item.sv
 `include "uvm_macros.svh"
import uvm_pkg::*;

class seq_item extends uvm_sequence_item;
  rand bit [ 7: 0] ip1, ip2;
  bit [ 8: 0]      out;

  function new(string name = "seq_item");
    super.new(name);
  endfunction

  `uvm_object_utils_begin(seq_item)
    `uvm_field_int(ip1, UVM_ALL_ON)
    `uvm_field_int(ip2, UVM_ALL_ON)
  `uvm_object_utils_end

  constraint ip_c {ip1 < 100; ip2 < 100;}

endclass // seq_item

これに基づいて、seqcrを定義する。seq_itemを拡張する形で定義する。これはテンプレートとして使えそうな構成だ。

  • verify/seqcr.sv
 `include "uvm_macros.svh"
import uvm_pkg::*;

class seqcr extends uvm_sequencer#(seq_item);
  `uvm_component_utils(seqcr)

  function new(string name = "seqcr", uvm_component parent = null);
    super.new(name, parent);
  endfunction

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
  endfunction // build_phase

endclass // seqcr

これに基づいてDriverを定義する。Driverはadder_ifを駆動するものだ。ポイントはvif.ip1vif.ip2を駆動するところだ。 どうもこれはseq_item_port.get_next_item(req)seq_item_port.item_done()で囲むところがポイントらしい。

  • verify/driver.sv
 `include "uvm_macros.svh"
import uvm_pkg::*;

class driver extends uvm_driver#(seq_item);
  virtual add_if vif;
  `uvm_component_utils(driver)

  function new(string name = "driver", uvm_component parent = null);
    super.new(name, parent);
  endfunction

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    if(!uvm_config_db#(virtual add_if) :: get(this, "", "vif", vif))
      `uvm_fatal(get_type_name(), "Not set at top level");
  endfunction

  task run_phase (uvm_phase phase);
    forever begin
      // Driver to the DUT
      seq_item_port.get_next_item(req);
      `uvm_info(get_type_name, $sformatf("ip1 = %0d, ip2 = %0d", req.ip1, req.ip2), UVM_LOW);
      vif.ip1 <= req.ip1;
      vif.ip2 <= req.ip2;
      seq_item_port.item_done();
    end
  endtask // run_phase

endclass // driver

Vivado Simulatorを用いてUVMに入門する (1. adderを用いたシンプルな例)

UVMに入門したくて、簡単な例を用いて試してみることにした。以下のようなシンプルなデザインをテストしたい。

vlsiverify.com

module adder
  (input logic       clk,
   input logic          reset,
   input logic [ 7: 0]  in1,
   input logic [ 7: 0]  in2,
   output logic [ 8: 0] out
   );

always@(posedge clk, posedge reset) begin
  if (reset)
    out <= 'h0;
  else
    out <= in1 + in2;
end

endmodule // adder

つまり、入力in1in2に対して、出力outが正しく出力されていることを確認したいというわけだ。1サイクル後に結果が出力される。

上記のリファレンスによると、UVMで以下のような構成を想定するらしい。

必要な環境は:

  • TB_top : テストベンチ全体を含む。テスト環境とインタフェース・DUTをすべて含む
  • Test :テスト環境を含む。エージェントとスコアボードを含む
  • Agent:テストを駆動する側。シーケンサ・ドライバ・モニタを含む
  • Sequencer : おそらくはテストシーケンスを生成する側。
  • Driver:シーケンスを元にインタフェースをドライブする。
  • Monitor:インタフェースを通じて結果を取得する。
  • Scoreboard:結果を想定する値と比較する。
.
├── design
│   └── adder.sv
├── verify
│   ├── add_if.sv
│   ├── agent.sv
│   ├── base_seq.sv
│   ├── base_test.sv
│   ├── driver.sv
│   ├── env.sv
│   ├── monitor.sv
│   ├── scoreboard.sv
│   ├── seq_item.sv
│   ├── seqcr.sv
│   └── tb_top.sv
└── xsim
    ├── Makefile
    └── filelist.vf

まずは、Vivado Simulatorを用いてUVMのシミュレーションを行う。

.PHONY: all
all: build run

.PHONY: build
build:
        xvlog -sv $(SRCS) -L uvm --include ../verify
        xelab $(TOP_MODULE) -L uvm -timescale 1ns/1ps

.PHONY: run
run:
        xsim $(TOP_MODULE) -R --testplusarg "UVM_TESTNAME=base_test" -testplusarg "UVM_VERBOSITY=UVM_LOW"
UVM_INFO /tools/Xilinx/Vivado/2023.2/data/system_verilog/uvm_1.2/xlnx_uvm_package.sv(18648) @ 0: reporter [NO_DPI_TSTNAME] UVM_NO_DPI defined--getting UVM_TESTNAME directly, without DPI
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO /tools/Xilinx/Vivado/2023.2/data/system_verilog/uvm_1.2/xlnx_uvm_package.sv(20867) @ 0: reporter [UVM/COMP/NAMECHECK] This implementation of the component name checks requires DPI to be enabled
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/base_seq.sv(10) @ 5000: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/driver.sv(22) @ 5000: uvm_test_top.env_o.agt.drv [driver] ip1 = 34, ip2 = 44
----------------------------------------------------------------------------------------------------------
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/scoreboard.sv(29) @ 10000: uvm_test_top.env_o.sb [scoreboard] Matched: ip1 = 34, ip2 = 44, out = 78
----------------------------------------------------------------------------------------------------------
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/base_seq.sv(10) @ 10000: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/driver.sv(22) @ 10000: uvm_test_top.env_o.agt.drv [driver] ip1 = 31, ip2 = 31
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/base_seq.sv(10) @ 15000: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/driver.sv(22) @ 15000: uvm_test_top.env_o.agt.drv [driver] ip1 = 50, ip2 = 59
----------------------------------------------------------------------------------------------------------
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/scoreboard.sv(29) @ 18000: uvm_test_top.env_o.sb [scoreboard] Matched: ip1 = 31, ip2 = 31, out = 62
----------------------------------------------------------------------------------------------------------
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/base_seq.sv(10) @ 20000: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/driver.sv(22) @ 20000: uvm_test_top.env_o.agt.drv [driver] ip1 = 9, ip2 = 14
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/base_seq.sv(10) @ 25000: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/driver.sv(22) @ 25000: uvm_test_top.env_o.agt.drv [driver] ip1 = 44, ip2 = 87
----------------------------------------------------------------------------------------------------------
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/scoreboard.sv(29) @ 26000: uvm_test_top.env_o.sb [scoreboard] Matched: ip1 = 9, ip2 = 14, out = 23
----------------------------------------------------------------------------------------------------------
...
----------------------------------------------------------------------------------------------------------
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/scoreboard.sv(29) @ 50000: uvm_test_top.env_o.sb [scoreboard] Matched: ip1 = 9, ip2 = 74, out = 83
----------------------------------------------------------------------------------------------------------
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/base_seq.sv(10) @ 50000: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/driver.sv(22) @ 50000: uvm_test_top.env_o.agt.drv [driver] ip1 = 53, ip2 = 9
UVM_INFO /home/kimura/work/sv/uvm_adder/verify/base_test.sv(24) @ 50000: uvm_test_top [base_test] End of testcase
UVM_INFO /tools/Xilinx/Vivado/2023.2/data/system_verilog/uvm_1.2/xlnx_uvm_package.sv(19968) @ 50000: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /tools/Xilinx/Vivado/2023.2/data/system_verilog/uvm_1.2/xlnx_uvm_package.sv(13673) @ 50000: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---

** Report counts by severity
UVM_INFO :   32
UVM_WARNING :    0
UVM_ERROR :    0
UVM_FATAL :    0
** Report counts by id
[NO_DPI_TSTNAME]     1
[RNTST]     1
[TEST_DONE]     1
[UVM/COMP/NAMECHECK]     1
[UVM/RELNOTES]     1
[base_seq]    10
[base_test]     1
[driver]    10
[scoreboard]     6

$finish called at time : 50 ns : File "/tools/Xilinx/Vivado/2023.2/data/system_verilog/uvm_1.2/xlnx_uvm_package.sv" Line 18699
exit
INFO: [Common 17-206] Exiting xsim at Thu Mar 14 09:01:44 2024...

これ、良く分からんけど6個しかテスト動いて無くない?でも、base_seqdriverが10回動いているから大丈夫なのかしら。 これから、このデザインを読み解いていこうと思う。