56 / 63 · 概念
2 読み出し・1 書き込みレジスタファイルとバイパス
独立した二つの読み出しポートと同期書き込みを実装し、ポートごとの競合バイパスと初期化・valid 方針を検算する。
レッスンは無料で読めます。受講登録すると進捗を保存できます。
二つの値を同時に取り出す記憶回路
演算器で A+B を計算するには、二つのオペランドが同じサイクルに必要である。2-read/1-write レジスタレジスタ 複数ビットの状態を記憶する回路です。このコースの同期式レジスタは、クロックエッジで定められた入力を記憶します。 詳しく見るファイルは、二つの読み出しアドレスと一つの書き込みアドレスを提供する。ここでは 16 ビットのワードを 8 個格納し、二つの読み出し結果も立ち上がりエッジでレジスタに記憶する。アドレス幅は 3 ビット、記憶容量は 8×16=128 ビットである。アドレス 0 も通常の記憶領域であり、定数レジスタとしての特別な規則はない。
読み出しポートごとに enable と valid を持たせる。一方のポートが休んでいても他方は読み出せ、両方が同じアドレスを読んでもよい。異なる二つのアドレスを同時に読む機能には、単なる記憶容量の増加とは別のコストがかかる。小規模な回路ならフリップフロップフリップフロップ 指定されたクロックエッジで入力を記憶し、次のエッジまで保持する素子です。入力はセットアップ・ホールド条件を満たす必要があります。 詳しく見る配列と二つの選択回路で実装できる。特定の RAM を使う場合は、読み出しポート数を確保するために記憶領域を複製し、両方に同じ書き込みを送る構成も考えられる。どのリソースに合成されるかは、RTL の宣言だけでは確定しない。
書く値が、今読む値でもある場合
今回の契約では、同じエッジで同じアドレスを読み書きすると、新しい書き込みデータを応答として返す。記憶配列の更新を待たず、wr_data を読み出し出力の入力へ直接送るバイパスを設ける。ポート の競合条件を次のように定義する。
のとき、応答は次のとおりである。
はエッジ直前の記憶値である。 は各読み出しアドレスに対して独立に比較する。一方のポートが競合しただけで両方の出力へ書き込みデータを送ると、別のアドレスのオペランドまで変わってしまう。また、wr_en を条件から落とすと、実際の書き込みがないサイクルでも偶然の wr_addr 一致で誤ってバイパスする。
module regfile8x16_2r1w (
input logic clk, rst, wr_en,
input logic [2:0] wr_addr,
input logic [15:0] wr_data,
input logic rd_en0, rd_en1,
input logic [2:0] rd_addr0, rd_addr1,
output logic [15:0] rd_data0, rd_data1,
output logic rd_valid0, rd_valid1
);
timeunit 1ns;
timeprecision 1ps;
logic [15:0] mem [0:7];
always_ff @(posedge clk) begin
if (rst) begin
rd_valid0 <= 1'b0;
rd_valid1 <= 1'b0;
end else begin
rd_valid0 <= rd_en0;
rd_valid1 <= rd_en1;
if (wr_en)
mem[wr_addr] <= wr_data;
if (rd_en0)
rd_data0 <= (wr_en && wr_addr == rd_addr0)
? wr_data : mem[rd_addr0];
if (rd_en1)
rd_data1 <= (wr_en && wr_addr == rd_addr1)
? wr_data : mem[rd_addr1];
end
end
endmoduleこのバイパスは入力の選択を変えるが、出力のレジスタ段はなくさない。アドレスと要求をエッジ前に与え、応答をそのエッジ後に観測する。後段レジスタは次の立ち上がりエッジで応答を受け取れる。組み合わせ読み出しのレジスタファイルへ変更すると、アドレスの変化が出力へ伝わる経路と遅延の契約も変わるため、単なるコードの短縮として扱ってはならない。
例:片方のポートだけが競合する
以前に M[2]=12、M[5]=7 を書いてあるとする。E0 直前に二つの読み出し enable を有効にし、rd_addr0=2、rd_addr1=5、wr_en=1、wr_addr=2、wr_data=20 を与える。E0 後、ポート 0 はバイパスされた 20、ポート 1 は格納済みの 7 を応答する。この二つの有効な応答を足すと 27 になる。バイパスのない read-first 実装で得られる 19 と区別できる。
E1 では二つの読み出しアドレスをともに 2 とし、そのアドレスに 30 を書く。両出力は 30 になる。E2 では wr_en=0、wr_addr=2、wr_data=99 のままアドレス 2 を読むと、両出力は 30 でなければならない。この 1 サイクルで、アドレス一致だけでバイパスする誤りを検出できる。E3 で rd_en0=0、rd_en1=1 とすると、ポート 0 の valid は 0、ポート 1 の valid は 1 になる。
各列は一つの立ち上がりエッジ立上りエッジ クロックが 0 から 1 に変わる瞬間です。CLK=1 の区間全体を指すレベルとは区別します。 詳しく見るの観測記録である。入力はエッジ直前、名前に「更新後」が付く状態は更新直後の値を示す。列の整列はサンプル順を示し、物理的な伝搬遅延伝搬遅延 入力が変わってから、出力が正しい値に安定するまでの時間です。論理式の等価性と時間特性は別の性質です。 詳しく見るを描いたものではない。あらかじめ M[2]=12、M[5]=7 を格納し、二つの読み出し enable は常に 1 とする。書き込みアドレスは 2 である。最後の列は wr_en=0 なので、wr_data=99 をバイパスしてはならない。
波形データを表示
| 信号 | 波形 | バスの値 |
|---|---|---|
| エッジ | 234 | E0 → E1 → E2 |
| wr_en | 1.0 | |
| wr_data | 234 | 20 → 30 → 99 |
| rd_addr0 | 2.. | 2 |
| rd_addr1 | 23. | 5 → 2 |
| 更新後 out0 | 23. | 20 → 30 |
| 更新後 out1 | 23. | 7 → 30 |
何を初期化するのか
アクティブ High の同期 reset は書き込みを阻止し、二つの valid を 0 にする。配列と出力データは reset しない。そのため、reset 後に出力バスが以前の値を保持していても、valid=0 なら正常である。配列に格納済みの内容は、この RTL の reset では消えない。システムが reset 後の新しい処理だけを許可するなら、その処理の初期化順序を別途定める。
初めて書くアドレスを同じエッジで読む場合、今回の実装ではバイパスによって新しい値が定義される。しかし、書き込みなしで一度も初期化されていないアドレスを読む値は依然として未定義である。rd_valid は要求応答の時点を示すもので、アドレスごとの initialized ビットではない。検証器は通常の読み出しの期待値を作る前に、そのアドレスの値が既知かを確認し、同一エッジのバイパスを別途扱う必要がある。
検証項目には、二つの読み出し enable の 4 通り、アドレスの一致・不一致、書き込みと競合するポートがなし・0 番・1 番・両方の場合、連続書き込み、reset の優先順位がある。複数の書き込みポートはこの講義の範囲外であり、追加するなら、同じアドレスに二つの書き込みが重なるときの優先順位を先に決め直す必要がある。ポート数を変えると、機能の契約とリソースのコストがともに変わる。
自分で考えてみましょう
初期状態は M[1]=4、M[4]=9 である。二つの読み出し enable が 1、アドレスがそれぞれ 1 と 4 のとき、アドレス 4 に 13 を書くと出力の組はどうなるか。次のエッジで wr_en=0、wr_addr=1、wr_data=99、両方の読み出しアドレス=1 ならどうなるか。最後に、ポート 0 の enable だけを 0 にしたときの valid とデータ消費の規則を説明せよ。
解説を見る
最初の応答は (4,13) である。書き込みアドレスと競合するのはポート 1 だけなので、新しい値 13 をバイパスする。次の応答は (4,4) であり、wr_en=0 のため 99 は使わない。最後のサイクルは rd_valid0=0、rd_valid1=1 となる。ポート 0 のデータレジスタは以前の値を保持するが、有効な応答として消費しない。アドレス 0 も他のアドレスと同じように読み書きできる。