美しいSystemVerilog機能の合成可能性を実際にテストする

設計環境のため、私たちのチームはSystemVerilog言語の機能を研究する必要がありました。 憶測に終止符を打つために、私は実際に小さなテストを実施しました。 テストプロジェクトの開発中に、文献を掘り下げることでいくつかの質問が削除されましたが、1つの興味深い点が浮上し、その明確な説明は見つかりませんでした。 状況を修正するために、文書化することにしました。



だから。 SytemVerilogのすべての種類を詰め込んだプロジェクトがあります。 「これ」またはそのことを使用してもあまり利益が得られないと思われる場合でも、「プロジェクト」の主なタスクはSystemVerilogの機能を研究することであるため、これは誤った印象です。 そして今、いくつかのモジュールのセットがあり(具体的には、これらはUARTレシーバーです)、そこからのデータを単一のバスに「マージ」し、RoundRobinアルゴリズムに従ってソートします(特にUARTの場合、蓄積したデータを単一のキューにマージします) 、一方でUSBバスに送られます)。





これは、UARTモジュールの宣言のようです。



module UARTreceiver( RxBus.Slave Bus, input logic [15:0] divider, input logic RxD );
      
      







これは、そのインターフェイスがどのように見えるかであり、RoundRobinアルゴリズムを使用する予定です。



 //   FIFO      interface RxFifoBus #(parameter width=8)(input clk); logic [width-1:0] data; logic rdReq; logic empty; modport slave (input clk, rdReq, output data,empty); modport master (input clk, data, empty, output rdReq); endinterface
      
      







結合モジュールでは、インターフェースは配列として記述されるため、インデックスを作成できます。



 RxBus rcvBuses [0:UARTS](.clk (Bus.clk),.reset_n);
      
      







