VerilatorのInternalドキュメントを読む。ソースコードを読んでいるだけではだんだん良く分からなくなってきたので、一応概要を確認しておく。
Verilator Internals
このファイルではVerilatorの内部構造とプログラミングの詳細について議論する。これは開発者が問題をデバッグするときに参照するためのものである。
Verilator Internalsのプレゼンテーション http://www.veripool.org も参照のこと。
コードフロー
Verilatorフロー
Verilatorのメインフローは、Verilator.cppのprocess()
関数を追いかけることができる。
最初に、コマンドラインで指定されたファイルが読み込まれる。ファイルを読み込んだことでプリプロセッシングが動作しFlexによる字句解析とBisonによる構文解析が行われる。これによりデザインを表現する抽象文法木(Abstract Syntax Tree: AST)表現が生成され、以降で説明する.tee
ファイルで可視化される。
Verilatorは次にAST上でいくつかのPassを形成し、ASTのリファインと最適化を行う。
AST内のCellが最初にリンクされ、上記のさらに別のファイルが読み込まれ構文解析が行われる。
パラメータが解決され、デザインがエラボレートされる。
次に、Verilatorは階層デザインに対して様々な変更や最適化を行う。これにはカバレッジやアサーション、Xの除去、インライン化、定数伝搬、不使用コードの削除などが含まれる。
デザイン中の参照は次に疑似的にフラット化される。各モジュールの変数と関数は「スコープ」参照を持つようになる。スコープ参照はフラット化された階層内でフラット化されていない変数を発生させる。階層構造中に一度しか現れないモジュールは各変数に対して単一のスコープと単一のVarScope
しか持たない。2回登場するデザインはそれぞれの登場に対して1つのスコープと、各変数につき2つのVarScope
を持つ。これによりフラット化されたデザインに対して、階層を維持しつつ最適化を実行することができるようになる。
更なる変更と最適化は疑似的にフラット化されたデザイン中で実行される。これらの処理にはモジュール参照、関数のインライン化、ループアンローリング、変数のライフタイム解析、ルックアップテーブルの作成、always
の分割、論理ゲートの簡単化(インバータの挿入、など)が含まれる。
Verilatorはコードを順番化する。最良のケースでは、この結果により単一のeval()
関数によりすべてのalways
文が上から下にループすることなく実行される。
Verilatorはフラット化したもののほとんどを捨てる、これにより同じモジュールが複数回呼び出されたときには、コードが共有化される。これにより変数がローカライズされ、個々の関数が組み合わさり、マクロがCのプリミティブに展開され、分岐予測のヒントが与えられ、さらに定数伝搬が実行される。
Verilatorは最終的にC++のモジュールを生成する。
Verilatorのフローで使用されるカギとなるクラス
AstNode
ASTはAstNode
クラスのトップレベルで表現される。この抽象クラスから、個々のコンポーネント(例えば、AstGenerate
はgenerateブロックのためのコンポーネントである)やコンポーネントのグループ(例えば、AstNodeFTask
は関数とタスクのためのクラスであり、AstFunc
とAstTask
クラスに置き換えられる)。
各AstNode
は4つの子供へのポインタを持っている。これらはop1p
からop4p
と呼ばれる。これらのメソッドは次に特定のAst*
ノードクラスにより抽象化される。例えば、AstIf
ノード(if
文用のノード)では、ifsp
はop2p
を呼び出すことでthen
ブロックのASTへのポインタを取得しelsesp
はop3p
を返すことでelse
ブロックのASTか、else
ブロックが無い場合はNULLを取得することができる。
AstNode
クラスは次と前のASTのコンセプトを持っている。例えばブロック中の前後の文などである。これらの文のASTへのポインタは(もし存在すれば)back
やnext
メソッドを用いることで取得することができる。
AstNetList
はツリーのトップに配置されており、もしツリーのトップにいる場合はこのクラスをチェックすることが内部をチェックす標準的な方法であることを覚えておくとよい。
慣習により、各関数やメソッドはnodep
という変数を、現在処理しているAstNode
へのポインタとして使用している。
AstNVisitor
PASSはASTビジタークラス(「ビジター関数」を参照のこと)により実装されている。これらは抽象クラスであるAstNVisitor
のサブクラスとして実装してある。各PASSはビジタークラスのインスタンスを生成し、PASSの処理を実装することになる。
V3Graph
いくつかのPASSはグラフアルゴリズムを使うことになり、V3Graph
クラスがグラフを表現するために使用される。グラフは有向グラフであり、グラフを操作するためのアルゴリズムと、その結果をGraphVizのdotフォーマット(http://www.graphviz.org を参照のこと)を出力する。V3Graph.h
にクラスのドキュメントが格納されている。
V3GraphVertex
これはグラフの頂点を示すためのベースクラスである。頂点はfanout
、 color
、rank
属性が付けられており、グラフを処理するためのアルゴリズムで使用される。汎用的なuser
/userp
メンバ変数も提供される。
dotファイルを出力するための名前、色、形、スタイルを指定するための仮想メソッドが提供される。典型的にはユーザはV3GraphVertex
からの派生クラスを作成し、これらのメソッドを再実装することになる。
エッジの入出力に対してアクセスするためのイテレータが提供されている。典型的にはこれらな以下の形式で使用される。
for (V3GraphEdge *edgep = vertexp->inBeginp();
edgep;
edgep = edgep->inNextp()) {
V3GraphEdge
これはグラフの頂点を有向エッジを表現するためのクラスである。エッジにはweight
とcutabe
属性が付けられている。汎用的なuser/userp
メンバ変数も提供されている。
fromp
とtop
アクセサはそれぞれエッジのfrom
とto
の頂点を返す。
dotファイルを出力するためのラベル、色、スタイルを指定するための仮想めそっだが提供される。典型的には、ユーザはV3Graphedge
からの派生クラスを作成し、これらのメソッドを再実装することになる。
V3GraphAlg
これはグラフアルゴリズムのベースとなるクラスである。このクラスではbool
を返すメソッドであるfollowEdge
メソッドを実装し、アルゴリズムがエッジをどのように追いかけていくかどうかを決めることができる。このメソッドはあるユーザ関数(edgeFuncp()
:コンストラクタにより提供する)よりも重みが大きければtrueを返す。
あらかじめ定義されたアルゴリズムのクラスとメソッドはV3GraphAlg.cpp
から取得可能である。
Verilatedフロー
Verilatorにより生成された評価ループは、殆どの状況において、評価を実行するために実行する単一の関数としてデザインされている。
最初の評価では、Verilatedなコードはinitial
ブロックを呼び出し、すべての信号が安定化するまで(always
ステートメントにより)関数を評価するモジュールを"settle"状態にする。
他の評価では、Verilatedコードはどの入力信号が変化したかをチェックする。クロックである場合、(alwyas @ posedge
ステートメントから)適切なシーケンシャル関数を呼び出す。シーケンシャル関数の間には、組み合わせ回路関数(always @
より)が呼び出される。これが終わると、組み合わせループや内部で生成されたクロックによる変化を検出し、それが見つかった場合にはモデルを再評価しなければならない。
SystemCコードでは、eval()
関数はSystemC SC_METHODによりラップされる。これはすべての入力に対して反応する(理想的にはクロックと組み合わせ入力について伸び反応すれば良いが、トレースのためにはすべての信号に対して評価が必要であり、また性能への影響も小さい)。
トレースが有効である場合、コールバック関数はすべての変数の変化をチェックし、それぞれの変化をトレースに書き込む。頃プロセスを高速化するためには評価プロセスは変数うのビットマスクを評価し、変化を検出する; もしクリアされている場合には、その信号の変化に対するチェックはスキップする。