FPGA で AND 等の論理演算が若干遅延することを実際に観測してみました。
実験設定
使用した FPGA ボードは Tang Nano 4K です。
実験のために以下のような Verilog プログラムを作りました。raddr
と rx_v
が今回観測したい信号で、それぞれ条件演算子および AND 演算子が使われています。assign
は継続代入というもので、右辺の信号のいずれかが変化するたびに代入が行われます。
信号の定義
wire [7:0] rdata;
wire [2:0] raddr;
wire rx_v;
reg rx_en, rdata_v, rx_rdy, blank_rd;
assign raddr = rx_rdy ? 0 : 5; // 今回注目する信号(条件演算子)
assign rx_v = rdata_v & rx_rdy & ~blank_rd; // 今回注目する信号(AND 演算子)
// rx_en, rdata_v, rx_rdy, blank_rd はいずれも
// always 文でのノンブロッキング代入により変化する
always @(posedge sys_clk) begin
if (!rst_n)
rx_en <= 1'd0;
else
rx_en <= ~rx_en; // rx_en は clk を 2 分周した波形
end
always @(posedge sys_clk) begin
if (!rst_n)
rdata_v <= 1'd0;
else
rdata_v <= rx_en; //
end
always @(posedge sys_clk) begin
if (!rst_n)
rx_rdy <= 1'd0;
else if (rdata_v && raddr == 5 && rdata[0])
rx_rdy <= 1'd1;
else if (rdata_v && rx_v)
rx_rdy <= 1'd0;
end
always @(posedge sys_clk) begin
if (!rst_n)
blank_rd <= 1'd0;
else if (rdata_v && raddr == REG_LSR && rdata[0])
blank_rd <= 1'd1;
else if (rdata_v)
blank_rd <= 1'd0;
end
波形の観測は Gowin Analysis Oscilloscope で行いました。これは Gowin の FPGA に特有の測定機能で、FPGA の内蔵メモリに波形データを記録し、PC に転送して画面に表示してくれます。波形データは設定したクロックのエッジで記録できます。今回は sys_clk
および sys_clk
を PLL で 4 倍にしたクロックの立ち上がりエッジで波形を記録させました。
sys_clk
は 27MHz を PLL で 13/7 倍した 50.143MHz です。「sys_clk
を PLL で 4 倍にしたクロック」は 27MHz を 52/7 = 4×13/7 倍した 200.571MHz です。
実験結果
観測された波形を示します。
まず sys_clk
で記録した波形に注目します。見ての通り sys_clk
(一番上の信号)は常に 0 で記録されています。立ち上がりエッジの直前の信号レベルで記録されるということですね。
2 行目の信号 I_RX_EN
には rx_en
が接続されているので、I_RX_EN
と rx_en
は等価だと思っていただいて大丈夫です。同様に 3 行目、4 行目の信号 I_RADDR
、O_RDATA
にはそれぞれ 、raddr
、rdata
が接続されており、それぞれ等価と思ってください。
rx_en
は sys_clk
の立ち上がりエッジのタイミングで反転するので、sys_clk
をちょうど 2 分周した信号となります。
rdata
は、黄色い縦線のタイミングで 5→0 に変化しています。sys_clk
の立ち上がりエッジに合わせて、一瞬で 5→0 に変わったかのように観測されています。
遅延の考察
次に図の下半分(sys_clk
の 4 倍のクロックで信号を記録した場合)に注目します。4 倍のクロックでサンプリングしたため、sys_clk
の変化も捉えることができています。
先ほどと異なるのは、rx_en
とraddr
の変化タイミングの違いです。rx_en
は依然として sys_clk
の立ち上がりエッジと同じタイミングで変化しているものの、raddr
は少し遅れて変化しています。
sys_clk
の 4 倍のクロックでしか記録をしないので、実際の遅れ幅は確定できません。ただ、少なくとも sys_clk
の周期の 1/4(5ns 程度)以上は遅延していると判断できると思います。(sys_clk
の周期の 1/4 より短い遅延は観測できないと筆者は認識していますが、間違っていたらコメントください)
rx_v
について見てみます。上の図では後半に山が 1 つだけ見えますが、下の図では山が 2 つ観測されました。先ほどの大きな山の前に、小さな山が出現したのです。
rx_v
の右辺は rdata_v & rx_rdy & ~blank_rd;
ですから、rdata_v
=0 のときは rx_v
=0 になるはずです。しかし、この小さな山のタイミングでは rdata_v
=0 になっているにもかかわらず、rx_v
=1 となっています。単に「信号が遅れている」だけでなく、プログラム上起きないはずの不整合な状態になっていることが分かりました。
投稿者の人気記事
-
uchan
さんが
2021/12/13
に
編集
をしました。
(メッセージ: 初版)
ログインしてコメントを投稿する