電気回路/HDL/Verilogメモ のバックアップソース(No.2)

更新

[[電気回路/HDL]]

#contents

* 内部信号線を pullup / pulldown できないのでその代替案 [#sdffefa8]

以下は Xilinx ISE 14.2 で試した結果です。

** pullup/pulldown の基本 [#c4a046bf]

Verilog ではバス信号線などを pullup / pulldown 
で引っ張ることにより、すべてのドライバが highZ 
の時の値を 1 または 0 に決められることになっています。

通常そのようなトライステートなバスは 
FPGA 外部に出て行く信号線を記述するときに使います。

外部ピンをトライステートにする場合とは対照的に、~
FPGA 内部のネットを疑似トライステート的に記述した場合には、
その回路は論理合成時に マルチプレクサ/and/or などの論理回路に置き換えられて、
「結果的にトライステートの場合と同様の動作をするよう」取りはからってもらえます。

たとえばこんな回路。

 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}};

これは次のような警告を出しつつ問題なく論理合成できて、

 LANG:console
 
 WARNING:Xst:2042 - Unit main: 8 internal tristates are replaced by logic (pull-up yes): bus<0>, bus<2>, bus<3>, bus<4>, bus<5>, bus<6>, bus<7>.
 

デフォルトでは pullup されているかのごとく highZ 時の値は 1 になります。

これを明示的に pulldown するには、Verilog 文法的には

 LANG:verilog
 // 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

を付け足せば良いことになるのですが・・・実は後述の通りこのコードは論理合成できません。

もちろん 3 つの assign と pulldown をまとめて

 LANG:verilog
 assign bus = oe1 ? data1 : 
              oe2 ? data2 : 
              oe3 ? data3 : 
                    {BUS_BITS{1'b0}};

と書いてしまえば論理合成可能で、得られる結果も同じなのですが、

data1, data2, data3 をバスに繋ぐコードは通常
あちこちのモジュールに分散しているので、
pulldown で「それ以外」の場合を一度に書けたらうれしいと。

** 内部信号線に付けた pullup/pulldown は論理合成できない [#fb524492]

上記のコードを論理合成しようとすると、
Synthesize でエラーになります。

 LANG:console
 
 ERROR:Xst:850 - "main.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

のように PULLDOWN してやると、Synthesize は通るようになります。

でも Translate で 

 LANG:console
 
 ERROR:Xst:2617 - Data Corruption(Timing): Arrival time traversal failed on Ugate element
 

となってやはり論理合成できない。

どうやら PULLDOWN/PULLUP モジュールは IOB に対するプルアップ・プルダウン制約に
特化した記述なようで、内部信号線には使えないみたいです。

** そこで代替案 [#bbe7f2de]

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 と同じ結果です。

** デバッグ時の注意点 [#l4bb90ba]

ただしこのようにしてしまうと回路のバグで複数のドライバが同時に
値を出力した場合にもシミュレーション時の結果が 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 = {BUS_BITS{1'b0}};
 `endif // XILINX_ISIM

どなたか、普通ならこうするよ、とか、もっと良い方法があるよ、とかあれば教えて下さい。

** この記事を書いてから発見した記述 [#n8fce6b7]

http://forums.xilinx.com/t5/Synthesis/problem-of-quot-INOUT-quot-use-quot-internal-tristates-are/td-p/122342 より

VHDL ではデフォルトの pullup を pulldown に上書きするために
attribute を指定することが可能と書かれていました。

この記述がシミュレーションでも効果を発揮するのかどうか未確認です。
少なくともデフォルトの pullup はシミュレーションでは効かないので、
pullup/down を指定しない内部トライステートバスはシミュレーションと
実装後とで異なる動作をすることを確認済みです。

 LANG:VHDL
 -- Data Bus Pull Down (virtual)
 attribute tristate2logic_pullup: string;
 attribute tristate2logic_pullup of DATABUS: signal is "no";    -- 0 in case the bus is not driven

別の人は、内部トライステートを使わずすべてちゃんとロジックで記述すべきだと書いていますね。

上の wand/wor を使った回路は一応 「ロジックで記述」 したことになっているので、
そちらがむしろ正攻法なのかもしれませんね。

そうだとすれば、シミュレーション時にトライステートバスを使うことで、
出力がぶつからないことを容易に確認するという手法の方がむしろ「メモ」に値するのかも。

* コメント [#i0f2cd0c]

#article_kcaptcha

Counter: 25116 (from 2010/06/03), today: 2, yesterday: 0