前のブログの記事では、LVTを用いたSingle Port RAMをMulti Portに複製するための方法について調査した。
LVT(Live Value Table)を用いた手法では、各RAMインデックスについて、どのバンクRAMに最新の値が書き込まれているかを示すLVTエントリを用意する。 この手法は強力で便利なのだが、ポートの数が増えれば増えるほど、LVTの必要なビット幅は増加していく。
例えば、かなり高性能寄りCPUでは読み込みポートが12、書き込みポートが10とかいうとんでもない量のポートが必要になる。 また、物理レジスタの数を考慮すると256エントリなどのRAMを管理する必要があり、これを純粋にLVTで管理するとなると、必要となるLVTのFFの数は以下のように計算できる。
256 * $clog2(10) = 1024 FF
それでもって、このLVTをアップデートするための論理も結構きついものになってくる。単純に実装すると、10ポートのFFとなり、周辺の論理回路もかなり大きなものになるだろう。
always_ff @ (posedge i_clk, negedge i_reset_n) begin if (!i_reset_n) begin for (int i = 0; i < WORDS; i++) r_lvt[i] <= 'h0; end else begin for (int i = 0; i < WR_PORTS; i++) begin if (i_wr[i]) begin r_lvt[i_wr_addr[i]] <= i; end end end end // always_ff @ (posedge i_clk, negedge i_reset_n)
そこで、LVTすら排除してしまう手法であるXOR Multi Port RAMについて調査する。この手法はLVTを除去することができるが、さらに余分にRAMが必要なる。
基本的な考え方としては、例えば書き込みポートが2であるとして、それぞれRAM1とRAM2を使用しているとする。あるインデックスA
にそれぞれVALUE1
, VALUE2
が書き込まれているとする。
ライトポート0に対してNEW
が書き込まれたとする。この時、RAM1に対して以下のようにRAM2との値を使用して変更した値を書き込む。
RAM1 New Value = NEW ^ VALUE2
そして、読み出すときはRAM1の値(NEW ^ VALUE2
)とRAM1の値(VALUE2
)をXORして読み出す。すると、NEW ^ VALUE2 ^ VALUE2 = NEW
となり、NEW
の値を読み出すことができる。
ただし、これでは問題がある。読み込みと書き込みが同時に発生したとき、書き込みにも既存の値を読み出すために読み込みポートが必要になる。単純に考えればこのためにポートを1つ増やす必要があるが、これを回避する方法がある。それ専用のRAMを新たに追加すればよいのだ。 これは自分以外の書き込んだデータを記憶しておくためのRAMなので、各書き込みポートあたり、書き込みポート数-1個が必要となる。
結果的に、全体的に必要なRAMの数は、
書き込みに必要なRAMの数 = 書き込みポート数 * (書き込みポート数-1) 読み出しに必要なRAMの数 = 書き込みポート数 * 読み出しポート数 全体で必要なRAMの数 = 書き込みに必要なRAMの数 + 読み出しに必要なRAMの数 = 書き込みポート数 * (書き込みポート数-1+読み出しポート数)
となる。上記の読み出しポート=12,書き込みポート数=10の場合でLVTと必要なRAMの数を比較すると、
- LVTの場合 = 10 * 12= 120
- XORの場合 = 10 * (10-1+12) =10 * 21 = 210
と、大幅に増加してしまう。しかしまあ、LVTというクリティカルパスを完全に削除することができるため、使いどころによっては有用であろう。