現在の自作CPUは、結構面積が大きくて通常のFPGAに乗り切らない。どうにか乗り切れるように、面積削減を検討している。 LSUの中で圧倒的に面積が巨大なのがSTQだ。STQが巨大な要因はいくつかある。
- LDQと異なり、各エントリはアドレスとデータの両方を管理する必要がある。
- ロード命令のフォワーディングをサポートする必要がある。この時、バイト単位での細かいフォワーディングをサポートすると、論理が増大する。
- ストアバッファへデータを移動する論理が必要である。データ移動時に、同じキャッシュラインへのマージ機能をサポートすると、論理が増大する。
それぞれについて対策を考える。
- アドレスの管理はどうしてもCAMになる必要がある。データの管理は、Distributed RAMに移動したい。
- フォワーディングを簡易化し、特定の条件でなければフォワーディングをサポートしない。複数エントリにヒットしてバイト単位でマージする必要がある場合などは、フォワーディングをキャンセルする。
- これにより、常にフォワーディングに必要なデータエントリを1つに限定し、Distributed RAMを使いたい。
- ストアバッファへのデータの移動時のマージ量を減らす。
次に、2について考える。フォワーディングをサポートするのをSTQのエントリ1つに限定する。 これにより、バイト・ストアx4 → ワード・ロードx1などのフォワーディングがサポートできなくなるが、そういうことはあまり起こらないと仮定して無視する代わりに、論理を簡単化できる。 また、フォワーディングに必要なデータの管理をDistributed RAMに移すことで、FPGAの資源を削減できる。
基本的な考え方としては、
sb x1, 0(x10) sb x2, 1(x10) sb x3, 2(x10) sb x4, 3(x10) lw x5, 0(x10)
このようなシーケンスにおいて、フォワーディングによって検知されるのはLW
にとって最も近いストア命令、sb x4, 3(x10)
のみである。
そうすると、それより前の3つのストア命令はすべてフォワーディングの対象とならない。
そうすると、lwの対象バイト・ストローブが0000_1111
であるのに対して、sb
がフォワードできるバイト・ストローブが0000_1000
なので完全なフォワーディングを実現できず、LW
命令としてはフォワーディングは行われない。
その代わりに、フォワード不完全ということでMSHRにリクエストが入り、L1Dにキャッシュ・ロードが行われる。
しかし問題はそのあとだ。上記のシーケンスで、L1Dキャッシュへのヒットが発生した場合、x1,x2,x3
のストアを無視された状態ですべてのデータがそろってしまう(0000_1000
はsb x4
から、0000_0111
はキャッシュから)ため、誤ったデータをロードする結果になってしまう。
これを防ぐためには、そもそもSTQに複数のエントリがヒットしたことを検知する必要があり、複数STQにフォワード対象がヒットした場合は、命令のハザードを発生しすべての命令がL1Dキャッシュに書き込まれるのを待つ必要がある。 このように、実装としては少し工夫が必要だが、全体としての論理が削減されるかどうか、観察していこうと思う。