Verilog
電子回路を設計するハードウェア記述言語 ウィキペディアから
電子回路を設計するハードウェア記述言語 ウィキペディアから
Verilog(ヴェリログ)は、IEEE 1364として標準化されているハードウェア記述言語(Hardware Description Language; HDL)である。最も重要な用途は、デジタル回路をレジスタ転送レベルで設計・検証することである。また、アナログ回路や混合信号回路の検証や、遺伝子回路の設計にも使用されている[1]。
パラダイム | 構造化プログラミング |
---|---|
登場時期 | 1984年 |
最新リリース | IEEE/IEC 62530:2011/ 2011年5月19日 |
型付け | 弱い静的型付け |
影響を受けた言語 | Pascal, C言語,VHDL |
影響を与えた言語 | SystemVerilog |
ライセンス | IEEE/IEC standard |
拡張子 | .v |
もともとVerilogは電子回路シミュレーションを行うシミュレータであり、それに使用する言語であった。文法は、プログラミング言語のC言語やPascalに似ている。
後継言語はSystemVerilogで、Verilogの機能的な上位互換である。System Verilogの規格と統合して、「IEEE/IEC 62530:2011 SystemVerilog - Unified Hardware Design, Specification, and Verification Language」と呼ばれる標準になっている。
Verilogのようなハードウェア記述言語は、ソフトウェアプログラミング言語に似ている。これは、信号の伝達時間や信号強度を記述するコードから作られているからである。Verilogには、ブロッキング代入とノン・ブロッキング代入という2種類の代入演算子がある。ノン・ブロッキング代入文のおかげで、設計者はステートマシンの更新を一時変数を宣言せずに書くことができるようになった。これらの概念はVerilog言語のセマンティックの一部となっているため、設計者は大規模な回路の記述を比較的にコンパクトで簡潔な形式ですばやく書くことができる。Verilogが開発された当初(1984年)、回路設計者はグラフィカルな回路図入力ソフトウェアや、ドキュメントと電子回路シミュレータのためだけに書かれた特殊なソフトウェアを使っていたため、複雑な回路を簡潔に記述できるVerilogは、極めて大きな生産性の向上をもたらした。
Verilogの設計者は、言語の構文を、工学分野のソフトウェア開発で当時すでに広く普及していたC言語と似たものにしたいと考えた。そのため、C言語のように、Verilogの識別子は ケース・センシティブであり、基本的なプリプロセッサーを持っている(ただし、ANSI C/C++のものほどは洗練されていない)。制御フローのキーワード(if/else、for、while、caseなど)はC言語と同一であり、演算子の優先順位もC言語と互換性がある。構文の違いとしては、変数宣言にビット幅が必要であることや、手続きブロックの宣言の相違(カーリー・ブレイス{}の代わりにbegin/endを使用する)があり、その他多数の細かな違いがある。Verilogは変数に固定サイズを与えなければならないが、C言語の場合はサイズは変数の「型」から推定される(たとえば、整数型は8ビットであると)。
Verilogのコードはモジュールの階層から構成する。モジュールは設計の階層をカプセル化し、他のモジュールとは、宣言された入力、出力、必要に応じて双方向ポートを使用して通信する。モジュールの内部では、net/変数の宣言(wire、reg、integerなど)、並行または逐次のステートメントブロック、他のモジュールのインスタンス(サブ階層)などの、あらゆる組み合わせが記述できる。逐次的な文は、begin/endブロック内に配置し、ブロック内では逐次的に実行される。しかし、ブロック自体は並列に実行されるため、この特徴によりVerilogはデータフロー言語となっている。
次に、配線を示すwire、記憶素子を示すregとサブモジュールのリストなどを定義する。さらに、続いてその動作を規定するステートメントやステートメントをグループにしたブロック群を定義する。ブロックはbeginキーワードで始まり、endキーワードで終わる範囲で定義し、ブロック内はステートメントが並列に実行される。逐次実行したい場合は、ブロッキング代入を使うか、クロックのタイミングを待つ書き方をする。各ブロックは並列に実行される。Verilogの'wire'の概念は信号の値 (4状態:"1、0、floating、undefined") と信号の強度(強い、弱いなど)からなる。このシステムのおかげで、共有信号線の抽象的なモデリングが可能になり、複数のソースで共通の回路網を駆動することが可能になった。wireが複数のドライバーを持つ時、wireの(読み取り可能な)値はソースドライバーの関数と強度で解決される。
Verilog言語の文のサブセットは、合成可能である。RTL(register-transfer level; レジスタ転送レベル)として知られる合成可能なコーディングスタイルを使用して書かれたVerilogのモジュールは、合成ソフトウェアを使用することで物理的に実装することができる。合成ソフトウェアは、アルゴリズムを用いて、(抽象的な)Verilogのソースをネットリストに変換する。ネットリストとは、特定のFPGAやVLSI技術で利用可能な基本ゲート(AND、OR、NOT、フリップフロップなど)のみからなるソースコードと論理的に等価な記述である。さらにネットリストに修正を加えることで、究極的には(ASICのフォトマスクのセットやFPGAのビットストリームなどの)電子回路製造の設計図を生成することが可能である。
「Verilog-HDL」という表記が用いることがあるが、IEEEなどの文書では「Verilog」と「HDL」との間にハイフンが入らない「Verilog HDL」のように表記されている。
Verilogは、ゲートウェイ・デザイン・オートメーション社のフィル・ムーアビーが、ハードウェア・モデリング言語とそのためのシミュレータとして1984年頃開発した。その後、同社は1990年にケイデンス・デザイン・システムズが買収した。ケイデンスは、現在も大元のゲートウェイVerilogおよびVerilog-XL論理シミュレータの版権を持っている。
同種のハードウェア記述言語であるVHDLの台頭に対し、ケイデンスはVerilogの規格を公開し標準化する道をとり、OVI(Open Verilog International)と呼ばれる組織へ版権の一部を移譲した。OVI LRM2.0に準拠したVerilogシミュレータとしてSimucad社が開発したSILOS IIIは、1993年には日本で発売されていた[2]。それと前後した1993年6月には、IEEE1364として標準化が開始された[3]。その後、Verilogの規格がIEEEに提出され、IEEE 1364-1995[4]として標準化された。この標準は、Verilog-1995と呼ばれる。なお、OVIはその後Accelleraという組織になっている。
標準化にともない、ケイデンス社以外の会社やフリーソフトのVerilogシミュレータが登場するようになった。
オリジナルのVerilog標準に対する不満を解消するために、Verilog-95に対する拡張がなされた。この拡張はVerilog 2001といいIEEE 1364-2001[5]になった。VHDLにあったgenerate文に対応し、大規模設計が容易になった。
やがてOpenVeraやVerisityのE言語のようなハイ・レベルの検証言語が登場する。このことはその種の機能を盛り込んだVerilog、すなわちコデザイン・オートメーション社によるSuperlogの開発を促すこととなった。コデザイン・オートメーション社は、シノプシスによってその後買収された。SuperlogとVeraの基本部分はAccelleraに寄贈され、次のIEEE標準としてSystemVerilogとVerilogとに分かれた。
言語の最新のバージョンは、アナログ素子および回路の機能記述を行うための言語であるVerilog-Aを包含し、さらにアナログ/デジタルのミックス・シグナル・モデルへの対応もなされており、Verilog-AMSという。
IEEE 1364-2005[6] として規格化。その際、上位互換の SystemVerilog IEEE 1800-2005 も作った。
IEEE/IEC 62530-2011 - SystemVerilog – Unified Hardware Design, Specification, and Verification Language [7]として、Verilog HDLとSystem Verilogの文書を一本化した。
簡単な2フリップフロップの例を以下に示す。
module toplevel(clock,reset);
input clock;
input reset;
reg flop1;
reg flop2;
always @ (posedge reset or posedge clock)
if (reset)
begin
flop1 <= 0;
flop2 <= 1;
end
else
begin
flop1 <= flop2;
flop2 <= flop1;
end
endmodule
Verilogにおける"<="演算子は、普通の手続き型言語とは異なる、ハードウェア記述言語としてのもう1つの側面である。この演算子は「ノンブロッキング」代入文として知られ、alwaysブロックが実行されるまでアクションは登録されない。つまり、代入文を書いた順序は無関係であり、必ず同じ結果が生じる。そのため、flop1とflop2はクロックごとに値がスワップされることになる。
もう1つの代入演算子である"="は、ブロッキング代入文と呼ばれる。"="による代入が行われると、代入先の値は即座に更新される。もし上の例で"<="の代わりにブロッキング演算子の"="を使用したとすると、flop1とflop2の値はスワップされず、手続き型プログラミング言語のように、コンパイラはflop1の値をflop2の値にセットするのだと解釈する(その結果、flop2とflop1の値にセットするという冗長な論理は無視されることになる)。
モジュールはキーワードmoduleで始まる。その後のDiv20xが名前を示し、続く括弧内は端子リストである。6個の端子があり、それぞれの端子の入出力方向はモジュール中のポート宣言(inputとoutput)で指定される。最後のendmoduleでモジュールの終了である。モジュール内には、パラメータ宣言、ポート宣言、レジスタ宣言、イベント宣言、ネット宣言、ステートメントなどを記述する。
//
// 表題 イネーブル付20段カウンター
//
module Div20x (
// ポート宣言(外部から本モジュールへの接続を定義する)
input clock, // クロック
input reset, // リセット(正論理, ハイアクティブ)
input cet, // カウンターとTC出力のイネーブル
input cep, // カウンターのみのイネーブル
output reg [size - 1:0] count, // 束線を示す。この場合5bit幅
// alwaysまたはinitialブロックでドライブする信号は
// reg型でなければならない
output tc); // regと明記していない、他の信号はwire型。
// ポート宣言したwire型信号のネット宣言は省略可能であり、省略することも多い。
// パラメータ宣言
parameter length = 20; // カウント段数
parameter count_zero = 5'b00000; // 5bit幅,2進数の0を表す
parameter count_one = 5'b00001; // 5bit幅,2進数の1を表す
parameter size = 5; // bit幅,基数の指定を省略すると32bit,10進数になる。
// always文。resetやclock信号の変化に同期し並列に実行される
always @ (posedge clock or posedge reset) begin
if (reset) begin // 非同期リセット
count <= count_zero;
end else begin
if (cet || cep) begin // イネーブル
if (count == length - 1) begin
count <= count_zero;
end else begin
count <= count + count_one;
end
end
end
end
// assign文。tcの値は実行中、継続的に値が与る
assign tc = (cet && (count == length - 1));
endmodule
ポート宣言やレジスタ宣言での[a:b]のような形式は、束線(多くはバス)を示す。例えば[3:0]であれば配線が4本あることになる。always節は、括弧内の信号を指定した条件に変化があったときに実行される。posedgeは信号が0から1に変化した場合を指定する。orは括弧内の信号のどれか1つでも変化した場合を指定する。上記のように、resetに合わせてreg型を初期化するのが基本的なデザインパターンである。
ディレイの例は以下の通りである。
...
reg a, b, c, d;
wire e;
...
always @(b or e)
begin
a = b & e;
b = a | b;
#5 c = b;
d = #6 c ^ e;
end
上記のalways節は、もう1つの使い方を示している。たとえば、上の例では、リストに含まれる要素(bまたはe)が変化したタイミングでいつでもalways節が実行される。いずれかの変数が変化すると、即座に新しい値がaに代入される。ブロッキング代入文を使用しているため、bにはその代入後に(aの新しい値を用いて)新しい値が代入される。5単位時間のディレイの後、cにbの値が代入されると同時に、c ^ eの値が計算され、見えない保管場所に一時的に保存される。6単位時間後、保管されていた値がdに代入される。
プロセス内(initialまたはalwaysブロック内)で操作される信号は、reg型を持つ必要がある。また、プロセスの外で操作される信号はwire型を持たなければならない。この時使われるregという名前は、必ずしもハードウェアのレジスタを意味するわけではない。
Verilogにおける定数の定義では、追加のビット幅パラメータがサポートされている。基本的な構文は次のとおりである。
<ビット幅>'<基数を表す文字><数字>
例:
Verilogのいくつかの文は、たとえば$display文など、現実のハードウェアでは物理的に実装できないものもある。そのため、言語の多くの機能はハードウェアを記述するためには使用できない。以下に示す例では、直接物理的なゲートに対応する、合成可能な言語の典型的なサブセットを使用している。
// Mux の例 — 同じ動作をする例を3種類の方法で示す。
// 1番目の例は連続束縛を使用している。
wire out;
assign out = sel ? a : b;
// 2番目の例は同じことを行うために手続き的な表現を使用している。
reg out;
always @(a or b or sel)
begin
case(sel)
1'b0: out = b;
1'b1: out = a;
endcase
end
// 3番目の例: 手続き構造の中で if/else 文を使用することもできる。
reg out;
always @(a or b or sel)
if (sel)
out = a;
else
out = b;
次の非常によく使われる例は、フリップフロップである。Verilogでは、D-フリップフロップが最も簡単に記述でき、以下のようにモデリングできる。
reg q;
always @(posedge clk)
q <= d;
例の中で注目すべき重要な点は、ノンブロッキング代入を使用していることである。基本的な経験則(rule of thumb)として、always節の中でposedgeまたはnegedge文を使用する場合には、<=
を使用するとよい。
Dフリップフロップの変種として、非同期リセット機能を付加したものがある。慣習として、以下のようにリセット状態をチェックするコードを文内の最初のif節とすることが多い。
reg q;
always @(posedge clk or posedge reset)
if(reset)
q <= 0;
else
q <= d;
Varilogの処理を宣言する方法には、alwaysキーワードとinitialキーワードという異なる2つの方法がある。alwaysキーワードは、自由に実行される処理を表す。initialキーワードは、ちょうど1回だけ実行される処理を表す。2つの処理はともに、シミュレーター時刻0に実行が開始され、ともにブロックの終わりまで実行される。alwaysブロックの場合、処理がブロックの最後に到達すると、実行が再度スケジュールされる。よくある間違いは、initialブロックは常にalwaysブロックの前に最初に実行されるというものである。実際には、initialブロックは、1回目の実行時に終了する特殊なalways-ブロックであると考えるとよい。
// 例:
initial
begin
a = 1; // reg a の値を 0 に束縛する
#1; // 1 単位時間待つ
b = a; // reg a の値を reg b の値に束縛する
end
always @(a or b) // a または b が *変更された* タイミングで処理が行われる
begin
if (a)
c = b;
else
d = ~b;
end // このブロックが終了すると、先頭から処理が再開される (例: @ event-control)
always @(posedge a) // reg a が low から high に変更されるたびに実行される
a <= b;
上記2つは、これら2つのキーワードの典型的な使用例であるが、更に2つの特徴的な使用例がある。最もよく使うのが、信号強度を書いたリスト@(...)なしでalwaysキーワード使用する方法である。alwaysは以下のように使用することもできる。
always
begin // always は時刻0に実行が始まり、*決して* 終了しない
clk = 0; // clk を 0 にセットする
#1; // 1 単位時間待つ
clk = 1; // clk を 1 にセットする
#1; // 1 単位時間待つ
end // 実行を続ける — したがって、begin から実行が再開する
alwaysキーワードは、C言語のwhile(1) {..}構造と同じように無限ループとして振る舞う。
もう1つの特徴的な使用例は、initialキーワードにforeverキーワードを追加するというものである。
次の例は、上のalwaysの例と同等である。
initial forever // 時刻 0 で実行が開始され、begin/end の間の処理を永遠に (forever) 繰り替える
begin
clk = 0; // clk を 0 にセットする
#1; // 1 単位時間待つ
clk = 1; // clk を 1 にセットする
#1; // 1 単位時間待つ
end
fork/joinのペアは、Verilog内で並列処理を作るために使われる。fork/joinのペアの間にある文(またはブロック)は、実行フローでforkに到達したタイミングで同時に実行が開始され、forkとjoinの間で最も長い時間実行される文またはブロックの終了後、残りのコードの実行が再開する。
initial
fork
$write("A"); // 文字 A を出力する
$write("B"); // 文字 B を出力する
begin
#1; // 1 単位時間待つ
$write("C");// 文字 C を出力する
end
join
上のように書いた場合、出力は"ABC"または"BAC"のいずれにもなる可能性がある。最初の$writeと2番目の$writeのシミュレーションの実行順序は、シミュレータの実装に依存するためである。シミュレータによっては、意図的に順序をランダム化していることもある。そのため、偶然競合状態が起きたり、意図的な非決定的な振る舞いをする場合がある。
VHDLは、Verilogとは違い、複数のプロセスを動的に生成することはできない[8]。
Verilogでは、実行の順序は常に保証されるわけではない。これは、典型的な例で最もよく示すことができる。次のコードスニペットについて考えてみる。
initial
a = 0;
initial
b = a;
initial
begin
#1;
$display("Value a=%d Value of b=%d",a,b);
end
aおよびbの値として出力されるのはどんな値だろうか?initialブロックの実行順序によって、それぞれ0と0になる場合と、0と何らかの初期化されていない値となる場合の2パターンがありえる。ただし、$display文は#1ディレイを挟んでいるため、2つの代入ブロックより後に実行されることが常に保証されている。
注意:これらの演算子は優先度の順に並んでいるわけではない。
演算の種類 | 演算子記号 | 実行する演算 |
---|---|---|
ビット操作 | ~ | ビット単位NOT(1ビットの否定) |
& | ビット単位AND | |
| | ビット単位OR | |
^ | ビット単位XOR | |
~^ または ^~ | ビット単位XNOR | |
論理演算 | ! | NOT |
&& | AND | |
|| | OR | |
リダクション | & | Reduction AND |
~& | Reduction NAND | |
| | Reduction OR | |
~| | Reduction NOR | |
^ | Reduction XOR | |
~^ または ^~ | Reduction XNOR | |
数値演算 | + | 加算 |
- | 減算 | |
- | 次の値の否定 | |
* | 乗算 | |
/ | 除算 | |
** | べき乗(*Verilog-2001) | |
関係演算 | > | 大なり |
< | 小なり | |
>= | 以上 | |
<= | 以下 | |
== | 論理的に等しい(1'bXのビット値は比較対象から除外される) | |
!= | 論理的に異なる(1'bXのビット値は比較対象から除外される) | |
=== | 4値理論で等しい(1'bXのビット値はリテラルとみなされる) | |
!== | 4値理論で異なる(1'bXのビット値はリテラルとみなされる) | |
シフト操作 | >> | 論理右シフト |
<< | 論理左シフト | |
>>> | 算術右シフト(*Verilog-2001) | |
<<< | 算術左シフト(*Verilog-2001) | |
連結 | { および } | 連結 |
複製 | {n{m}} | 値mをn回複製する |
条件分岐 | ? : | 条件分岐 |
IEEE 1364標準では、0、1、Z(ハイ・インピーダンス)、X(論理値が未知)の4つの状態を使用する4値理論が定義されている。一方、Verilogの競合であるVHDLの標準IEEE 1164では、9つのレベルを持つ多値理論が定義されている。
システムタスクは、シミュレーション中に単純なI/Oや設計測定関数を操作するために利用できる。すべてのシステムタスクは、$が接頭辞になっており、ユーザー定義のタスクや関数と区別される。このセクションでは、最もよく使用されるタスクの一覧を紹介する。完全なリストではないことに注意。
PLI(Program Language Interface)は、プログラマーに対して、VerilogからC言語で書かれたプログラムに制御を移すことを可能にするメカニズムを提供する。IEEE Std 1364-2005において、PLIは新しいVerilog Procedural Interfaceに完全に置き換えられて公式に非推奨となった。
PLI(現在はVPI)を利用することで、Verilogは、たとえば、テストハーネス、マイクロプロセッサの命令セットシミュレータ、デバッガなど、C言語で書かれた他のプログラムと連携することができる。たとえば、PLIはC言語の関数tf_putlongp()
およびtf_getlongp()
を提供しており、この関数を利用すると、現在のVerilogタスクや関数の引数の値を読み書きすることができる。具体的には、tf_putlongp(pin, lower_32, higher_32)
は、C言語側の higher_32
と lower_32
を、Verilog側の64幅のpin信号にセットすることができ, lower_32 = tf_getlongp(higher_32, pin)
は、Verilog側の64幅のpin信号を読み出し、その上位と下位をC言語側の変数 higher_32
と lower_32
にそれぞれセットする。
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.