FPGA開発日記

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

RISC-V向けLinuxをビルドするための確認 (2. Dockerで環境構築)

RISC-V向けLinuxのビルドについて少し確認したいと思っていろいろと調査している。結局WSLの上ではビルドが難しそうなので、WSL2にDockerをインストールしてこの上で動かすことにした。

Dockerでfreedom-u-sdkを構築するためにDockerfileを書いている。

FROM ubuntu:20.04

ENV RISCV=/opt/riscv
ENV PATH=$RISCV/bin:$PATH
ENV MAKEFLAGS=-j4

WORKDIR $RISCV

RUN apt update && \
    apt install -y tzdata && \
    apt install -y git autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev

RUN git clone --single-branch https://github.com/riscv/riscv-gnu-toolchain.git && \
cd riscv-gnu-toolchain && \
git submodule update --init --recursive

RUN cd riscv-gnu-toolchain && mkdir build && \
cd build && \
../configure --prefix=${RISCV} --enable-multilib && \
make linux

WORKDIR /root

RUN git clone https://github.com/sifive/freedom-u-sdk.git -b v1_0 --recurse-submodules
RUN cd freedom-u-sdk && \
make

ハマったのはriscv-gnu-toolchainのビルドにおいてmake linuxではなくmakeとしてしまい、riscv64-unknown-elfが作られてしまいLinuxのビルド時にこけてしまったこと。 これで再度ビルドをやり直した。すると以下のエラーだ。

#12 14.23 make[2]: Leaving directory '/root/freedom-u-sdk/buildroot'
#12 14.31 You must install 'wget' on your build machine
#12 14.32 You must install 'cpio' on your build machine
#12 14.32 You must install 'python' on your build machine
#12 14.32 You must install 'unzip' on your build machine
#12 14.32 You must install 'rsync' on your build machine
#12 14.32 make[1]: *** [support/dependencies/dependencies.mk:24: core-dependencies] Error 1
#12 14.33 make[1]: Leaving directory '/root/freedom-u-sdk/buildroot'
#12 14.33 make: *** [Makefile:90: /root/freedom-u-sdk/work/buildroot_initramfs/images/rootfs.tar] Error 2

またやり直しだ...

Rocket-ChipのPMA検出回路を自作CPUに実装する

前回、Rocket-ChipがどのようにPMAをていぎしているのか分かったので、この構造をそのまま自作CPUに取り込みたいと思う。

前回作成した表をもとに、JSON形式でPMAの定義を作成し、これを自動的にSystemVerilog形式に落とし込むスクリプトを作った。 元となるJSONファイルはこんな感じで定義した。

