部品:AXIストリームレジスタ シミュレーション [AXI]
axis_sliceをシミュレーションしてみます。
テストパターン発生に先のaxis_source,axis_sinkを使います。で、早速変更してしまってます。Xilinxのisim(無償版)を試してみたのですが、「input wire integer ratio」の構文が通らなかったので、ratioを内部変数にしてます。そういえば元々この構文変だなぁと思いながらもModelSim(Altera版)では通っちゃってたので、まぁいっかで放ってた物でした。
「実体名.ratio<=値」の形で変更します。
axis_source、ソース側
axis_sink、シンク側
axis_sliceテスト
[src] --ts1--> [ts1_to_ts2] --ts2--> [ts2_to_ts3] --ts3--> [snk]
と数珠つなぎになってます。ソース側で発生させるデータ列はカウント値、シンク側でも同じように発生させて、同じ順番になってなければエラーです。
※'X'や'Z'は見てないので、テストとしては不十分ですが…
最初はソース、シンクとも100%。途中でソースの帯域<シンクの帯域、ソースの帯域>シンクの帯域、と変化させています。
isim起動のバッチファイルとプロジェクトファイルの例。コマンドラインから「isim.bat」で実行です。
tb.prj、プロジェクト
結果。まずは全体。最後まで行ってるので途中データが化けるようなことは起きて無いようです。
切り替わりのあたり。波形を見てみると、validは上流→下流に、readyは下流→上流に伝搬してるのがわかります。思った通りの動作、のはず。
ちなみにこのモジュール、「深さ1の小さなFIFO+出力レジスタ」です。後でFIFOも作りますが、「深さ1」と「深さ2以上」に大きな壁があるので分けて記述しました。
色々使えます。何度か紹介してる「周波数を出すために経路を分ける」という使い方や、非同期のループを回避するのにも使います。例えばreadyとvalidが組み合わせ回路のモジュール同士をつなぐとこうなる可能性があります。
この場合、間にaxis_sliceを入れて解決します。毎回注意するのも面倒なので、非同期のモジュールはaxis_sliceでサンドイッチするようにしてます。逆に言うと気にすることなく組み合わせ回路で作れます。マルチプレクサなどは組み合わせ回路で記述してしまった方が楽だったりします。
「AXIにはデータだけじゃなくIDやLastとかあるよ?どうするの?」という話。
AXIは"ready"と"valid"と"それ以外の荷物"という関係です。なので、こう書けます。
注意点としてはレイテンシは増えることです。多くの場合周波数は上げられるので、スループットは上がります。どちら重視かはシステム次第です。
テストパターン発生に先のaxis_source,axis_sinkを使います。で、早速変更してしまってます。Xilinxのisim(無償版)を試してみたのですが、「input wire integer ratio」の構文が通らなかったので、ratioを内部変数にしてます。そういえば元々この構文変だなぁと思いながらもModelSim(Altera版)では通っちゃってたので、まぁいっかで放ってた物でした。
「実体名.ratio<=値」の形で変更します。
axis_source、ソース側
module axis_source ( input wire enable, // enable input wire c_clk, // Clock input wire c_rst, // Reset input wire m_ready, // Ready output wire m_valid // Valid ); integer ratio; // ratio reg [3:0] rand; reg inhib; reg valid; assign m_valid = valid & ~inhib; always @(posedge c_clk) begin if (c_rst) begin valid <= 1'b0; rand <= 0; inhib <= 1'b1; end else begin valid <= enable; if (inhib | m_ready) begin rand = $random; case (ratio) default : inhib <= 0; 1 : inhib <= &rand[3:0]; 2 : inhib <= &rand[2:0]; 3 : inhib <= &rand[1:0]; 4 : inhib <= rand[0]; 5 : inhib <= |rand[1:0]; 6 : inhib <= |rand[2:0]; 7 : inhib <= |rand[3:0]; endcase end end end endmodule
axis_sink、シンク側
module axis_sink ( input wire enable, // enable input wire c_clk, // Clock input wire c_rst, // Reset output wire s_ready, // Ready input wire s_valid // Valid ); integer ratio; // ratio reg [3:0] rand; reg inhib; reg ready; assign s_ready = ready & ~inhib; always @(posedge c_clk) begin if (c_rst) begin ready <= 1'b0; rand <= 0; inhib <= 1'b0; end else begin ready <= enable; rand = $random; case (ratio) default : inhib <= 0; 1 : inhib <= &rand[3:0]; 2 : inhib <= &rand[2:0]; 3 : inhib <= &rand[1:0]; 4 : inhib <= rand[0]; 5 : inhib <= |rand[1:0]; 6 : inhib <= |rand[2:0]; 7 : inhib <= |rand[3:0]; endcase end end endmodule
axis_sliceテスト
`timescale 1ns / 1ps `default_nettype none module tb; parameter stdw = 16; reg c_clk; reg c_rst; reg enable; initial c_clk <= 1'b0; always #5 c_clk <= ~c_clk; initial begin c_rst <= 1'b1; enable <= 1'b0; #100; @(posedge c_clk); c_rst <= 1'b0; #200; @(posedge c_clk); enable <= 1'b0; @(posedge c_clk); src.ratio <= 0; snk.ratio <= 0; @(posedge c_clk); enable <= 1'b1; #1000; @(posedge c_clk); enable <= 1'b0; @(posedge c_clk); src.ratio <= 5; snk.ratio <= 3; @(posedge c_clk); enable <= 1'b1; #2000; @(posedge c_clk); enable <= 1'b0; @(posedge c_clk); src.ratio <= 3; snk.ratio <= 5; @(posedge c_clk); enable <= 1'b1; #2000; $display("finish"); $stop; end wire ts1_ready; wire ts1_valid; wire [stdw-1:0] ts1_data ; wire ts2_ready; wire ts2_valid; wire [stdw-1:0] ts2_data ; wire ts3_ready; wire ts3_valid; wire [stdw-1:0] ts3_data ; /////////////////////////////// axis_source src ( .enable (enable ), .c_clk (c_clk ), .c_rst (c_rst ), .m_ready (ts1_ready ), .m_valid (ts1_valid ) ); reg [stdw-1:0] s_data; always @(posedge c_clk) begin if (c_rst) s_data <= 0; else if (ts1_ready & ts1_valid) s_data <= s_data + 1; end assign ts1_data = s_data; /////////////////////////////// axis_sink snk ( .enable (enable ), .c_clk (c_clk ), .c_rst (c_rst ), .s_ready (ts3_ready ), .s_valid (ts3_valid ) ); reg [stdw-1:0] d_data; always @(posedge c_clk) begin if (c_rst) d_data <= 0; else if (ts3_ready & ts3_valid) d_data <= d_data + 1; end always @(posedge c_clk) begin if (ts3_ready & ts3_valid) begin if (ts3_data != d_data) begin $display("error %x,%x", ts3_data, d_data); $stop; end end end /////////////////////////////// axis_slice #(stdw) ts1_to_ts2 ( c_clk, c_rst, // clk, rst ts1_ready, ts1_valid, ts1_data, // sink ts2_ready, ts2_valid, ts2_data // source ); axis_slice #(stdw) ts2_to_ts3 ( c_clk, c_rst, // clk, rst ts2_ready, ts2_valid, ts2_data, // sink ts3_ready, ts3_valid, ts3_data // source ); endmodule `default_nettype wire
[src] --ts1--> [ts1_to_ts2] --ts2--> [ts2_to_ts3] --ts3--> [snk]
と数珠つなぎになってます。ソース側で発生させるデータ列はカウント値、シンク側でも同じように発生させて、同じ順番になってなければエラーです。
※'X'や'Z'は見てないので、テストとしては不十分ですが…
最初はソース、シンクとも100%。途中でソースの帯域<シンクの帯域、ソースの帯域>シンクの帯域、と変化させています。
isim起動のバッチファイルとプロジェクトファイルの例。コマンドラインから「isim.bat」で実行です。
tb.prj、プロジェクト
verilog work ../axis_slice.v verilog work ../axis_txgen.v verilog work tb.visim.bat、実行バッチ
fuse work.tb -prj tb.prj -o tb.exe tb.exe -gui -wdb tb.wdb
結果。まずは全体。最後まで行ってるので途中データが化けるようなことは起きて無いようです。
切り替わりのあたり。波形を見てみると、validは上流→下流に、readyは下流→上流に伝搬してるのがわかります。思った通りの動作、のはず。
ちなみにこのモジュール、「深さ1の小さなFIFO+出力レジスタ」です。後でFIFOも作りますが、「深さ1」と「深さ2以上」に大きな壁があるので分けて記述しました。
色々使えます。何度か紹介してる「周波数を出すために経路を分ける」という使い方や、非同期のループを回避するのにも使います。例えばreadyとvalidが組み合わせ回路のモジュール同士をつなぐとこうなる可能性があります。
この場合、間にaxis_sliceを入れて解決します。毎回注意するのも面倒なので、非同期のモジュールはaxis_sliceでサンドイッチするようにしてます。逆に言うと気にすることなく組み合わせ回路で作れます。マルチプレクサなどは組み合わせ回路で記述してしまった方が楽だったりします。
「AXIにはデータだけじゃなくIDやLastとかあるよ?どうするの?」という話。
AXIは"ready"と"valid"と"それ以外の荷物"という関係です。なので、こう書けます。
axis_slice #(IDの幅+データの幅+…) sync ( c_clk, c_rst, // clk, rst s_ready, s_valid, {s_id, s_last, s_data}, // sink m_ready, m_valid, {m_id, m_last, m_data} // source );ここはverilog記述が楽ですね~VHDLやSystemVerilogだったら構造体使うかな?
注意点としてはレイテンシは増えることです。多くの場合周波数は上げられるので、スループットは上がります。どちら重視かはシステム次第です。
2012-04-26 13:55
nice!(0)
コメント(0)
トラックバック(0)
コメント 0