火曜日, 4月 13, 2010

Constant Function

by Jorge Franganillo
Verilog2001からconstant functionというものがサポートされています。
使い用によっては便利だなと思いつつ、使わなくてもなんとかなるものなのであまり気にしていませんでしたが、使いたくなったときに調べるのが面倒なのでメモしときます。

まずどういうときに使うのかというと、使いたい時というのは多分限られていて例えばFIFOなどのパラメータ化を考えたとき、昔のVerilog HDLではパラメータとしてデータのビット幅とFIFOの深さそれとFIFOのポインタのビット幅をという3つのパラメータを指定する必要がありました。

この中でFIFOのポインタのビット幅というのはFIFOの深さから導けるのですが、単純な四則演算などで計算することが出来ず、for文などを使う必要があるため別途パラメータとして渡してあげなければならなかったわけです。
例えば次のような感じです。
  1. module my_fifo (clk, rst_n, we, re, wdt, rdt, full, empty);  
  2.   
  3.   parameter DT_WIDTH = 8;  
  4.   parameter FIFO_DEPTH = 16;  
  5.   parameter PTR_WIDTH = 4;  
  6.   
  7.   input clk, rst_n, we, re;  
  8.   input [DT_WIDTH-1:0] wdt;  
  9.   output [DT_WIDTH-1:0] rdt;   
  10.   output full, empty;  
  11.   
  12.   reg [DT_WIDTH-1:0] ram [0:FIFO_DEPTH-1];  
  13.   reg [PTR_WIDTH-1:0] wr_ptr;  
  14.   reg [PTR_WIDTH-1:0] rd_ptr;  
  15.   
  16.   ...;  
  17.   
  18. endmodule  
それがVerilog2001からは演算結果が定数となる場合であれば、parameterの右辺にfunctionを使えるようになったため以下のように書けるので、パラメータの受け渡しの際にFIFO_DEPTHとPTR_WIDTHの間で不整合が起こるような危険を回避できます。
  1. module my_fifo (clk, rst_n, we, re, wdt, rdt, full, empty);  
  2.   
  3.   parameter DT_WIDTH = 8;  
  4.   parameter FIFO_DEPTH = 16;  
  5.   localparam PTR_WIDTH = log2(FIFO_DEPTH);  
  6.   
  7.   input clk, rst_n, we, re;  
  8.   input [DT_WIDTH-1:0] wdt;  
  9.   output [DT_WIDTH-1:0] rdt;   
  10.   output full, empty;  
  11.   
  12.   reg [DT_WIDTH-1:0] ram [0:FIFO_DEPTH-1];  
  13.   reg [PTR_WIDTH-1:0] wr_ptr;  
  14.   reg [PTR_WIDTH-1:0] rd_ptr;  
  15.   
  16.   ...;  
  17.   
  18. endmodule  
ここでlog2()は2を底とする入力の対数を計算するfunctionとします。

で、この2を底とする入力の対数を求めるfunctionなんですがConstant Functionの例では必ずと行っていいほどこの例になっています。しかし、なかには間違えているコードもあるようなのでご注意ください。
例えばこちらの"3.0 LRM Errors"ではIEEE Verilog-2001 Standardで紹介されている次のコードに誤りがあると指摘しています。
  1. //define the clogb2 function   
  2. function integer clogb2;   
  3.   input depth;   
  4.   integer i,result;   
  5.   begin   
  6.     for (i = 02 ** i < depth; i = i + 1)   
  7.       result = i + 1;   
  8.     clogb2 = result;   
  9.   end   
  10. endfunction  
どこが悪いのかわかりますか?

1つは入力のdepthが1ビットとなっていること。

そしてもうひとつはdepthが1だった場合にforループが実行されず、resultが初期化されないためにこのfunctionが不定値を返してしまうことです。

そこでこのfunctionは次のように置き換えるべきだと書いてあるのですが。。。
  1. function integer clogb2;   
  2.   input [31:0] value;   
  3.   for (clogb2=0; value>0; clogb2=clogb2+1)   
  4.     value = value>>1;   
  5. endfunction   
これはこれで問題がある気がします。

