Vivado発表 [HDL]
Xilinxの次世代ツール、Vivadoが発表されました。
C/C++/SystemC辺りは「ハイレベル合成」のライセンスが必要らしい。これってAutoESLかな?お値段ちょっとお高い?といっても50万ぐらい。ちょっと前からしたら格安の値段だと思います。
WebPackでもSystemVerilog使えるなら、QuartusIIはだいぶ前からsv使えるので、svメインにしても良いかな~
Vivadoって名前聞いたとき、もちろん「ビビデ・バビデ・ブー」からもじったんだと思いましたよ(笑)。こっちはBでVじゃないですが。
C/C++/SystemC辺りは「ハイレベル合成」のライセンスが必要らしい。これってAutoESLかな?お値段ちょっとお高い?といっても50万ぐらい。ちょっと前からしたら格安の値段だと思います。
WebPackでもSystemVerilog使えるなら、QuartusIIはだいぶ前からsv使えるので、svメインにしても良いかな~
Vivadoって名前聞いたとき、もちろん「ビビデ・バビデ・ブー」からもじったんだと思いましたよ(笑)。こっちはBでVじゃないですが。
部品: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だったら構造体使うかな?
注意点としてはレイテンシは増えることです。多くの場合周波数は上げられるので、スループットは上がります。どちら重視かはシステム次第です。
部品:AXIシミュレーション用モジュール [AXI]
作ったaxis_sliceを早速テストしたいところですが、いろんなパターンのreadyやvalidを作るのはめんどくさいです困難を極めます。
手を抜きます。
souce、マスター側
sink、スレーブ側
souceはvalidを、sinkはreadyを、ランダムでトグルさせます。テストデータの生成とチェックは別のテストベンチ側でやります。タイミングを生成するだけのモジュール。
有効の比率をratioで変えてて、0は100%、常に有効、1~7につれて有効の比率が下がります。4が50%ぐらい、たぶん。
こんな感じで使います。
この例では直結。本来は間にテスト対象を挟んでデータのチェックを行わせます。
追記。
input wire integer ratio
この構文がisimで通らないので、変更しました。変更したソースは次の「部品:AXIストリームレジスタ、シミュレーション」に。
手を抜きます。
souce、マスター側
module axis_source ( input wire integer ratio, // ratio input wire enable, // enable input wire c_clk, // Clock input wire c_rst, // Reset input wire m_ready, // Ready output wire m_valid // Valid ); 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
sink、スレーブ側
module axis_sink ( input wire integer ratio, // ratio input wire enable, // enable input wire c_clk, // Clock input wire c_rst, // Reset output wire s_ready, // Ready input wire s_valid // Valid ); 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//
souceはvalidを、sinkはreadyを、ランダムでトグルさせます。テストデータの生成とチェックは別のテストベンチ側でやります。タイミングを生成するだけのモジュール。
有効の比率をratioで変えてて、0は100%、常に有効、1~7につれて有効の比率が下がります。4が50%ぐらい、たぶん。
こんな感じで使います。
`timescale 1ns / 1ps `default_nettype none module tb; parameter dw = 16; reg c_clk; reg c_rst; reg enable; integer s_ratio; integer d_ratio; 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; /* 両方とも100% */ @(posedge c_clk); enable <= 1'b0; @(posedge c_clk); s_ratio <= 0; d_ratio <= 0; @(posedge c_clk); enable <= 1'b1; #500; /* 送り側が多め */ @(posedge c_clk); enable <= 1'b0; @(posedge c_clk); s_ratio <= 3; d_ratio <= 5; @(posedge c_clk); enable <= 1'b1; #500; /* 受け側が多め */ @(posedge c_clk); enable <= 1'b0; @(posedge c_clk); s_ratio <= 5; d_ratio <= 3; @(posedge c_clk); enable <= 1'b1; #500; $display("finish"); $stop; end wire ts_ready; wire ts_valid; wire [dw-1:0] ts_data ; /////////////////////////////// reg [dw-1:0] s_data; always @(posedge c_clk) begin if (c_rst) s_data <= 0; else if (ts_ready & ts_valid) s_data <= s_data + 1; end assign ts_data = s_data; /////////////////////////////// axis_source source ( .ratio (s_ratio ), .enable (enable ), .c_clk (c_clk ), .c_rst (c_rst ), .m_ready (ts_ready ), .m_valid (ts_valid ) ); /////////////////////////////// axis_sink sink ( .ratio (d_ratio ), .enable (enable ), .c_clk (c_clk ), .c_rst (c_rst ), .s_ready (ts_ready ), .s_valid (ts_valid ) ); endmodule `default_nettype wire
この例では直結。本来は間にテスト対象を挟んでデータのチェックを行わせます。
追記。
input wire integer ratio
この構文がisimで通らないので、変更しました。変更したソースは次の「部品:AXIストリームレジスタ、シミュレーション」に。
部品:AXIストリームレジスタ続き [AXI]
前回、
「値が有効を示す"valid"ももう一本増えます。」
って終わりましたが、いきなり訂正。増えません。
いや、正確には「必要だと思って作り始めたら実はいらなかったので消した」です。
ソースコード見直したらコメント残してましたよ。えらいぞ>過去の自分。でももうちょっとわかりやすく書こうな。
それにしてもまぁ自分の記憶の当てにならないこと。こういう文章書くと見直しになります。
どうせなので増やして設計始めた所から書いてみます。構成としてはこんな感じ。未接続のレジスタの値を制御することになります。
回路3
状態数を数えます。t_valid, m_ready, m_valid, s_ready, s_validの5本なので32個です。制御対象はt_valid, m_valid, s_ready、データレジスタはt_dataとm_dataです。これらをとりあえず羅列します。あまり頭使ってません。
{t_valid, m_ready, m_valid, s_ready, s_valid} -> {tv,mr,mv,sr,sv}
{t_data, m_data, s_data} -> {td,md,sd}
しばらく眺めます。…この表をそのままcase文にしてもそれはそれで良いかな…無駄なロジックがあるかもしれませんが、今のシリコンからしたら微々たるものとして、もしくはコンパイラががんばるとして「完成」としても良さそうです。物を作ることが目的で最適化が必須とは限りません。さらに、全ての状態を洗い出してるので不定状態がないという安心感も。illegalの所はシミュレーションで$stopさせるとか。
ちゃんと眺めます(笑)
・出力側を見ると、常にt_valid = not s_readyです。これは「t_dataレジスタが埋まってたらもうデータは受け取れない」と読めば当たり前です。ですので、増やしたt_valid信号はいらないのでした。けど思考補助にはなってます。
・t_valid=0もしくはm_valid=0の時、つまりどちらかのレジスタが空いているときに、s_readyを0に(受信拒否)にする理由はありません。この状態も必要ありません(never1の部分)。
・t_valid=1かつm_valid=0の条件、m_dataが空いてるのにt_dataだけにデータが存在するという状態はあり得ないので、この項目は全部消えます(never2の部分)。
・t_valid=1かつm_valid=1、両方のレジスタが埋まってる状態でs_readyを1にするのは違反です(illegalの部分)。s_validが1になったらデータロストを起こします。同時にm_ready=1ならシフトレジスタ動作になってロストしませんが、そのためにはs_readyを組み合わせ回路にする必要があります。
表の不要部分を削除します。
不要とした部分の一つが残ってますが、これをリセット状態とします。
「受信待ち」をリセット状態としても良さそうですが、m_validはともかく、s_readyが1になるのは問題です。全てのモジュールが同時にリセット解除されるという保証が無く、こちらのリセット解除の前に前段のリセットが解除されvalidが1にされた場合、データが失われます。s_readyはリセット中は0で解除後に1にします。
ほか、網掛けの「受信待ち」「空き待ち」は信号が動かないので記述する必要はありません。結果記述する条件は7個となります。
これをHDL化。
ただし、この記述にするときはちゃんとコメントや文章残しましょう。でないと後から見ると「ナニコレ?」状態になります。if~then~elseで書き直せばソースでもある程度意味がわかる記述になりますが、やっぱりコメントは残しましょう。
詠み人知らずの格言:昨日の自分は別の人。
追記
・c_rstもcase文の条件に入れてしまってます。普通の書き方(if (c_rst) ~ else case ~)にしてもリソース、速度ともに変わらなかったので(ISE S6でテスト)そのままにしてます。条件によっては変わるかも。
・「m_data <= s_data;」の記述を動作に関係ない所(例えば5'b01110のライン)に書くと、データラッチ条件のデコードビットが減って回路が小さく速くなる可能性があります。S6では変わりませんでした。
合成結果見たところ、がっつり最適化してくれてるようです。ありがたや~
追記その2
この回路、意外と入力のファンアウトが多いです。それが原因の問題には今のところ当たったことは無いですが、もし当たったら厄介そうです。
入力もレジスタ化するのは結構難儀なので、当たったときに考えようかと…(笑)
「値が有効を示す"valid"ももう一本増えます。」
って終わりましたが、いきなり訂正。増えません。
いや、正確には「必要だと思って作り始めたら実はいらなかったので消した」です。
ソースコード見直したらコメント残してましたよ。えらいぞ>過去の自分。でももうちょっとわかりやすく書こうな。
それにしてもまぁ自分の記憶の当てにならないこと。こういう文章書くと見直しになります。
どうせなので増やして設計始めた所から書いてみます。構成としてはこんな感じ。未接続のレジスタの値を制御することになります。
回路3
状態数を数えます。t_valid, m_ready, m_valid, s_ready, s_validの5本なので32個です。制御対象はt_valid, m_valid, s_ready、データレジスタはt_dataとm_dataです。これらをとりあえず羅列します。あまり頭使ってません。
{t_valid, m_ready, m_valid, s_ready, s_valid} -> {tv,mr,mv,sr,sv}
{t_data, m_data, s_data} -> {td,md,sd}
tv | mr | mv | sr | sv | tv | mv | sr | td | md | ||
---|---|---|---|---|---|---|---|---|---|---|---|
0 | x | 0 | 0 | 0 | 0 | 0 | 1 | never1 | |||
0 | x | 0 | 0 | 1 | 0 | 0 | 1 | never1 | |||
0 | x | 0 | 1 | 0 | 0 | 0 | 1 | 受信待ち | |||
0 | x | 0 | 1 | 1 | 0 | 1 | 1 | sd | m_data受信 | ||
0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | never1 | |||
0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | never1 | |||
0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 受信待ち | |||
0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | sd | t_data受信。これ以上は受け取れない | ||
0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | never1 | |||
0 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | never1 | |||
0 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | m_data送信。空になる | |||
0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | sd | m_data送信と同時に受信 | ||
1 | x | 0 | 0 | 0 | 0 | 1 | 1 | td | never12 | ||
1 | x | 0 | 0 | 1 | 0 | 1 | 1 | td | never12 | ||
1 | x | 0 | 1 | 0 | 0 | 1 | 1 | td | never2 | ||
1 | x | 0 | 1 | 1 | 1 | 1 | 0 | sd | td | never2 | |
1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 空き待ち | |||
1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 空き待ち | |||
1 | 0 | 1 | 1 | 0 | illegal | ||||||
1 | 0 | 1 | 1 | 1 | illegal | ||||||
1 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | td | m_data送信。t_data送信準備 | ||
1 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | td | m_data送信。t_data送信準備 | ||
1 | 1 | 1 | 1 | 0 | illegal | ||||||
1 | 1 | 1 | 1 | 1 | illegal |
しばらく眺めます。…この表をそのままcase文にしてもそれはそれで良いかな…無駄なロジックがあるかもしれませんが、今のシリコンからしたら微々たるものとして、もしくはコンパイラががんばるとして「完成」としても良さそうです。物を作ることが目的で最適化が必須とは限りません。さらに、全ての状態を洗い出してるので不定状態がないという安心感も。illegalの所はシミュレーションで$stopさせるとか。
ちゃんと眺めます(笑)
・出力側を見ると、常にt_valid = not s_readyです。これは「t_dataレジスタが埋まってたらもうデータは受け取れない」と読めば当たり前です。ですので、増やしたt_valid信号はいらないのでした。けど思考補助にはなってます。
・t_valid=0もしくはm_valid=0の時、つまりどちらかのレジスタが空いているときに、s_readyを0に(受信拒否)にする理由はありません。この状態も必要ありません(never1の部分)。
・t_valid=1かつm_valid=0の条件、m_dataが空いてるのにt_dataだけにデータが存在するという状態はあり得ないので、この項目は全部消えます(never2の部分)。
・t_valid=1かつm_valid=1、両方のレジスタが埋まってる状態でs_readyを1にするのは違反です(illegalの部分)。s_validが1になったらデータロストを起こします。同時にm_ready=1ならシフトレジスタ動作になってロストしませんが、そのためにはs_readyを組み合わせ回路にする必要があります。
表の不要部分を削除します。
mr | mv | sr | sv | mv | sr | td | md | ||
---|---|---|---|---|---|---|---|---|---|
x | 0 | 0 | x | 0 | 1 | リセット状態 | |||
x | 0 | 1 | 0 | 0 | 1 | 受信待ち | |||
x | 0 | 1 | 1 | 1 | 1 | sd | m_data受信 | ||
0 | 1 | 1 | 0 | 1 | 1 | 受信待ち | |||
0 | 1 | 1 | 1 | 1 | 0 | sd | t_data受信。これ以上は受け取れない | ||
1 | 1 | 1 | 0 | 0 | 1 | m_data送信。空になる | |||
1 | 1 | 1 | 1 | 1 | 1 | sd | m_data送信と同時に受信 | ||
0 | 1 | 0 | 0 | 1 | 0 | 空き待ち | |||
0 | 1 | 0 | 1 | 1 | 0 | 空き待ち | |||
1 | 1 | 0 | 0 | 1 | 1 | td | m_data送信。t_data送信準備 | ||
1 | 1 | 0 | 1 | 1 | 1 | td | m_data送信。t_data送信準備 |
不要とした部分の一つが残ってますが、これをリセット状態とします。
「受信待ち」をリセット状態としても良さそうですが、m_validはともかく、s_readyが1になるのは問題です。全てのモジュールが同時にリセット解除されるという保証が無く、こちらのリセット解除の前に前段のリセットが解除されvalidが1にされた場合、データが失われます。s_readyはリセット中は0で解除後に1にします。
ほか、網掛けの「受信待ち」「空き待ち」は信号が動かないので記述する必要はありません。結果記述する条件は7個となります。
これをHDL化。
module axis_slice #( parameter dw = 0 // Data Width ) ( // common input wire c_clk, // clock input wire c_rst, // reset // stream-in, sink, slave output wire s_ready, // ready input wire s_valid, // valid input wire [dw-1:0] s_data , // data // stream-out, source, master input wire m_ready, // ready output wire m_valid, // valid output reg [dw-1:0] m_data // data ); reg [dw-1:0] t_data; reg [2:1] mvsr; assign {m_valid,s_ready} = mvsr; always @(posedge c_clk) begin casex ({c_rst,m_ready,m_valid,s_ready,s_valid}) 5'b1xxxx : begin mvsr <= 2'b00; end 5'b0x00x : begin mvsr <= 2'b01; end 5'b0x011 : begin mvsr <= 2'b11; m_data <= s_data; end 5'b00111 : begin mvsr <= 2'b10; t_data <= s_data; end 5'b01110 : begin mvsr <= 2'b01; end 5'b01111 : begin mvsr <= 2'b11; m_data <= s_data; end 5'b0110x : begin mvsr <= 2'b11; m_data <= t_data; end endcase end endmodule表をそのままcaseにしただけ。
ただし、この記述にするときはちゃんとコメントや文章残しましょう。でないと後から見ると「ナニコレ?」状態になります。if~then~elseで書き直せばソースでもある程度意味がわかる記述になりますが、やっぱりコメントは残しましょう。
詠み人知らずの格言:昨日の自分は別の人。
追記
・c_rstもcase文の条件に入れてしまってます。普通の書き方(if (c_rst) ~ else case ~)にしてもリソース、速度ともに変わらなかったので(ISE S6でテスト)そのままにしてます。条件によっては変わるかも。
・「m_data <= s_data;」の記述を動作に関係ない所(例えば5'b01110のライン)に書くと、データラッチ条件のデコードビットが減って回路が小さく速くなる可能性があります。S6では変わりませんでした。
合成結果見たところ、がっつり最適化してくれてるようです。ありがたや~
追記その2
この回路、意外と入力のファンアウトが多いです。それが原因の問題には今のところ当たったことは無いですが、もし当たったら厄介そうです。
入力もレジスタ化するのは結構難儀なので、当たったときに考えようかと…(笑)
部品:AXIストリームレジスタ [AXI]
AXIストリームの単純なリピーターを作ってみます。
プロトコルでは無く、タイミングを考えるレイヤーです。
受け取ったデータをラッチしてそのまま出力するだけ。(その1)で経路を分割するときにレジスタを入れる、という話がありましたが、経路が長くなったときに周波数を出すために入れるレジスタにもなります。
単純なようですが、意外と奥が深いです。そしてこの部品がAXI設計時の要となります。これを作っておかないと色々行き詰まる事が後からわかりました。
経路を分ける、切るという意味合いで、DecoupleとかSliceとか呼んでます。
宣言はこんな感じ。outputのreg/wireは場合によって変わります。ホントはsouce、sinkという名前にしたいところですが、同じ「s」なので、slave、masterとなってます。
AXIのハンドシェイクのタイミングです。
タイミング
"ready"と"valid"の組み合わせなので、4パターンあります。
[valid,ready]="00"と"01"は一緒にして良いかも。
この波形からまず思いつくのはこんな回路。
回路1
"ready"がクロックイネーブルとして使われた、普通のパイプラインレジスタですね。これはこれで使います。DFFを複数段にしてn段のディレイとしても使います。
追記:この回路はAXIとしては使ってはいけません。デッドロックします。
しかし、経路分割としては不十分。"ready"が分割されてません。"ready"の経路が長いままでクリティカルパスになる可能性があります。また、"ready"がレジスタ出力かどうかは後段の"ready"出力によります。この回路では不明です。
あと、せっかくレジスタなのに、受け側が"ready=0"だと、レジスタが空いていても受け取れません。レジスタが空いてたら受け取ってく欲しいですよね。「レジスタが空いてる」=「"valid"が"0"」ですので、こうしてみます。
回路2
機能的には良さそうですが、この回路を入出力端で使ってはいけません。"ready"が組み合わせ出力です。色々弊害が起きます。他の回路でもこのパターンの回路を使うと、"ready"がクリティカルパスになります。"ready"が数珠つなぎになりますからね。
一工夫必要です。
感覚的には、使っちゃダメと言った回路2の方がAXIっぽいと思います。回路1では"valid"が"data"と同じ扱いですが、回路2では「レジスタ内のデータが有効だよ~」というフラグのような扱いになってます。考え方はこちらから発展してます。
"ready"をレジスタ出力にすると後段の"ready"入力から1クロック遅れます。後段が「待った」と言ってもそれが前段に伝わるのは1クロック後です。その間に前段の転送は成立してしまうので、どこかに保存しておかないといけません。ということで、「どこか」=もう一段データを保存するレジスタが必要になります。値が有効を示す"valid"ももう一本増えます。
続く
プロトコルでは無く、タイミングを考えるレイヤーです。
受け取ったデータをラッチしてそのまま出力するだけ。(その1)で経路を分割するときにレジスタを入れる、という話がありましたが、経路が長くなったときに周波数を出すために入れるレジスタにもなります。
単純なようですが、意外と奥が深いです。そしてこの部品がAXI設計時の要となります。これを作っておかないと色々行き詰まる事が後からわかりました。
経路を分ける、切るという意味合いで、DecoupleとかSliceとか呼んでます。
宣言はこんな感じ。outputのreg/wireは場合によって変わります。ホントはsouce、sinkという名前にしたいところですが、同じ「s」なので、slave、masterとなってます。
module axis_slice #( parameter dw = 0 // Data Width ) ( // common input wire c_clk, // clock input wire c_rst, // reset // stream-in, sink, write, slave output reg s_ready, // ready input wire s_valid, // valid input wire [dw-1:0] s_data , // data // stream-out, source, read, master input wire m_ready, // ready output reg m_valid, // valid output reg [dw-1:0] m_data // data ); /* 本体 */ endmodule
AXIのハンドシェイクのタイミングです。
タイミング
"ready"と"valid"の組み合わせなので、4パターンあります。
valid | ready | 状態 |
---|---|---|
0 | 0 | アイドル |
0 | 1 | アイドル(転送催促?) |
1 | 0 | ウェイト |
1 | 1 | 転送成立 |
この波形からまず思いつくのはこんな回路。
回路1
assign s_ready = m_ready; always @(posedge c_clk) begin if (c_rst) begin m_valid <= 1'b0; end else if (s_ready) begin m_valid <= s_valid; m_data <= s_data; end end
"ready"がクロックイネーブルとして使われた、普通のパイプラインレジスタですね。これはこれで使います。DFFを複数段にしてn段のディレイとしても使います。
追記:この回路はAXIとしては使ってはいけません。デッドロックします。
しかし、経路分割としては不十分。"ready"が分割されてません。"ready"の経路が長いままでクリティカルパスになる可能性があります。また、"ready"がレジスタ出力かどうかは後段の"ready"出力によります。この回路では不明です。
あと、せっかくレジスタなのに、受け側が"ready=0"だと、レジスタが空いていても受け取れません。レジスタが空いてたら受け取ってく欲しいですよね。「レジスタが空いてる」=「"valid"が"0"」ですので、こうしてみます。
回路2
assign s_ready = m_ready | ~m_valid; /*他同じ*/
機能的には良さそうですが、この回路を入出力端で使ってはいけません。"ready"が組み合わせ出力です。色々弊害が起きます。他の回路でもこのパターンの回路を使うと、"ready"がクリティカルパスになります。"ready"が数珠つなぎになりますからね。
一工夫必要です。
感覚的には、使っちゃダメと言った回路2の方がAXIっぽいと思います。回路1では"valid"が"data"と同じ扱いですが、回路2では「レジスタ内のデータが有効だよ~」というフラグのような扱いになってます。考え方はこちらから発展してます。
"ready"をレジスタ出力にすると後段の"ready"入力から1クロック遅れます。後段が「待った」と言ってもそれが前段に伝わるのは1クロック後です。その間に前段の転送は成立してしまうので、どこかに保存しておかないといけません。ということで、「どこか」=もう一段データを保存するレジスタが必要になります。値が有効を示す"valid"ももう一本増えます。
続く
OpenIndiana 設定メモ [PC]
ntpクライアントの設定。
NTPサーバーMFEED
ユーザをsuさせたい。
ドライブ情報を見る。
vncを有効にする。151a8デスクトップ版ではインストール済みなので設定だけ。
これで一応つながります。
しかしこのままだと、vncクライアントを終了させるとログインセッション以下、全部終了されてしまうので、少し不便です。
これでvncクライアントを閉じてもログインセッションは生き続けます。ただし、立ち上げっぱなしだとパスワード無しでいきなりつながるので、セキュリティ上は大問題でしょう。なにか設定はあると思いますが、今のところこのまま。
NTPサーバーMFEED
# vi /etc/inet/ntp.conf server ntp.jst.mfeed.ad.jp とか # svcadm enable ntp # svcs ntp STATE STIME FMRI online 18:55:14 svc:/network/ntp:default
ユーザをsuさせたい。
usermod -R root daresore
ドライブ情報を見る。
# iostat -En
vncを有効にする。151a8デスクトップ版ではインストール済みなので設定だけ。
# vi /etc/gdm/custom.conf [security] DisallowTCP=false [xdmcp] Enable=true 保存。 # svcadm enable xvnc-inetd # svcadm restart gdm # svcs オンラインになってることを確認。
これで一応つながります。
しかしこのままだと、vncクライアントを終了させるとログインセッション以下、全部終了されてしまうので、少し不便です。
# svccfg -s xvnc-inetd svc:/application/x11/xvnc-inetd> editprop viが立ち上がるので、以下を編集、保存。 setprop inetd/wait = boolean: true svc:/application/x11/xvnc-inetd> exit # svcadm restart xvnc-inetd
これでvncクライアントを閉じてもログインセッションは生き続けます。ただし、立ち上げっぱなしだとパスワード無しでいきなりつながるので、セキュリティ上は大問題でしょう。なにか設定はあると思いますが、今のところこのまま。
好き嫌いと効率 [HDL]
いくつかHDLのサンプルを上げてますが、verilog HDLばかりです。
でも実は言語としてはVHDLの方が好みです。記述量は増えますが、型管理が厳密で妙な値の変換による齟齬に悩まされないところとか、同期非同期リセットの記述がはっきり分かれてるとか、自分の型定義が便利とか、が良いです。あとVHDLは文法が通れば曖昧な解釈は無いだろうという点も好きです。
ついでにツールもずっとAlteraのQuartus II、というよりSOPC Builderべったりです。Nios IIを使わない事は良くありますが、SOPC Builderを使わない事はほとんどありませんでした。
けど、最近はXilinx ISE+verilogばかりだったりします。verilogの妙な癖のカンがついてきたのと、たまたま最近使ったボードがXilinxだったぐらいが理由でしょうか。また、AXIで作るようになって、SOPC Builderじゃ無くてもつなぐのが楽になったというのもある感じです。QsysがAXIをサポートしたら…やっぱり楽な方に転ぶと思いますが(笑)
結局何でも良かったりします。VHDLとverilog、どっちが優れた言語か、なんて話は興味ありません。言語の細かい文法をろくに覚えてなかったりして、毎回書籍や前のソースを参照してます。その代わり、全く知らない言語でも取っつくようにしてます。「道具」ですから、目的に合っていれば使うし、合わなければ別の道具を使います。FPGAだからHDLで書かなければならないワケでもありませんし、ロジックを使わなければならない理由もありません。CPU(DSP)+C/C++の方が良いならそうします。PC+GPGPUの方が適したアプリケーションもあります。
別に言語マスターになりたいわけでは無いので、これで良いとも思ってます。
一番良いのは、何も書かずに物ができあがることです。
「手を抜くための努力は惜しまない」
でも実は言語としてはVHDLの方が好みです。記述量は増えますが、型管理が厳密で妙な値の変換による齟齬に悩まされないところとか、同期非同期リセットの記述がはっきり分かれてるとか、自分の型定義が便利とか、が良いです。あとVHDLは文法が通れば曖昧な解釈は無いだろうという点も好きです。
ついでにツールもずっとAlteraのQuartus II、というよりSOPC Builderべったりです。Nios IIを使わない事は良くありますが、SOPC Builderを使わない事はほとんどありませんでした。
けど、最近はXilinx ISE+verilogばかりだったりします。verilogの妙な癖のカンがついてきたのと、たまたま最近使ったボードがXilinxだったぐらいが理由でしょうか。また、AXIで作るようになって、SOPC Builderじゃ無くてもつなぐのが楽になったというのもある感じです。QsysがAXIをサポートしたら…やっぱり楽な方に転ぶと思いますが(笑)
結局何でも良かったりします。VHDLとverilog、どっちが優れた言語か、なんて話は興味ありません。言語の細かい文法をろくに覚えてなかったりして、毎回書籍や前のソースを参照してます。その代わり、全く知らない言語でも取っつくようにしてます。「道具」ですから、目的に合っていれば使うし、合わなければ別の道具を使います。FPGAだからHDLで書かなければならないワケでもありませんし、ロジックを使わなければならない理由もありません。CPU(DSP)+C/C++の方が良いならそうします。PC+GPGPUの方が適したアプリケーションもあります。
別に言語マスターになりたいわけでは無いので、これで良いとも思ってます。
一番良いのは、何も書かずに物ができあがることです。
「手を抜くための努力は惜しまない」
HP ProLiant G7 ML110 ESXi導入 [PC]
さて、とりあえずOI151a入れて低消費電力サーバーとしてひとまず安定してるこのサーバーですが、これだけ素性が良いとやっぱり欲が出てきます。FPGAのコンパイル任せたりとか、あれやこれや・・・そうなるとWindows系とLinux系の環境が欲しくなります。仮想環境?
でもzfsをストレージとして使いたいのでSolaris系は残したい。VirtualBoxも考えましたが、やはりここはハイパーバイザー、VMware ESXiでしょう!。何しろハイパーだし(笑)Solaris系の仮想環境はイマイチよくわかりません。
ということで早速USBを別の2GBのに差し替え、VMwareのからESXi 5.0をダウンロード、同時にライセンスも取得、CDRに焼いて起動。BIOSのパワー制御の設定は「OS Control」にしておかないとピンク画面になるそうです。
インストールに関してはたくさん紹介されてるので割愛。G7でも普通通り導入できました。SSH開放してvSphere Clientインストールしてハイパーバイザー環境があっさりできあがり。上手く行ったらメモリを16GBにしようかな~とか考えてます。
ESXiは仮想マシンのファイルを置いておくデータストレージが必要です。外部のiSCSIやNFSサーバーを使うのが想定された使い方ですが、別途サーバーを置くのは避けたいので、内部のSATAに小容量のSSDを接続。データストレージとします。
仮想環境でも普段通りOI151aのインストールは完了します。物理的なCDRではなくISOファイルが使えるのでさらに楽です。
raidz2ボリュームのimport。ここで分かれ目です。zfsのHDDはESXi側で認識されているので、仮想マシンへそのまま渡すにはRDM(Raw Device Mapping)にしなくてはなりません。RDMのやり方は"ESXi RDM"などで検索するとたくさん出てきます。SSHコンソールから、
ただ、自分の環境ではスピンダウンの設定が上手く働きません。何か設定おかしいのかな?HDDの健康のため回しっぱなしならRDMでも良いのですが…
もう一つの方法、「PCI PassThrough」があります。HDDのコントローラ丸ごと仮想環境に渡してしまうという秘技です。これなら!……標準のCeleron G530はこれが出来ません。Vt-dが無効になってるんです。
PCIパススルーをやってみたい。この辺から目的と手段を入れ違えてます(先のRDMで一応の目的は達成されてるのですから)。ということで「Xeon E3 1220L」を購入。HPがラインアップしてるのは1220(L無し。4コア4スレッド)で、こちらの方が確実に動作するのですが、2コア4スレッド、TPD20Wに惹かれ、ちょっと冒険です。
CPU換装にはT15のトルクスドライバー(星形のやつ)が必要ですが、どのご家庭にも必ず1本はあるので問題ないでしょう。グリスを塗り直してヒートシンクを戻し、電源投入。…3分ぐらい画面真っ黒でした。やってしまったかと思った頃、画面が出て「1220L」の文字が!ちゃんと認識されてるようです。
※本来のターボ動作をしてるかは未確認です。
消費電力はあまり変わらないみたいです。同じ2コアのSandy Bridgeでキャッシュが1.5倍、HTが有効という差です。アイドルで気持ち1~2W高い感じでしょうか。
さっそくvShpere Clientからの確認。
サマリ
ばっちり。
パススルーの設定画面。
パススルー
完璧っ!……え?6ポート?
ヤな予感を感じつつ、リブートしたところ…やはり、ストレージとして使ってたSATA接続のSSDもまとめてパススルーされ、ESXiから見えなくなってましたよorz
当たり前といえば当たり前。が、気づきませんでした。
どこかからESXiのデータストレージを確保しなくてはなりませんが、ESXiはUSBのストレージを許してません※。PCIeに拡張しようにもSilcon Imageなどの安いカードは軒並み全滅。Adaptecなど有名どころなら使えますが、価格と消費電力、そしてG7のファンの騒音が増える(別のカードで確認済み)ので躊躇してます。G7単体では手詰まりです。
外部に別途NFSサーバーを立てて、そこにストレージを作り、OI151aをインストール、パススルーさせたHDDのコントローラのzfsのアクセス。これは上手くいってます。が、サーバーを別に用意するのは…これはさっきも書きましたね。
RDMかRAIDカードかNFSサーバーか、の3択となりました。
今はOI151aに戻してます。VirtualBoxを入れてみようかと。
ちょっと前ならI/OデータのUSL-5Pとかで試したいところですが、さすがに入手が…NFSが出来るのかも情報少ないですし。OpenBlockSなどなら安全そうですが、ちょっと高いな…
ということで密かに「Raspberry Pi」に期待してます。NFSサーバーが動くかは知らないのですが、SDカードをNFS exportできれば1W以下のデータストレージとして使えないかな、と。
※追記
色々試してはいますが自分のスキルでは無理そう
・usbarbitratorを止める。
→これを止めると仮想マシンに渡されてたUSBメモリがESXiに戻ってくる。
・/dev/disksにUSBデバイス出てくる。
・fdiskでパーティション作成、タイプをvmfsにする。
・vmkfstoolでvmfs作成。
→失敗。カーネルがサポートしてないよ~的なエラーが出る。
modprobeとかで見るとvmfsはあるが、usbをファイルシステムとして扱うモジュールが無さそう?
で、あきらめ状態。
でもzfsをストレージとして使いたいのでSolaris系は残したい。VirtualBoxも考えましたが、やはりここはハイパーバイザー、VMware ESXiでしょう!。何しろハイパーだし(笑)Solaris系の仮想環境はイマイチよくわかりません。
ということで早速USBを別の2GBのに差し替え、VMwareのからESXi 5.0をダウンロード、同時にライセンスも取得、CDRに焼いて起動。BIOSのパワー制御の設定は「OS Control」にしておかないとピンク画面になるそうです。
インストールに関してはたくさん紹介されてるので割愛。G7でも普通通り導入できました。SSH開放してvSphere Clientインストールしてハイパーバイザー環境があっさりできあがり。上手く行ったらメモリを16GBにしようかな~とか考えてます。
ESXiは仮想マシンのファイルを置いておくデータストレージが必要です。外部のiSCSIやNFSサーバーを使うのが想定された使い方ですが、別途サーバーを置くのは避けたいので、内部のSATAに小容量のSSDを接続。データストレージとします。
仮想環境でも普段通りOI151aのインストールは完了します。物理的なCDRではなくISOファイルが使えるのでさらに楽です。
raidz2ボリュームのimport。ここで分かれ目です。zfsのHDDはESXi側で認識されているので、仮想マシンへそのまま渡すにはRDM(Raw Device Mapping)にしなくてはなりません。RDMのやり方は"ESXi RDM"などで検索するとたくさん出てきます。SSHコンソールから、
# vmkfstools -z /vmfs/devices/disks/"妙に長いHDDの名前" ./"RDMファイルの名前".vmdk -a lsilogicと台数分RDMのファイルを作成し、仮想マシンに追加すればOKとなります。raidz2の4台のHDDも問題なく登録され、仮想oiから"zpool import"でマウントできます。データ転送速度もネイティブ並み、scrubも通ってます。
ただ、自分の環境ではスピンダウンの設定が上手く働きません。何か設定おかしいのかな?HDDの健康のため回しっぱなしならRDMでも良いのですが…
もう一つの方法、「PCI PassThrough」があります。HDDのコントローラ丸ごと仮想環境に渡してしまうという秘技です。これなら!……標準のCeleron G530はこれが出来ません。Vt-dが無効になってるんです。
PCIパススルーをやってみたい。この辺から目的と手段を入れ違えてます(先のRDMで一応の目的は達成されてるのですから)。ということで「Xeon E3 1220L」を購入。HPがラインアップしてるのは1220(L無し。4コア4スレッド)で、こちらの方が確実に動作するのですが、2コア4スレッド、TPD20Wに惹かれ、ちょっと冒険です。
CPU換装にはT15のトルクスドライバー(星形のやつ)が必要ですが、どのご家庭にも必ず1本はあるので問題ないでしょう。グリスを塗り直してヒートシンクを戻し、電源投入。…3分ぐらい画面真っ黒でした。やってしまったかと思った頃、画面が出て「1220L」の文字が!ちゃんと認識されてるようです。
※本来のターボ動作をしてるかは未確認です。
消費電力はあまり変わらないみたいです。同じ2コアのSandy Bridgeでキャッシュが1.5倍、HTが有効という差です。アイドルで気持ち1~2W高い感じでしょうか。
さっそくvShpere Clientからの確認。
サマリ
ばっちり。
パススルーの設定画面。
パススルー
完璧っ!……え?6ポート?
ヤな予感を感じつつ、リブートしたところ…やはり、ストレージとして使ってたSATA接続のSSDもまとめてパススルーされ、ESXiから見えなくなってましたよorz
当たり前といえば当たり前。が、気づきませんでした。
どこかからESXiのデータストレージを確保しなくてはなりませんが、ESXiはUSBのストレージを許してません※。PCIeに拡張しようにもSilcon Imageなどの安いカードは軒並み全滅。Adaptecなど有名どころなら使えますが、価格と消費電力、そしてG7のファンの騒音が増える(別のカードで確認済み)ので躊躇してます。G7単体では手詰まりです。
外部に別途NFSサーバーを立てて、そこにストレージを作り、OI151aをインストール、パススルーさせたHDDのコントローラのzfsのアクセス。これは上手くいってます。が、サーバーを別に用意するのは…これはさっきも書きましたね。
RDMかRAIDカードかNFSサーバーか、の3択となりました。
今はOI151aに戻してます。VirtualBoxを入れてみようかと。
ちょっと前ならI/OデータのUSL-5Pとかで試したいところですが、さすがに入手が…NFSが出来るのかも情報少ないですし。OpenBlockSなどなら安全そうですが、ちょっと高いな…
ということで密かに「Raspberry Pi」に期待してます。NFSサーバーが動くかは知らないのですが、SDカードをNFS exportできれば1W以下のデータストレージとして使えないかな、と。
※追記
色々試してはいますが自分のスキルでは無理そう
・usbarbitratorを止める。
→これを止めると仮想マシンに渡されてたUSBメモリがESXiに戻ってくる。
・/dev/disksにUSBデバイス出てくる。
・fdiskでパーティション作成、タイプをvmfsにする。
・vmkfstoolでvmfs作成。
→失敗。カーネルがサポートしてないよ~的なエラーが出る。
modprobeとかで見るとvmfsはあるが、usbをファイルシステムとして扱うモジュールが無さそう?
で、あきらめ状態。
AXIの勉強(その1) [AXI]
そのいくつまで行くかは不明ですが。
参考にしているのはARM本家の仕様書やXilinxの資料など。まだベータですがAlteraのQsysも追いかけてます。
タイミングチャートの作成にはTimingChartViewerをお借りしてます。同期回路限定ですが便利に使わせていただいてます。ありがとうございます。
用語とか勝手に呼び替えてしまってるところがあります。適当に読み替えて下さい。
AXIのタイミングは、良く資料などでも出てきますが、
リード
ライト
という感じです。
なんか複雑そうですが、実はタイミングとして見ないとならないのは"ready"と"valid"だけです。もう一つのAXI規格の「AXI stream」もそうですが、"ready"と"valid"が両方とも"1"(有効)の時にデータ(荷物、ペイロード)が転送される、という仕組みになってます。この仕組みはAXI3もAXI4も同じです。
図中の"last"信号や、"id"、"size"、"burst"、"lock"などいろいろな信号がありますが、これら全て荷物、ペイロードとして扱います。"ready"と"valid"と"荷物"という関係です。
これに関してはARMの資料にも記述されてます。以下抜粋ですが、これもよく見かける図ですね。
リード
ライト
この図だと、リードが2チャンネルで、ライトが3チャンネルとアンバランスな感じがしますが、リードのデータチャンネルにライトのレスポンスと同等の情報が載ってるので(OKAYなどを返してる)、行き交う情報の量はだいたい対称です。
届いた荷物の中身が重要であって、荷物がいつ届いたかに関してはあまり考えなくて良くなります。マスター側は荷物の準備が出来たら"valid"を有効にする、スレーブ側は荷物の受け入れ準備が出来たら"ready"を有効にする、両方が有効なら転送成立、という考え方です。
ちょっと図を変えるとその分野の人には見慣れた図になったりします。
リード
ライト
ra、waはリードライトのアドレス、rd、wdはデータ、+lastはフラグを付加といった感じです。RS-232Cやネットワークとかの通信でもよく見る、データ通信プロトコルの図になりました。
AXIって物理的な通信経路が複数本あるだけで考え方はネットワークに近いと見た方が良いと私は思ってます。ユーザーが作るマスター・スレーブの回路は、このプロトコルを処理するシーケンサーという事になります。
これによる最大の利点、設計時にタイミングのことを考えなくて良い(タイミングとプロトコルを分けて考えられる)という点だと思います。例えばマスターとスレーブが離れた位置にあって、信号到達までに時間がかかり速度が出ない、という場合、通信経路にレジスタを入れて経路を分割してもプロトコル上は影響ありません。
リード、経路分割
途中にレジスタが入って経路が分割されても通信は成り立ちます。見た目には赤点線のように、到着時間が変わった=矢印の傾きが変わった、のと等価です。
レジスタが入ってるので、個々のチャンネルのタイミングは大きく変わってるはずです。が、AXIは届いた荷物の中身が重要で、届きさえすれば良いという仕組みですから、タイミングの違いは無視してかまわない事になります。これが設計をすごく楽にします。
個々のチャンネルはAXIのストリームと同じですから、次からはその辺の部品を作っていきたいと思います。
参考にしているのはARM本家の仕様書やXilinxの資料など。まだベータですがAlteraのQsysも追いかけてます。
タイミングチャートの作成にはTimingChartViewerをお借りしてます。同期回路限定ですが便利に使わせていただいてます。ありがとうございます。
用語とか勝手に呼び替えてしまってるところがあります。適当に読み替えて下さい。
AXIのタイミングは、良く資料などでも出てきますが、
リード
ライト
という感じです。
なんか複雑そうですが、実はタイミングとして見ないとならないのは"ready"と"valid"だけです。もう一つのAXI規格の「AXI stream」もそうですが、"ready"と"valid"が両方とも"1"(有効)の時にデータ(荷物、ペイロード)が転送される、という仕組みになってます。この仕組みはAXI3もAXI4も同じです。
図中の"last"信号や、"id"、"size"、"burst"、"lock"などいろいろな信号がありますが、これら全て荷物、ペイロードとして扱います。"ready"と"valid"と"荷物"という関係です。
これに関してはARMの資料にも記述されてます。以下抜粋ですが、これもよく見かける図ですね。
リード
ライト
この図だと、リードが2チャンネルで、ライトが3チャンネルとアンバランスな感じがしますが、リードのデータチャンネルにライトのレスポンスと同等の情報が載ってるので(OKAYなどを返してる)、行き交う情報の量はだいたい対称です。
届いた荷物の中身が重要であって、荷物がいつ届いたかに関してはあまり考えなくて良くなります。マスター側は荷物の準備が出来たら"valid"を有効にする、スレーブ側は荷物の受け入れ準備が出来たら"ready"を有効にする、両方が有効なら転送成立、という考え方です。
ちょっと図を変えるとその分野の人には見慣れた図になったりします。
リード
ライト
ra、waはリードライトのアドレス、rd、wdはデータ、+lastはフラグを付加といった感じです。RS-232Cやネットワークとかの通信でもよく見る、データ通信プロトコルの図になりました。
AXIって物理的な通信経路が複数本あるだけで考え方はネットワークに近いと見た方が良いと私は思ってます。ユーザーが作るマスター・スレーブの回路は、このプロトコルを処理するシーケンサーという事になります。
これによる最大の利点、設計時にタイミングのことを考えなくて良い(タイミングとプロトコルを分けて考えられる)という点だと思います。例えばマスターとスレーブが離れた位置にあって、信号到達までに時間がかかり速度が出ない、という場合、通信経路にレジスタを入れて経路を分割してもプロトコル上は影響ありません。
リード、経路分割
途中にレジスタが入って経路が分割されても通信は成り立ちます。見た目には赤点線のように、到着時間が変わった=矢印の傾きが変わった、のと等価です。
レジスタが入ってるので、個々のチャンネルのタイミングは大きく変わってるはずです。が、AXIは届いた荷物の中身が重要で、届きさえすれば良いという仕組みですから、タイミングの違いは無視してかまわない事になります。これが設計をすごく楽にします。
個々のチャンネルはAXIのストリームと同じですから、次からはその辺の部品を作っていきたいと思います。
念のため
ここに置いてあるコンテンツ(HDLなど)は非商用目的に限りご利用いただけます。
実務で使う方がいるとは思いませんが、念のため。
実務で使う方がいるとは思いませんが、念のため。