VerilatorがVerilogの構成自体をAst形式で格納しているのは理解できた。では、これがどのようにC++に置き換えられているのかを観察することにする。
C++のヘッダファイルと実装ファイルが出力されるのはV3EmitC.h
とV3EmitC.cpp
が使用されているようだ。
verilator/src/V3EmitC.cpp
//###################################################################### // Internal EmitC implementation class EmitCImp final : EmitCStmts { // MEMBERS
このなかで、どうやらヘッダファイルとソースファイルの出力でメソッドが2つに分かれているようだった。
//###################################################################### // EmitC class functions void V3EmitC::emitc() { UINFO(2, __FUNCTION__ << ": " << endl); // Process each module in turn for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep = VN_CAST(nodep->nextp(), NodeModule)) { if (VN_IS(nodep, Class)) continue; // Imped with ClassPackage // clang-format off EmitCImp cint; cint.mainInt(nodep); cint.mainImp(nodep, true); { EmitCImp fast; fast.mainImp(nodep, false); } // clang-format on } }
cint.mainInt()
とcint.mainImp()
に分けられる。mainInt()
はヘッダファイル側、mainImp()
がそーそファイル側のようだ。なんでInt()
がヘッダファイル側なのかはよく分からない。
void EmitCImp::mainInt(AstNodeModule* modp) { AstNodeModule* fileModp = modp; // Filename constructed using this module /* ... 中略 ... */ emitIntTop(modp); emitInt(modp);
void EmitCImp::mainImp(AstNodeModule* modp, bool slow) { // Output a module AstNodeModule* fileModp = modp; // Filename constructed using this module m_modp = modp; m_slow = slow; m_fast = !slow; UINFO(5, " Emitting " << prefixNameProtect(modp) << endl); m_ofp = newOutCFile(fileModp, !m_fast, true /*source*/); emitImpTop(fileModp); emitImp(modp);
それぞれ以下のような構成になっている。
// ソースファイル側 void emitImpTop(AstNodeModule* modp); void emitImp(AstNodeModule* modp); // ヘッダファイル側 void emitIntTop(AstNodeModule* modp); void emitInt(AstNodeModule* modp);
ヘッダファイル側から見ていく。emitIntTop()
は本当にヘッダファイルのヘッダ部分を出力するだけのようだ。
void EmitCImp::emitIntTop(AstNodeModule*) { // Always have this first; gcc has short circuiting if #ifdef is first in a file ofp()->putsGuard(); puts("\n"); ofp()->putsIntTopInclude(); if (v3Global.needHeavy()) { puts("#include \"verilated_heavy.h\"\n"); } else { puts("#include \"verilated.h\"\n"); } if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n"); if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n"); if (v3Global.opt.coverage()) { puts("#include \"verilated_cov.h\"\n"); if (v3Global.opt.savable()) v3error("--coverage and --savable not supported together"); } if (v3Global.dpi()) { // do this before including our main .h file so that any references to // types defined in svdpi.h are available puts("#include \"" + topClassName() + "__Dpi.h\"\n"); } }
emitInt()
の方が主役を張っているような関数だが、大きくどのように構成されているのだろうか?
void EmitCImp::emitInt(AstNodeModule* modp) { puts("\n//==========\n\n"); /* ... 中略 ... */
まずはクラス宣言の出力。
// Declare foreign instances up front to make C++ happy puts("class " + symClassName() + ";\n"); emitModCUse(modp, VUseType::INT_FWD_CLASS); puts("\n//----------\n\n"); emitTextSection(AstType::atScHdr); if (AstClass* classp = VN_CAST(modp, Class)) { puts("class " + prefixNameProtect(modp)); if (classp->extendsp()) puts(" : public " + prefixNameProtect(classp->extendsp()->classp())); puts(" {\n"); } else if (optSystemC() && modp->isTop()) { puts("SC_MODULE(" + prefixNameProtect(modp) + ") {\n"); } else { puts("VL_MODULE(" + prefixNameProtect(modp) + ") {\n"); }
プライベート信号と、入出力ポートはここで宣言するようだ。
- ポートの宣言
string section; section = "\n// PORTS\n"; if (modp->isTop()) { section += ("// The application code writes and reads these signals to\n" "// propagate new values into/out from the Verilated model.\n"); } emitVarList(modp->stmtsp(), EVL_CLASS_IO, "", section /*ref*/);
- ローカル変数の宣言
section = "\n// LOCAL SIGNALS\n"; if (modp->isTop()) section += "// Internals; generally not touched by application code\n"; emitVarList(modp->stmtsp(), EVL_CLASS_SIG, "", section /*ref*/); section = "\n// LOCAL VARIABLES\n"; if (modp->isTop()) section += "// Internals; generally not touched by application code\n"; emitVarList(modp->stmtsp(), EVL_CLASS_TEMP, "", section /*ref*/); puts("\n// INTERNAL VARIABLES\n"); if (modp->isTop()) puts("// Internals; generally not touched by application code\n"); if (!VN_IS(modp, Class)) { // Avoid clang unused error (& don't want in every object) ofp()->putsPrivate(!modp->isTop()); // private: unless top puts(symClassName() + "* __VlSymsp; // Symbol table\n"); }
- 内部信号の宣言
ofp()->putsPrivate(false); // public: if (modp->isTop()) { if (v3Global.opt.inhibitSim()) { puts("bool __Vm_inhibitSim; ///< Set true to disable evaluation of module\n"); } if (v3Global.opt.mtasks()) emitMTaskState(); } emitCoverageDecl(modp); // may flip public/private
- パラメータ宣言
section = "\n// PARAMETERS\n"; if (modp->isTop()) section += "// Parameters marked /*verilator public*/ for use by application code\n"; ofp()->putsPrivate(false); // public: emitVarList(modp->stmtsp(), EVL_CLASS_PAR, "", section /*ref*/); // Only those that are non-CONST bool first = true; emitParams(modp, false, &first, section /*ref*/);