[
        {
        "base": "0x0000_0000",
        "size": "0x0000_1000",
        "attr": {"R": 1, "W": 1, "X": 1, "A": 1, "C": 0},
        "comment": "Debug Controller"
    },
    {
                "base": "0x0000_3000",
        "size": "0x0000_1000",
        "attr": {"R": 1, "W": 1, "X": 1, "A": 1, "C": 0},
        "comment": "error device"
    },
    {
                "base": "0x0000_4000",
        "size": "0x0000_1000",
        "attr": {"R": 1, "W": 1, "A": 1, "X": 0, "C": 0},
        "comment":"boot ROM"
    },
    {
                "base": "0x0001_0000",
        "size": "0x0001_0000",
...

これをスクリプトで読み込ませると、こんな感じのSystemVerilogファイルを吐き出すようにした。

module pma_map (
  input logic [riscv_pkg::PADDR_W-1: 0] i_pa
  output logic o_map_hit,
  output map_attr_t o_mapt_attr_hit
);


localparam MAP_TABLE_SIZE = 11;
logic [MAP_TABLE_SIZE-1: 0] w_hit_addr;
map_attr_t w_map_attr[MAP_TABLE_SIZE];
assign w_hit_addr[ 0] = (i_pa & 'hfffffffffff000) = 'h00000000000000;  // Address Region : 0 - fff
assign w_map_attr[ 0].r = 1'b1;
assign w_map_attr[ 0].w = 1'b1;
assign w_map_attr[ 0].x = 1'b1;
assign w_map_attr[ 0].a = 1'b1;
assign w_map_attr[ 0].c = 1'b0;
assign w_hit_addr[ 1] = (i_pa & 'hfffffffffff000) = 'h00000000002000;  // Address Region : 3000 - 3fff
assign w_map_attr[ 1].r = 1'b1;
assign w_map_attr[ 1].w = 1'b1;
assign w_map_attr[ 1].x = 1'b1;
assign w_map_attr[ 1].a = 1'b1;
/* ... 途中省略 ... */
assign w_map_attr[ 9].x = 1'b0;
assign w_map_attr[ 9].a = 1'b1;
assign w_map_attr[ 9].c = 1'b0;
assign w_hit_addr[10] = (i_pa & 'hfffffff0000000) = 'h00000080000000;  // Address Region : 80000000 - 8fffffff
assign w_map_attr[10].r = 1'b1;
assign w_map_attr[10].w = 1'b1;
assign w_map_attr[10].x = 1'b1;
assign w_map_attr[10].a = 1'b1;
assign w_map_attr[10].c = 1'b1;


assign o_map_hit = |w_hit_addr;
always_comb begin
case (w_hit_addr)
  11'b00000000001 : o_map_attr = w_map_attr[0];
  11'b00000000010 : o_map_attr = w_map_attr[1];
  11'b00000000100 : o_map_attr = w_map_attr[2];
  11'b00000001000 : o_map_attr = w_map_attr[3];
  11'b00000010000 : o_map_attr = w_map_attr[4];
  11'b00000100000 : o_map_attr = w_map_attr[5];
  11'b00001000000 : o_map_attr = w_map_attr[6];
  11'b00010000000 : o_map_attr = w_map_attr[7];
  11'b00100000000 : o_map_attr = w_map_attr[8];
  11'b01000000000 : o_map_attr = w_map_attr[9];
  11'b10000000000 : o_map_attr = w_map_attr[10];
  default   : o_map_attr = 'h0;
endcase
end

endmodule

これを今度はTLBに繋げて、Memory Access例外を通知するように実装してみる。

ChipyardのRocket-ChipにおけるPMA構成調査

自作CPUにPMA (Physical Memory Attributes) を実装するために、まずは既存のRISC-V実装がどのようなメモリマップ構成になっているのかを調査した。 対象としたのはChipyard上で構成できるRocket-Chipだ。Rocket-ChipのVerilogファイルを生成すると同時にメモリマップの情報を持つJSONファイルも生成されるので、これを参考に表を作ってみた。

JSONファイルはめちゃめちゃ崩れているのでFormatterで整理する。

  • chipyard.TestHarness.RocketConfig.json
{"#address-cells":[1],"#size-cells":[1],"aliases":{"serial0":["&/soc/serial@54000000"]},"compatible":["freechips,rocketchip-unknown-dev"],"cpus":{"#address-cells":[1],"#size-cells":[0],"cpu@0":{"clock-frequency":[0],"compatible":["sifive,rocket0","riscv"],"d-cache-block-size":[64],"d-cache-sets":[64],"d-cache-size":[16384],"d-tlb-sets":[1],"d-tlb-size":[32],"device_type":["cpu"],"hardware-exec-breakpoint-count":[1],"i-cache-block-size":[64],"i-cache-sets":[64],"i-cache-size":[16384],"i-tlb-sets":[1],"i-tlb-size":[32],"interrupt-controller":{"#interrupt-cells":[1],"compatible":["riscv,cpu-intc"],"interrupt-controller":[]},"mmu-type":["riscv,sv39"],"next-level-cache":["&/soc/cache-controller@2010000"],"reg":[{"base":0,"size":1,"r":false,"w":false,"x":false,"c":false,"a":false}],"riscv,isa":["rv64imafdc"],"riscv,pmpgranularity":[4],"riscv,pmpregions":[8],"status":["okay"],"timebase-frequency":[1000000],"tlb-split":[]}},"htif":{"compatible":["ucb,htif0"]},"memory@80000000":{"device_type":["memory"],"reg":[{"base":2147483648,"size":268435456,"r":true,"w":true,"x":true,"c":true,"a":true}]},"model":["freechips,rocketchip-unknown"],"soc":{"#address-cells":[1],"#size-cells":[1],"boot-address-reg@4000":{"reg":[{"base":16384,"size":4096,"r":true,"w":true,"x":false,"c"

Formatterで処理した後でメモリマップを表にまとめてみる。

ベースアドレス サイズ アクセス権限 説明
0x0000_0000 0x0000_1000 RWXA デバッグコントローラ
0x0000_3000 0x0000_1000 RWXA エラーデバイス
0x0000_4000 0x0000_1000 RWA ブートROM
0x0001_0000 0x0001_0000 RX ROM
0x0002_0000 0x0001_0000 RXC LBWIF ROM
0x0200_0000 0x0001_0000 RWA CLINT
0x0201_0000 0x0000_1000 RWA キャッシュコントローラ
0x0c00_0000 0x0400_0000 RWA 割り込みコントローラ
0x1000_0000 0x0000_1000 RWXCA LBWIF RAM
0x5400_0000 0x0000_1000 RWA シリアルデバイス
0x8000_0000 0x1000_0000 RWXCA メインメモリ

メモリ属性については、

  • R: Read
  • W: Write
  • A: Atomic Access
  • X: Execute
  • C: Cacheable

この中で分からないのはLBWIFというデバイスだ。検索してみるとLBWIFというのはLow Bandwidth Interfaceのことらしい。 Chipyardのドキュメントを見るとSerDesなどに使用するということらしいので、とりあえず実装に当たっては無視していいだろう。

RISC-V向けLinuxをビルドするための確認

RISC-V向けLinuxのビルドについて少し確認したいと思っていろいろと調査している。昔ビルドに使用していたのはfreedom-u-sdkのツールセットなのだが、今ははどうなっているのか。

github.com

それ以外の方法としても以下の方法が用意されていた。QEMUを使うのがデファクトスタンダードになっているようだ。

risc-v-getting-started-guide.readthedocs.io

とりあえず過去の方法としてfreedom-u-sdkを再ビルドしている。確認したいのは、Linuxのビルドにあたってどの命令が最低限必要なのか、ということ。 自作CPUの実装としてアトミック命令の実装が必要かなど、実装の優先度を確認するために使用したい。

$ git clone https://github.com/sifive/freedom-u-sdk.git
$ cd freedom-u-sdk
$ make -j$(nproc)

RISC-VのGPGPU実装であるVortexのビルドメモ

以下のリポジトリでVortexのソースコードをCloneできる。

github.com

  • Specifications
    • Support RISC-V RV32IMF ISA
    • Scalability: 1 to 32 cores with optional L2 and L3 caches
    • Software: OpenCL 1.2 Support
    • Supported FPGAs:

とりあえずREADMEを読みながらビルドしてみた。

git clone --recursive https://github.com/vortexgpgpu/vortex.git
cd vortex
make
      |          ^
%Warning-EOFNEWLINE: ../dpi/util_dpi.vh:10:7: Missing newline at end of file (POSIX 3.206).                                            : ... Suggest add newline.
   10 | `endif
      |       ^
%Warning-EOFNEWLINE: ../rtl/libs/VX_index_buffer.v:90:10: Missing newline at end of file (POSIX 3.206).                                                        : ... Suggest add newline.
   90 | endmodule
      |          ^
%Warning-EOFNEWLINE: ../rtl//VX_csr_data.v:217:10: Missing newline at end of file (POSIX 3.206).                                                 : ... Suggest add newline.
  217 | endmodule
      |          ^
%Warning-EOFNEWLINE: ../rtl/fp_cores/VX_fpu_fpnew.v:209:12: Missing newline at end of file (POSIX 3.206).                                                          : ... Suggest add newline.
  209 | `TRACING_ON
      |            ^
%Warning-EOFNEWLINE: ../rtl/libs/VX_serial_div.v:100:10: Missing newline at end of file (POSIX 3.206).                                                       : ... Suggest add newline.
  100 | endmodule
      |          ^
%Warning-UNUSED: ../rtl//VX_lsu_unit.v:26:16: Parameter is not used: 'REQ_ADDRW'
                                            : ... In instance Vortex.genblk2[0].cluster.genblk2[0].core.pipeline.execute.lsu_unit
   26 |     localparam REQ_ADDRW  = 32 - REQ_ASHIFT;
      |                ^~~~~~~~~
                 ../rtl//VX_execute.v:65:1: ... note: In file included from VX_execute.v
                 ../rtl//VX_pipeline.v:194:1: ... note: In file included from VX_pipeline.v
                 ../rtl//VX_core.v:86:1: ... note: In file included from VX_core.v
                 ../rtl//VX_cluster.v:55:1: ... note: In file included from VX_cluster.v
                 ../rtl//Vortex.v:53:1: ... note: In file included from Vortex.v
%Warning-UNUSED: ../rtl/libs/VX_stream_arbiter.v:23:16: Parameter is not used: 'LOG_NUM_REQS'
                                                      : ... In instance Vortex.genblk2[0].cluster.genblk2[0].core.pipeline.commit.writeback.rsp_arb
   23 |     localparam LOG_NUM_REQS = $clog2(NUM_REQS);
      |                ^~~~~~~~~~~~
                 ../rtl/cache/VX_cache.v:537:1: ... note: In file included from VX_cache.v
                 ../rtl//Vortex.v:102:1: ... note: In file included from Vortex.v
%Error: Exiting due to 87 warning(s)

うーん、大量にエラーが出てきてしまった。Verilatorのバージョンがあっていないのか?

マイクロアーキテクチャについて

以下のドキュメントを読んでみる。

github.com

Vortexマイクロアーキテクチャ

Vortex GPU実行モデル

VortexはSIMT (Single Instruction, Multiple Threads) の実行モデルを使用しており、単一のWarpがサイクル毎に発行される。

  • スレッド
    • 計算の最小単位
    • 各スレッドレジスタファイルを持っている(32個の整数、32個のFPレジスタ)
    • スレッドは並列に実行される
  • Wrap
    • スレッドの論理的なクラスタ
    • Warp内の各スレッドhh同じ命令を実行する
      • PCは共有され、ライトバックのためにスレッドのマスクが管理されている
    • Warpは長いステップで時分割されて実行される
      • 例: Warp0はサイクル0で実行され、Warp1はサイクル1で実行される

Vortex RISC-V命令拡張

  • スレッドマスク制御
    • 実行中にアクティブなWarpの数を制御する
    • TMC count: count数のスレッド数をアクティベートする
  • Warpスケジューリング
    • 実行中に起動するWarpの数を制御する
    • WSPAWN count, addr: count分だけWarpを起動し、addrの位置にジャンプする。
  • コントロールフローの分岐
    • 分岐したときに起動するスレッドを制御
    • SPLIT predicate: 'taken' 述語のスレッドマスクを適用し、 'not-taken' を IPDOM スタックに保存する。
    • JOIN: 'not-taken' スレッドマスクの復元
  • ワープの同期
    • BAR ID, count: バリアIDに入るワープをカウント数に達するまでストールする

Vortexパイプライン/データパス

f:id:msyksphinz:20210914000008p:plain
  • フェッチ
    • Warpスケジューラ
      • ストールしたWarpとアクティブなWarpの追跡、分岐とバリアの解決、分割/結合IPDOMスタックの維持
    • 命令キャッシュ
      • キャッシュからの命令の取得、I-cacheリクエスト/レスポンスの発行
  • デコード
    • フェッチされた命令をデコードし、以下の命令がデコードされたらWarpスケジューラに通知する
      • 分岐、tmc、split/join、wspawn
    • used_regs mask の事前計算 (Issueステージに必要)
  • 発行
    • スケジューリング
      • インオーダ発行(オペランド/実行ユニットの準備)、アウトオブオーダコミット
    • IBuffer
      • フェッチされた命令を格納し、Warpごとにキューを分け、ラウンドロビン・スケジューリングによって次のWarpを選択する
    • スコアボード
    • GPR(General-Purpose Registers)ステージ
  • 実行ユニット
    • ALUユニット
      • シングルサイクル演算(+,-,>>,<<,&,|,^)、分岐命令(ALUリソースの共有
    • MULDIVユニット
      • 乗算器 - 2サイクルで実行
      • 除算器 - 除算と余剰、32サイクルで実行
    • FPUユニット
      • マルチサイクル演算、ASICではFPnewライブラリを使用、FPGAではハードDSPを使用
    • CSRユニット
      • バイス・キャップ、FPUステータス・フラグ、パフォーマンス・カウンタなどの恒常的なステータス・レジスタの格納
      • 外部からのCSRリクエスト(ホストCPUからのリクエスト)に対応
    • LSUユニット
      • ロード/ストア操作の処理、D-キャッシュ要求の発行、D-キャッシュ応答の処理
      • ロードレスポンスのコミット - ストレージの保存、スコアボードによる完了の追跡
    • GPGPUユニット
      • GPGPU命令の処理
        • tmc、wspawn、split、bar
      • JOINはWarp Schedulerが処理します(SPLIT応答時)。
  • コミット
    • コミット
      • CSRフラグの更新、パフォーマンスカウンタの更新
    • ライトバック
      • 結果のGPRへの書き戻し、スコアボードへの通知(使用中レジスタの解放)、候補命令の選択(ALUユニットが最優先
  • クラスタリング
    • 複数のコアをクラスタにまとめる(L2キャッシュを共有することも可能)
    • 複数のクラスタをグループ化(L3キャッシュの共有も可能)
    • ビルド時に設定可能
    • デフォルトの設定。
  • FPGA AFUインターフェース
    • CPU-GPU間通信の管理
      • バイスキャップの照会、カーネル命令とリソースバッファの読み込み、カーネル実行の開始、デスティネーションバッファの読み込み
    • ローカルメモリ - GPUによるローカルDRAMへのアクセス
    • 予約済みI/Oアドレス - ホストCPUへのリダイレクト、コンソール出力

Rocket-ChipのTLBがメモリの属性を取得する仕組み (Rocket-ChipでPMAのエラー発生源を追う)

Rocket-ChipのTLBの解析の続き。Rocket-ChipのPMAはTileLinkによって接続された各デバイスのアドレス情報を引っ張ってきてテーブルを作り、許可されるアドレス領域かどうかを判断していることが分かった。

具体的なテストケースを使って確認しよう。rv64mi-p-accessテストパタンをRocket-Chip(Chipyard)上で実行して確認してみよう。

./simulator-chipyard-RocketConfig-debug +verbose -v rv64mi-p-accses.vcd /home/msyksphinz/work/riscv/chipyard/riscv-tools-install/riscv64-unknown-elf/share/riscv-tests/isa/rv64mi-p-access 2>&1 | spike-dasm | tee rv64mi-p-accses.log

この時にテストケースは非常に大きなアドレスにジャンプしようとしてTLBエラーが発生している。 このエラーが発生する仕組みを追いかける。

 C0:        424 [1] pc=[0000000080000194] W[r 5=ffffffffffffffff][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[fff0029b] addiw   t0, zero, -1
 C0:        425 [1] pc=[0000000080000198] W[r 5=8000000000000000][1] R[r 5=ffffffffffffffff] R[r 0=0000000000000000] inst=[03f29293] slli    t0, t0, 63
 C0:        426 [1] pc=[000000008000019c] W[r 5=80000000800001dc][1] R[r 5=8000000000000000] R[r 7=00000000800001dc] inst=[0072c2b3] xor     t0, t0, t2
 C0:        427 [1] pc=[00000000800001a0] W[r 3=0000000000000002][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00200193] li      gp, 2
 C0:        428 [1] pc=[00000000800001a4] W[r 6=0000000000000001][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00100313] li      t1, 1
 C0:        429 [1] pc=[00000000800001a8] W[r 9=00000000800001a8][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00000497] auipc   s1, 0x0
 C0:        430 [1] pc=[00000000800001ac] W[r 9=00000000800001b8][1] R[r 9=00000000800001a8] R[r 0=0000000000000000] inst=[01048493] addi    s1, s1, 16
 C0:        431 [1] pc=[00000000800001b0] W[r 7=0000000000000000][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00000393] li      t2, 0
 C0:        432 [1] pc=[00000000800001b4] W[r 7=00000000800001b8][1] R[r 5=80000000800001dc] R[r 0=0000000000000000] inst=[000283e7] jalr    t2, t0, 0
// ここでアドレス0x80000000800001dcにジャンプする。非常に大きなアドレスでアクセス許可範囲外
 C0:        436 [0] pc=[ffffff80800001dc] W[r 8=0000000000000000][0] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00000000] c.addi4spn s0, sp, 0
 C0:        441 [1] pc=[0000000080000004] W[r30=0000000000000001][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[34202f73] csrr    t5, mcause
 C0:        442 [1] pc=[0000000080000008] W[r31=0000000000000008][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00800f93] li      t6, 8
 C0:        444 [1] pc=[000000008000000c] W[r 0=0000000000000000][0] R[r30=0000000000000001] R[r31=0000000000000008] inst=[03ff0a63] beq     t5, t6, pc + 52

波形を確認すると、この時にTLBのinst.ae信号が落ちている。これにより命令フェッチアクセスエラーが発生していることが明らかになった。

f:id:msyksphinz:20210913002015p:plain

このinst.ae信号はテーブルのhit信号と、さらにpx_array信号のAND演算によって成り立っている。

   io.resp.ae.inst := (~px_array & hits).orR

px_array信号は、テーブル12を参照している時にはprot_xを参照する。このprot_x信号は以下のような感じでfastCheck()という関数で作成されている。まあこれがTileLinkのデバイスチェックに繋がっている。

   val pr_array = Cat(Fill(nPhysicalEntries, prot_r), normal_entries.map(_.pr).asUInt) & ~ptw_ae_array
   val pw_array = Cat(Fill(nPhysicalEntries, prot_w), normal_entries.map(_.pw).asUInt) & ~ptw_ae_array
   val px_array = Cat(Fill(nPhysicalEntries, prot_x), normal_entries.map(_.px).asUInt) & ~ptw_ae_array
   def fastCheck(member: TLManagerParameters => Boolean) =
     legal_address && edge.manager.fastProperty(mpu_physaddr, member, (b:Boolean) => Bool(b))

   val prot_x = fastCheck(_.executable) && !deny_access_to_debug && pmp.io.x

legal_addressというのは、各デバイスが当該アクセスを許可できるかどうかを探索していることを意味する。mpu_phyaddrが当該物理アドレス

   val legal_address = edge.manager.findSafe(mpu_physaddr).reduce(_||_)

具体的に波形を観察すると、これまではlegal_address_T_49(おそらくメモリ領域だと思う)までのアクセスが許可されていたが、物理アドレス0x8080001DCとなりその領域を外れ、どのデバイスのアドレス領域でも無くなってしまった。これによりlegal_addressがすべて0となり、命令フェッチのアクセスエラーへとつながるようだ。

f:id:msyksphinz:20210913002549p:plain

CPU内のキャッシュラインを置き換えるためのフローについて考える2.

キャッシュラインを置き換える話その2。

キャッシュラインを置き換える際に、掃き出し用のデータを持っておく必要があるのだが、いくつかのケースでバグに嵌ってしまい再考を余儀なくされている。 ケースとしては、

  1. L1Dに書き込みをするためにMissしたライン(物理アドレスAとする)を取りに行く。その際に置き換え対象のライン(物理アドレスBとする)を吐き出す
  2. しかしさらに同じラインに対して書き込みが発生し同様にMissする(そのアドレスを物理アドレスCとする)と、同様にラインを取りに行くのだがその時に再度物理アドレスBのラインを吐き出すようになってしまう。

このときの問題は2つあって、

これを回避するためにいろいろ考えている。以下はメモ。


L1Dのデータ交換はタイミング的に難しいところであるため慎重に設計する必要があります。

ここでは新たに外部から要求する物理アドレスをA、置き換え対象となるL1Dにすでに配置されている物理アドレスをBとします。

パイプライン側からのロードリクエスト L1Dへの物理アドレスAの要求に従い置き換え対象となる物理アドレスB、データ、当該Way番号を取得します。 このWay番号がLRQを経由して新たに書き込まれるWayになります。 ストアバッファからのロードリクエスト ストアバッファはコミット済みのデータを受け取ると以下のように動作します。

  • サイクル1. L1Dに対して当該アドレスのリードリクエストを送出する
  • サイクル2. L1Dからヒット・ミス情報を取得する。ミスの場合には置き換え対象として掃き出し用の物理アドレス・データ・置き換え対象Wayを取得する
  • サイクル3. LRQにデータを渡す。この時にLRQ内のエントリとして同じ掃き出しアドレスを持っている場合、
    • 2回同じL1Dラインのデータが吐き出される可能性がある
    • 当該キャッシュラインに新しいデータがまだ入っておらず、結果として掃き出し用のデータが古い可能性がある

このため、同じ掃き出し対象のデータをLRQ内でで見つけた場合は処理を取りやめ、LRQの解決を待って再度L1Dのデータ取得からやり直す

f:id:msyksphinz:20210912013047p:plain