入力が2の定数である場合、結果が正しくないですよね?
例えば入力value=2の時、1を返すのが正しい結果のはずですがforループが2回実行されるので結果2を返してしまいます。

こちらで紹介されている例のほうが良いでしょう。
  1. function integer log2;  
  2.   input integer value;  
  3.   begin  
  4.     value = value-1;  
  5.     for (log2=0; value>0; log2=log2+1)  
  6.       value = value>>1;  
  7.   end  
  8. endfunction  
入力が0だと問題ですが、対数を求める関数なのでもともと入力は1以上であるはずです。

使う機会はそれほど多くないかもしれませんが覚えておいて損はないでしょう。

金曜日, 4月 09, 2010

SystemVerilogをはじめたい人に

by magnetbox
4月だし、これからSystemVerilogをはじめたいなという方に調度良いサイトを見つけたのでリンクしときます。

SystemVerilogの世界へようこそ

以前紹介したブログ、Verification Engineerの戯言を書かれている方のサイトです。

OVMやVMMなどの紹介もありますね。
日本語のサイトでは非常に珍しいと思います。

Have a nice experience!

木曜日, 4月 08, 2010

always_combとalways@*の違い

by Manuel Cernuda

以前のVerilog HDLでは、alwaysを使って組み合わせ回路を書く場合には
Sensitivity Listに入力を全て列挙しなければならず面倒で、またバグの温床でもありました。
例えばこんな感じです。

  1. always @ ( color or a or b or c or ... ) begin  
  2.   case (color)  
  3.     RED  : x = a;  
  4.     BLUE : x = b & c;  
  5.     ....;  
  6.   endcase  
  7. end  

Verilog 2001ではalways @*がサポートされてかなりの簡潔に記述できるようになり、Sensitivity Listの記入漏れの問題は起きにくくなりました。
しかし、それでもまだ実はシミュレーション時に問題が起こる可能性があります。

  1. always @* begin  
  2.   case (color)  
  3.     RED  : x = a;  
  4.     BLUE : x = b & c;  
  5.     PINK : x = my_function(d, e, f);  
  6.     ....;  
  7.   endcase  
  8. end  
  9.   
  10. function my_function;  
  11.   input sig_a;  
  12.   input sig_b;  
  13.   input sig_c;  
  14.   begin  
  15.     my_function = sig_a | sig_b | sig_c | SIG_X;  
  16.   end  
  17. endfunction  

always @*はalways内で直接使用している信号までしかsensitiveではなく、
例えば上の例の様な記述の場合、my_function()の中で使用しているSIG_Xに変化があったとしてもalways @*の出力xは変化しません。

このような問題を解決できるのがSystemVerilogのalways_combです。

  1. always_comb begin  
  2.   case (color)  
  3.     RED  : x = a;  
  4.     BLUE : x = b & c;  
  5.     PINK : x = my_function(d, e, f);  
  6.     ....;  
  7.   endcase  
  8. end  

このようにalways @*の場合と同様に記述出来ます(functionの内部は省略)。
しかし、always_combの場合はalways @*と違い、内部に含まれるfunctionが使用している信号に対してもsensitiveになります。つまり、この場合はSIG_Xの変化にもalways_combの出力xが連動します。

その他にも、always_combではalways @*と比べて以下のような違いがあります。

・時刻0で一度always_comb内部が実行される
・always_comb内でドライブされている信号は他の場所でドライブできない
・always_comb内ではwait()やfork...joinなどのタイミングをブロックするような制御が行えない

他にもSystemVerilogではalways_latchやalways_ffなどが追加されていますが特にこのalways_combが重要ですね。

alwaysで組み合わせ回路を記述する場合は是非使うようにしましょう。

最後に、昔書いた組み合わせ回路の記事のリンクを貼っておきます。
ここではalwaysで組み合わせ回路を記述する場合の短所としてregで出力を宣言しなければならないと書きましたが、SystemVerilogでは新たにlogicが追加されたので便利になりました。
functionを使う場合は、always_combの中に入れておくとより安全でしょう。

こちらで別の例が詳細されていたのでリンクしときます。