電気回路/HDL/Verilogメモ の履歴(No.1)
更新内部信号線に対する pullup / pulldown の代替案†
verilog ではFPGA内部に走らせるバス信号線を pullup / pulldown で引っ張ることによってすべてのドライバが highZ の時の値を 1 または 0 に決められることになっています。
LANG:verilog wire oe1, oe2, oe3; wire [BUS_BITS-1:0] data1, data2, data3; wire [BUS_BITS-1:0] bus; assign bus = oe1 ? data1 : {BUS_BITS{1'bz}}; assign bus = oe2 ? data2 : {BUS_BITS{1'bz}}; assign bus = oe3 ? data3 : {BUS_BITS{1'bz}}; // bus 信号線を pulldown して、何も出力されないときは 0 とする generate genvar bus_pull_i; for(bus_pull_i=0; bus_pull_i<BUS_BITS; bus_pull_i=bus_pull_i+1) begin: bus_pull pulldown bus[bus_pull_i]; end endgenerate
もちろんこれは
LANG:verilog assign bus = oe1 ? data1 : oe2 ? data2 : oe3 ? data3 : {BUS_BITS{1'b0}};
と書いても結果は同じなのですが、
data1, data2, data3 をバスに繋ぐコードは通常
あちこちのモジュールに分散しているので、
pulldown で「それ以外」の場合を一度に書けるのはうれしいのです。
ただ、このように書いたコードを論理合成しようとすると、 Synthesis でエラーになってしまいます。
曰く、
LANG:console ERROR:Xst:850 - "test.v" line 40: Unsupported gate instantiation.
そこで verilog のゲートプリミティブ記述 pulldown の代わりに PULLDOWN モジュールを使って
LANG:verilog generate genvar bus_pull_i; for(bus_pull_i=0; bus_pull_i<BUS_BITS; bus_pull_i=bus_pull_i+1) begin: bus_pull PULLDOWN bus_pd(.O(bus[bus_pull_i])); end endgenerate
のように1ビットずつ PULLDOWN してやれると Synthesis は通ります。
でも今度は Translate で
LANG:console ERROR:Xst:2617 - Data Corruption(Timing): Arrival time traversal failed on Ugate element
というエラーが出てしまう。
どうやら PULLDOWN/PULLUP モジュールは IOB に対するプルアップ・プルダウン制約に 特化した記述なようで、内部信号線には使えないということみたいです。
そこで代替案なのですが、pullup/pulldown の代わりに wor/wand を使うと、 同じ結果を論理合成可能な形で得られます。
LANG:verilog wire oe1, oe2, oe3; wire [BUS_BITS-1:0] data1, data2, data3; wor [BUS_BITS-1:0] bus_pulled_down = {BUS_BITS{1'b0}}; assign bus_pulled_down = oe1 ? data1 : {BUS_BITS{1'bz}}; assign bus_pulled_down = oe2 ? data2 : {BUS_BITS{1'bz}}; assign bus_pulled_down = oe3 ? data3 : {BUS_BITS{1'bz}}; wand [BUS_BITS-1:0] bus_pulled_up = {BUS_BITS{1'b1}}; assign bus_pulled_up = oe1 ? data1 : {BUS_BITS{1'bz}}; assign bus_pulled_up = oe2 ? data2 : {BUS_BITS{1'bz}}; assign bus_pulled_up = oe3 ? data3 : {BUS_BITS{1'bz}};
wor に 0 を繋いでおけば、他から 1 が繋がれれば 1 を出力しますが、 何も繋がなければゼロのままになります。これは pulldown と同じ結果です。
同様に、
wand に 1 を繋いでおけば、他から 0 が繋がれれば 0 を出力しますが、 何も繋がなければ 1 のままになります。これは pullup と同じ結果です。
ただしこのようにしてしまうと回路のバグで複数のドライバが同時に値を出力した場合にも 結果が x にならず、バグの発見が遅れてしまう可能性があります。
仕方がないので、現在のところ シミュレーション時には pullup/pulldown を使い、 論理合成時には wand/wor を使うようなコードを書いてみています。
LANG:verilog `ifdef XILINX_ISIM wire [BUS_BITS-1:0] bus; generate genvar bus_pull_i; for(bus_pull_i=0; bus_pull_i<BUS_BITS; bus_pull_i=bus_pull_i+1) begin: bus_pull pulldown bus[bus_pull_i]; end endgenerate `else wor [BUS_BITS-1:0] bus_pulled_down = {BUS_BITS{1'b0}}; `endif // XILINX_ISIM
どなたかもっと良い方法があれば教えて下さい。