これらは、Generateを介してモジュール自体に接続されます。



 genvar i; generate for (i=0;i<UARTS;i++) begin : RxGen UARTreceiver rec ( .Bus(rcvBuses[i]), .divider (16'd3125), .RxD (RxDs[i]) ); end endgenerate
      
      







それは見えるだろう-美! 次のように自分自身のスイッチを知ってください(データ行のみの例を示します)。





テキスト
 logic [$clog2(UARTS)-1:0] cnt; always @ (posedge Bus.clk, negedge reset_n) begin if (!reset_n) begin cnt <= 0; end else begin cnt <= cnt + 4'h1; dataToFifo [7:0] <= rcvBuses[cnt].data; dataToFifo [11:8] <= cnt; end end
      
      









しかし、それは期待です。 しかし実際には、rcvBusesオブジェクトにアクセスできないというエラーが表示されます。 インデックスが定数またはgenvar変数(実際には、定数と同等)である場合-問題ありません。必要なだけインデックスを作成します。 たとえば、「額」を禁止する人はいません。



 always_comb begin case (cnt) 4'h0: dataToFifo [7:0] = rcvBuses[0].data; 4'h1: dataToFifo [7:0] = rcvBuses[1].data; 4'h2: dataToFifo [7:0] = rcvBuses[2].data; 4'h3: dataToFifo [7:0] = rcvBuses[3].data; 4'h4: dataToFifo [7:0] = rcvBuses[4].data; 4'h5: dataToFifo [7:0] = rcvBuses[5].data; 4'h6: dataToFifo [7:0] = rcvBuses[6].data; 4'h7: dataToFifo [7:0] = rcvBuses[7].data; 4'h8: dataToFifo [7:0] = rcvBuses[8].data; 4'h9: dataToFifo [7:0] = rcvBuses[9].data; 4'ha: dataToFifo [7:0] = rcvBuses[10].data; 4'hb: dataToFifo [7:0] = rcvBuses[11].data; 4'hc: dataToFifo [7:0] = rcvBuses[12].data; 4'hd: dataToFifo [7:0] = rcvBuses[13].data; 4'he: dataToFifo [7:0] = rcvBuses[14].data; 4'hf: dataToFifo [7:0] = rcvBuses[15].data; default:dataToFifo [7:0] = rcvBuses[0].data; endcase end
      
      









これは機能しますが、モジュールのパラメーター化によって処理されるレシーバーの数を変更する機能は失われます。 そして現在、研究中の言語のソリューションがどれほど美しいかを正確にテストしています。



文献を掘り下げて、インターフェイスが未開封のものであることを自分で明らかにしました。 また、構造とは異なり、パッケージ化されたエンティティとして宣言することはできません。 System Verilog for Design 2nd Editionの有名な本の1つの例では、解決策が紹介されています(詳細は説明されていません)。 美しいオブジェクト指向の世界を残酷な普通の世界に残しておく必要があり、それには一連のチェーンが追加されます。



 logic [7:0] dataBuses [0:UARTS-1];
      
      







2つの世界(オブジェクトと古い)を接続するには、次の行を追加します。





テキスト
 genvar i; generate for (i=0;i<UARTS;i++) begin : RxGen assign dataBuses [i] = rcvBuses[i].data; UARTreceiver rec ( .Bus(rcvBuses[i]), .divider (16'd3125), .RxD (RxDs[i]) ); end endgenerate
      
      









そしてループでこれを行います:





テキスト
 logic [$clog2(UARTS)-1:0] cnt; always @ (posedge Bus.clk, negedge reset_n) begin if (!reset_n) begin cnt <= 0; end else begin cnt <= cnt + 4'h1; dataToFifo [7:0] <= dataBuses[cnt]; dataToFifo [11:8] <= cnt; end end
      
      









新しい配列はパックされているので、システムは私たちに誓うことをやめますが、実際には、最適化後は同じエンティティのエイリアスは2つだけになります。



いいね 発見できることとできないこと。 さて、この不名誉がすべて正しく合成されることを簡単な例で確認するのが良いでしょう。 そのため、ボタンと2チャンネルのオシロスコープを備えたブレッドボードしか手元にありませんでした。 たくさんありませんが、何ですか。 このようなスパルタン条件で上記のインデックス作成のパフォーマンス(または動作不能)を美しく証明する問題を考えてみましょう。



ボタンは2つだけです。 つまり、多くのソースは模倣していません。 しかし、逆システムですべてをチェックする人はいません。 タイヤの数は1つではなく、多くの1つです! 2つのボタン-2ビットバス。 FPGAの脚に配布します。



1つのボタンは脚の1つのグループに作用し、別のグループには影響しません。 ボタンの状態が変化すると、信号はレッグに沿って遅延して伝播します。 遅延は、各ペア間で1クロックです。 したがって、関心のあるすべてのもの-配列の要素のインデックス付けと、バスが正しく切り替えられるという事実の両方を制御することが可能になります。



このようなプロジェクトを作成します。



 module ObjTest1 #(parameter cnt=4) ( input logic clk50, input logic [1:0] button, output logic [cnt-1:0] group1, output logic [cnt-1:0] group2 ); //     , //     1  . logic clk; MainPll pll ( .inclk0 (clk50), .c0 (clk) ); //   ,     //     ( ) logic [1:0] wires [0:cnt-1]; //        //   ,       //  genvar i; generate for (i=0;i<cnt;i++) begin : generilka assign group1 [i] = wires [i][0]; assign group2 [i] = wires [i][1]; end endgenerate //      logic [$clog2(cnt)-1:0] iter; //      //      always_ff @(posedge clk) begin iter <= iter + 1'b1; wires [iter][0] <= button[0]; wires [iter][1] <= button[1]; end endmodule
      
      







まだ説明していないものから-PLL。 以前の記事の1つで 、誤った結論に達し、オシロスコープの上限で作業しました。 これをなくすために、PLLは周波数を1メガヘルツに減らします。 残りはすでに説明されています。 したがって、最上部を確認してください。



マイクロ回路の実際の脚は、2つのベクトルとして記述されます。 あまり美しくはありませんが、飾ります:



 output logic [cnt-1:0] group1, output logic [cnt-1:0] group2
      
      







それまでの間、それらを調査中のアレイに接続します。



 //   ,     //     ( ) logic [1:0] wires [0:cnt-1];
      
      







このように:



 //        //   ,       //  genvar i; generate for (i=0;i<cnt;i++) begin : generilka assign group1 [i] = wires [i][0]; assign group2 [i] = wires [i][1]; end endgenerate
      
      







ボタン-バスの形で説明されています:



 input logic [1:0] button,
      
      







また、ラウンドロビンアルゴリズムは次のように実装されます。



 //      logic [$clog2(cnt)-1:0] iter; //      //      always_ff @(posedge clk) begin iter <= iter + 1'b1; wires [iter] <= button; end
      
      







コンパイルし、どれだけのリソースを使用したかを確認します(ラッチされた8つのレッグに加えて、カウンターごとに2ビットがあり、合計で物理的に解決できないトリガーは10個未満です)







また、RTL Viewerには余分なものは表示されません。 PLLがあり、カウンターがあり、デコーダーがあり、2ビットバスに結合されたトリガーがあります。 私たちが求めたすべて:







クリスタルに注ぎ、隣接する2本の足に接続し、ボタンで遊んでください。 1 MHzの周波数に対応する1マイクロ秒の遅延が発生します。







2番目のプローブを次のレッグに転送します。







そして、次のことに:







すべてが理論と一致しています。 この半分は他のボタンに応答しません。



最後に、レッグを2つの連絡先グループとしてではなく、単一の配列として記述する場合に開発環境が何を伝えるかを確認しましょう。これにより、配列をグループに接続する退屈な生成ブロックが回避されます。 そのようなコードには、研究の本質だけでなく、余計なものは一切含まれていません(まあ、PLLで、オシロスコープではっきりと見える領域に結果を転送します):



 module ObjTest2 #(parameter cnt=4) ( input logic clk50, input logic [1:0] button, output logic [1:0] group [0:cnt-1] ); //     , //     1  . logic clk; MainPll pll ( .inclk0 (clk50), .c0 (clk) ); //      logic [$clog2(cnt)-1:0] iter; //      //      always_ff @(posedge clk) begin iter <= iter + 1'b1; group [iter] <= button; end endmodule
      
      







Pin Plannerにアクセスして、混合感に関するジョークを思い出します。







一方で、村にも街にもない奇妙なグループがあります(赤で囲みました)。 しかし、一方で、何も割り当てられていません。 また、多次元グループも存在します。 そして、それに脚を割り当てることができます。 そして、オシロスコープはすべてが正しく機能していることを示しています。

ちなみに、RTLビューの画像はちょうどきれいになりました! 回路設計の教科書に挿入してください!







おわりに



SystemVerilog言語が提供する素晴らしい機能は、Quartus II開発環境で完全に合成されます(言語が若いので最新バージョンをダウンロードしました。Quartusの古いバージョンではすべてがそれほどバラ色ではないかもしれません)。 残念ながら、この言語には、オブジェクト指向の世界でのプログラミングのみを不可能にするいくつかの欠点があります。 しかし、これらは言語の機能です。 それらは、テキストにパイルを追加する通常のエンティティを作成することで解決されますが、オブジェクトエンティティのエイリアスにすぎないため、結果のコードの複雑さには影響しません。



当社でこのトピックについて議論された質問は終了しました。 おそらく、上記のいくつかは他の人にとって興味深いものになるでしょう。



All Articles