プロセス(Processes)

概要

手続き構文をまとめる大きなブロックとして、alwaysや、initialがありますが、それらは、プロセスと呼ばれます。プロセスは、シーケンシャルブロックかパラレルブロックで構成されます。

VerilogHDLでの、信号は、大別して二つあり、

VariableとNet(wire)

とありますが、プロセス内での書き込みは、原則Veriableのみです。

学習で悩ましいのが、VariableとNET(wire)の違いではないでしょうか?

があります。alwaysや、initialでの書き込みは、原則としてVariableしかありません。Veriableは、通常のプログラミング言語の変数と同じようなものですが、4値(0,1,x,z)型の変数と 2値型(0,1) があります。一方、ネットでは、回路でのバスファイトや、スイッチドレベルのモデリング(CMOSのトランジスタや、DRAMのチャージ回路等)を扱うために、4値情報の他に強度情報(strength)が必要になります。この情報のために、1ビットのネットについて、1バイト程度の強度情報が付随します。回路としてのインピーダンス的な側面を扱おうとすれば、Netにならざるを得ないのですが、全てをいちいちNetで記述していたのでは、シミュレータ的にも重すぎます。

  

言語学習的には、一つにしてよ! と言いたくなりますが、高速にシミュレートするということを念頭に言語設計すると、やはり分けた方がメモリ効率が良いわけです。(80年代から90年代にかけて、乱立するHW記述言語の中でVerilogHDLが生き残ったのは、初期のVerilogXLシミュレータのゲートレベルシミュレーションが、他社より2桁ほど速かったことが大きかった、と言う人もいます。)

ビヘービア的な記述は、always/initial になりますが、そういう訳で軽量なVariableしか書けないようになっているのです。

さらに検証の面では、4値もいらない、2値で十分、そういう背景で、SVで2値型が導入されました。

時代の要請に従って、互換性を維持しつつ仕様が複雑になっていった、ということです。

プロセスとスレッドの違い

プロセスの実装としては、スレッドが用いられますが、悩ましいのは、LRMでもプロセスとスレッドが殆ど同義で使われてことがあるために、明確な区別はありません。由来は、勿論OS用語にあり、OS上のプロセスとスレッドは、明確に異なる概念ですが、VerilogHDLにおいては、プロセスとスレッドの違いは、曖昧です。

ブロッキングとは?

LRMを読むとしばしば、blocking とかblocked という単語に出会います。このblockとは一体なにか?を理解することが、VerilogHDLシミュレータの挙動を理解する、言い換えるとVerilogHDL学習の第一歩です。このblockedは、やはりOS由来だと思いますが、実行を邪魔される/実行を中断させられる、という意味で使っています。VerilogHDLでブロックされる構文は、僅かで、下の3個しかありません。

SVにおいては、これにstdパッケージのなかのクラスメソッドのいくつかが追加になっています。

VerilogHDLシミュレータは、LRMに従って、逐次実行するマシンです。他のプログラミング言語と同様に逐次実行されます。一つのプログラムカウンタ、つまり一個のCPUで言語仕様に従って逐次実行を模擬するマシンです。ブロック命令に出会わなければ、素直に上から下への順番通りに実行されます。ブロック命令に出会うと、次の実行は、シミュレータに委ねられ、実行が飛んでいくように見えます。 ハードウェア的には、同時でも、シミュレータ的には、LRMに従って、一つづつステートメントを処理しているだけです。言うなれば、筆を一つ持って一筆書きしているようなものです。OSでいうマルチスレッドのように同時に実行されるものではありません。

ノンブロッキング文とは、

ブロッキング文 =代入文のBNFを見ると lvalue= <delay_or_event_control> expressionになっていて、delayやイベントは、オプションになっています。ノンブロッキング文 <=も同様です。このブロッキング文の方は、delayやイベントでステートメントに着いた時点で、実行を中断させられるという意味で、ブロッキングです。(実務的には、ブロッキング文で、delayやイベント付きで書くことは稀なので、ブロッキング文とはいっても、殆どの場合ブロックされることはありません。)

一方、ノンブロッキングの方は、そのステートメントに着た時、あたかも、delayやイベントがなかったかのように、逐次実行されます。つまりその時点ではブロックされません。(delayはイベントは、後で処理されます。)そういう意味でノンブロッキング文になっています。

この様子は、シミュレータをステップ実行させれば、よく分かります。

中断して戻ってくる機構-コルーチン

C言語で、中断して戻ってくる関数は書けるでしょうか?親子関係にある子関数を呼ぶということであれば簡単ですが、そうではなくて親子関係にない関数郡での話しです。各関数は、互いに独立に進みものとします。

void c_function1()
{
    int i;
    int mem[10];

    printf("Hi I'm C i=%d",i);
    //#10 some delayed.. 別の関数を実行してまた戻ってくる。
    printf("Hi I'm C i=%d",i);//そのためには、CPUの全てのコンテキスト、c_function中のautomatic 変数も保存しておかなけれならない。

}
void c_function2()
{
    int i;
    int mem[10];

    printf("Hi I'm C i=%d",i);
    //#10 some delayed.. 別の関数を実行してまた戻ってくる。
    printf("Hi I'm C i=%d",i);//そのためには、CPUの全てのコンテキスト、c_function中のautomatic 変数も保存しておかなけれならない。

}

言語としては、サポートしていませんが、機能としては、、CPU実行コンテキスト(全レジスタ)と、関数上のスタックをそのまま保持しておいて戻ってくればよいので、実装依存にはなりますが書けます。

こういう機能をコルーチンと言いますが、言語機能として搭載している言語もあります。

で、ブロックされて戻ってくるには、コルーチンの技法を使えばよい訳です。つまりVerilogHDLのプロセスの実体は、コルーチンであり、多数のプロセスが同時に動くVerilog Simulatorは、コルーチンの塊である、ということが言えます。

コルーチンは、OS用語のスレッドで置き換えることはできますが、VerilogHDLでは、それを行った実装はありません。理由は二つあり、コルーチンの方がずっと軽量なのと、マルチスレッドの同期化が難しいからです。

function とtaskの違い

function内では、特別な場合を除きスレッドを起動することができません。いわば、functionは、Cの関数みたいなもので、途中で中断して戻ってくる機構がありません。これに対して、taskは、それ自体スレッドです。スレッドは、上で述べたようにCPU実行コンテキストとスタックの保存というオーバヘッドが付きまとうので、どうしても遅くなります。SVから、void function(値を戻さないfunction)ができます。スレッド・Delay・イベントを使わないのならば、taskを使わず、function で実装した方が高速です。

スレッドとは、

ここまで来ると、VerilogHDLでいうスレッドとは、なにかもうお分かりだと思います。OS上のスレッドとは違います。VerilogHDLのスレッドとは、中断して戻ってくる機構を備えたブロックを言い、コルーチンで実装されます。OS上のスレッドという意味ではシングルスレッドで実行されます。


まとめとして、