526 lines
22 KiB
C++
526 lines
22 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Verilator: Build DPI protected C++ and SV
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
// Version 2.0.
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
//
|
|
//*************************************************************************
|
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
#include "V3ProtectLib.h"
|
|
|
|
#include "V3Control.h"
|
|
#include "V3Hasher.h"
|
|
#include "V3InstrCount.h"
|
|
#include "V3String.h"
|
|
#include "V3Task.h"
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
//######################################################################
|
|
// ProtectLib top-level visitor
|
|
|
|
class ProtectVisitor final : public VNVisitor {
|
|
AstVFile* m_vfilep = nullptr; // DPI-enabled Verilog wrapper
|
|
AstCFile* m_cfilep = nullptr; // C implementation of DPI functions
|
|
// Verilog text blocks
|
|
AstTextBlock* m_modPortsp = nullptr; // Module port list
|
|
AstTextBlock* m_comboPortsp = nullptr; // Combo function port list
|
|
AstTextBlock* m_seqPortsp = nullptr; // Sequential function port list
|
|
AstTextBlock* m_comboIgnorePortsp = nullptr; // Combo ignore function port list
|
|
AstTextBlock* m_comboDeclsp = nullptr; // Combo signal declaration list
|
|
AstTextBlock* m_seqDeclsp = nullptr; // Sequential signal declaration list
|
|
AstTextBlock* m_tmpDeclsp = nullptr; // Temporary signal declaration list
|
|
AstTextBlock* m_hashValuep = nullptr; // CPP hash value
|
|
AstTextBlock* m_comboParamsp = nullptr; // Combo function parameter list
|
|
AstTextBlock* m_clkSensp = nullptr; // Clock sensitivity list
|
|
AstTextBlock* m_comboIgnoreParamsp = nullptr; // Combo ignore parameter list
|
|
AstTextBlock* m_seqParamsp = nullptr; // Sequential parameter list
|
|
AstTextBlock* m_nbAssignsp = nullptr; // Non-blocking assignment list
|
|
AstTextBlock* m_seqAssignsp = nullptr; // Sequential assignment list
|
|
AstTextBlock* m_comboAssignsp = nullptr; // Combo assignment list
|
|
// C text blocks
|
|
AstTextBlock* m_cHashValuep = nullptr; // CPP hash value
|
|
AstTextBlock* m_cComboParamsp = nullptr; // Combo function parameter list
|
|
AstTextBlock* m_cComboInsp = nullptr; // Combo input copy list
|
|
AstTextBlock* m_cComboOutsp = nullptr; // Combo output copy list
|
|
AstTextBlock* m_cSeqParamsp = nullptr; // Sequential parameter list
|
|
AstTextBlock* m_cSeqClksp = nullptr; // Sequential clock copy list
|
|
AstTextBlock* m_cSeqOutsp = nullptr; // Sequential output copy list
|
|
AstTextBlock* m_cIgnoreParamsp = nullptr; // Combo ignore parameter list
|
|
const string m_libName;
|
|
const string m_topName;
|
|
bool m_foundTop = false; // Have seen the top module
|
|
bool m_hasClk = false; // True if the top module has sequential logic
|
|
|
|
// VISITORS
|
|
void visit(AstNetlist* nodep) override {
|
|
m_vfilep
|
|
= new AstVFile{nodep->fileline(), v3Global.opt.makeDir() + "/" + m_libName + ".sv"};
|
|
nodep->addFilesp(m_vfilep);
|
|
m_cfilep
|
|
= new AstCFile{nodep->fileline(), v3Global.opt.makeDir() + "/" + m_libName + ".cpp"};
|
|
nodep->addFilesp(m_cfilep);
|
|
iterateChildren(nodep);
|
|
}
|
|
|
|
void visit(AstNodeModule* nodep) override {
|
|
if (!nodep->isTop()) {
|
|
return;
|
|
} else {
|
|
UASSERT_OBJ(!m_foundTop, nodep, "Multiple root modules");
|
|
}
|
|
FileLine* const fl = nodep->fileline();
|
|
// Need to know the existence of clk before createSvFile()
|
|
m_hasClk = checkIfClockExists(nodep);
|
|
createSvFile(fl, nodep);
|
|
createCppFile(fl);
|
|
|
|
iterateChildren(nodep);
|
|
|
|
// cppcheck-suppress unreadVariable
|
|
const V3Hash hash = V3Hasher::uncachedHash(m_cfilep);
|
|
m_hashValuep->add(cvtToStr(hash.value()) + ";\n");
|
|
m_cHashValuep->add(cvtToStr(hash.value()) + "U;\n");
|
|
m_foundTop = true;
|
|
}
|
|
|
|
void addComment(AstTextBlock* txtp, FileLine* fl, const string& comment) {
|
|
txtp->add(new AstComment{fl, comment});
|
|
}
|
|
|
|
void configSection(AstNodeModule* modp, AstTextBlock* txtp, FileLine* fl) {
|
|
txtp->add("\n`ifdef VERILATOR\n");
|
|
txtp->add("`verilator_config\n");
|
|
|
|
txtp->add("profile_data -hier-dpi \"" + m_libName + "_protectlib_combo_update\" -cost 64'd"
|
|
+ std::to_string(v3Global.currentHierBlockCost()) + "\n");
|
|
txtp->add("profile_data -hier-dpi \"" + m_libName + "_protectlib_seq_update\" -cost 64'd"
|
|
+ std::to_string(v3Global.currentHierBlockCost()) + "\n");
|
|
|
|
// Mark remaining NBA protectlib wrapper DPIs as non-hazardous by deliberately forwarding
|
|
// them with non-zero cost.
|
|
// Also, specify hierarchical workers for those tasks for scheduling.
|
|
txtp->add("profile_data -hier-dpi \"" + m_libName
|
|
+ "_protectlib_combo_ignore\" -cost 64'd1\n");
|
|
|
|
txtp->add("hier_workers -hier-dpi \"" + m_libName
|
|
+ "_protectlib_combo_update\" -workers 16'd"
|
|
+ std::to_string(V3Control::getHierWorkers(m_libName)) + "\n");
|
|
txtp->add("hier_workers -hier-dpi \"" + m_libName
|
|
+ "_protectlib_seq_update\" -workers 16'd"
|
|
+ std::to_string(V3Control::getHierWorkers(m_libName)) + "\n");
|
|
// No workers for combo_ignore
|
|
txtp->add("`verilog\n");
|
|
txtp->add("`endif\n");
|
|
}
|
|
|
|
void hashComment(AstTextBlock* txtp, FileLine* fl) {
|
|
addComment(txtp, fl, "Checks to make sure the .sv wrapper and library agree");
|
|
}
|
|
|
|
void initialComment(AstTextBlock* txtp, FileLine* fl) {
|
|
addComment(txtp, fl, "Creates an instance of the library module at initial-time");
|
|
addComment(txtp, fl, "(one for each instance in the user's design) also evaluates");
|
|
addComment(txtp, fl, "the library module's initial process");
|
|
}
|
|
|
|
void comboComment(AstTextBlock* txtp, FileLine* fl) {
|
|
addComment(txtp, fl, "Updates all non-clock inputs and retrieves the results");
|
|
}
|
|
|
|
void seqComment(AstTextBlock* txtp, FileLine* fl) {
|
|
addComment(txtp, fl, "Updates all clocks and retrieves the results");
|
|
}
|
|
|
|
void comboIgnoreComment(AstTextBlock* txtp, FileLine* fl) {
|
|
addComment(txtp, fl, "Need to convince some simulators that the input to the module");
|
|
addComment(txtp, fl, "must be evaluated before evaluating the clock edge");
|
|
}
|
|
|
|
void finalComment(AstTextBlock* txtp, FileLine* fl) {
|
|
addComment(txtp, fl, "Evaluates the library module's final process");
|
|
}
|
|
|
|
void createSvFile(FileLine* fl, AstNodeModule* modp) {
|
|
// Comments
|
|
AstTextBlock* const txtp = new AstTextBlock{fl};
|
|
addComment(txtp, fl, "Wrapper module for DPI protected library");
|
|
addComment(txtp, fl,
|
|
"This module requires lib" + m_libName + ".a or lib" + m_libName
|
|
+ ".so to work");
|
|
addComment(txtp, fl,
|
|
"See instructions in your simulator for how"
|
|
" to use DPI libraries\n");
|
|
|
|
// Module declaration
|
|
m_modPortsp = new AstTextBlock{fl, "module " + m_libName + " (\n", ", ", ");\n\n"};
|
|
txtp->add(m_modPortsp);
|
|
|
|
// Timescale
|
|
if (v3Global.opt.hierChild() && v3Global.rootp()->timescaleSpecified()) {
|
|
// Emit timescale for hierarchical Verilation if input HDL specifies timespec
|
|
txtp->add("timeunit "s + modp->timeunit().ascii() + ";\n");
|
|
txtp->add("timeprecision "s + +v3Global.rootp()->timeprecision().ascii() + ";\n");
|
|
} else {
|
|
addComment(txtp, fl,
|
|
"Precision of submodule"
|
|
" (commented out to avoid requiring timescale on all modules)");
|
|
addComment(txtp, fl, "timeunit "s + v3Global.rootp()->timeunit().ascii() + ";");
|
|
addComment(txtp, fl,
|
|
"timeprecision "s + v3Global.rootp()->timeprecision().ascii() + ";\n");
|
|
}
|
|
|
|
// DPI declarations
|
|
hashComment(txtp, fl);
|
|
txtp->add("import \"DPI-C\" function void " + m_libName
|
|
+ "_protectlib_check_hash(int protectlib_hash__V);\n\n");
|
|
initialComment(txtp, fl);
|
|
txtp->add("import \"DPI-C\" function chandle " + m_libName
|
|
+ "_protectlib_create(string scope__V);\n\n");
|
|
comboComment(txtp, fl);
|
|
m_comboPortsp = new AstTextBlock{fl, //
|
|
"import \"DPI-C\" function longint " + m_libName
|
|
+ "_protectlib_combo_update(\n", //
|
|
", ", //
|
|
");\n\n"};
|
|
m_comboPortsp->add("chandle handle__V\n");
|
|
txtp->add(m_comboPortsp);
|
|
seqComment(txtp, fl);
|
|
if (m_hasClk) {
|
|
m_seqPortsp = new AstTextBlock{fl, //
|
|
"import \"DPI-C\" function longint " + m_libName
|
|
+ "_protectlib_seq_update(\n", //
|
|
", ", //
|
|
");\n\n"};
|
|
m_seqPortsp->add("chandle handle__V\n");
|
|
txtp->add(m_seqPortsp);
|
|
}
|
|
comboIgnoreComment(txtp, fl);
|
|
m_comboIgnorePortsp = new AstTextBlock{fl, //
|
|
"import \"DPI-C\" function void " + m_libName
|
|
+ "_protectlib_combo_ignore(\n", //
|
|
", ", //
|
|
");\n\n"};
|
|
m_comboIgnorePortsp->add("chandle handle__V\n");
|
|
txtp->add(m_comboIgnorePortsp);
|
|
|
|
finalComment(txtp, fl);
|
|
txtp->add("import \"DPI-C\" function void " + m_libName
|
|
+ "_protectlib_final(chandle handle__V);\n\n");
|
|
|
|
// Local variables
|
|
// Avoid tracing handle, as it is not a stable value, so breaks vcddiff
|
|
// Likewise other internals aren't interesting to the user
|
|
txtp->add("// verilator tracing_off\n");
|
|
|
|
txtp->add("chandle handle__V;\n");
|
|
txtp->add("time last_combo_seqnum__V;\n");
|
|
if (m_hasClk) txtp->add("time last_seq_seqnum__V;\n");
|
|
txtp->add("\n");
|
|
|
|
m_comboDeclsp = new AstTextBlock{fl};
|
|
txtp->add(m_comboDeclsp);
|
|
m_seqDeclsp = new AstTextBlock{fl};
|
|
txtp->add(m_seqDeclsp);
|
|
m_tmpDeclsp = new AstTextBlock{fl};
|
|
txtp->add(m_tmpDeclsp);
|
|
|
|
// CPP hash value
|
|
addComment(txtp, fl, "Hash value to make sure this file and the corresponding");
|
|
addComment(txtp, fl, "library agree");
|
|
m_hashValuep = new AstTextBlock{fl, "localparam int protectlib_hash__V = 32'd", "", "\n"};
|
|
txtp->add(m_hashValuep);
|
|
|
|
// Initial
|
|
txtp->add("initial begin\n");
|
|
txtp->add(m_libName + "_protectlib_check_hash(protectlib_hash__V);\n");
|
|
txtp->add("handle__V = " + m_libName
|
|
+ "_protectlib_create"
|
|
"($sformatf(\"%m\"));\n");
|
|
txtp->add("end\n\n");
|
|
|
|
// Combinatorial process
|
|
addComment(txtp, fl, "Combinatorially evaluate changes to inputs");
|
|
txtp->add("always_comb begin\n");
|
|
m_comboParamsp = new AstTextBlock{fl, //
|
|
"last_combo_seqnum__V = " + m_libName
|
|
+ "_protectlib_combo_update(\n", //
|
|
",\n", //
|
|
"\n);\n"};
|
|
m_comboParamsp->add("handle__V");
|
|
txtp->add(m_comboParamsp);
|
|
txtp->add("end\n\n");
|
|
|
|
// Sequential process
|
|
if (m_hasClk) {
|
|
addComment(txtp, fl, "Evaluate clock edges");
|
|
m_clkSensp = new AstTextBlock{fl, "always @(", " or ", ")"};
|
|
txtp->add(m_clkSensp);
|
|
txtp->add(" begin\n");
|
|
m_comboIgnoreParamsp
|
|
= new AstTextBlock{fl, //
|
|
m_libName + "_protectlib_combo_ignore(\n", ",\n", "\n);\n"};
|
|
m_comboIgnoreParamsp->add("handle__V");
|
|
txtp->add(m_comboIgnoreParamsp);
|
|
m_seqParamsp = new AstTextBlock{fl, //
|
|
"last_seq_seqnum__V <= " + m_libName
|
|
+ "_protectlib_seq_update(\n", //
|
|
",\n", //
|
|
"\n);\n"};
|
|
m_seqParamsp->add("handle__V");
|
|
txtp->add(m_seqParamsp);
|
|
m_nbAssignsp = new AstTextBlock{fl};
|
|
txtp->add(m_nbAssignsp);
|
|
txtp->add("end\n\n");
|
|
}
|
|
|
|
// Select between combinatorial and sequential results
|
|
addComment(txtp, fl, "Select between combinatorial and sequential results");
|
|
txtp->add("always_comb begin\n");
|
|
if (m_hasClk) {
|
|
txtp->add("if (last_seq_seqnum__V > last_combo_seqnum__V) begin\n");
|
|
m_seqAssignsp = new AstTextBlock{fl};
|
|
txtp->add(m_seqAssignsp);
|
|
txtp->add("end else begin\n");
|
|
m_comboAssignsp = new AstTextBlock{fl};
|
|
txtp->add(m_comboAssignsp);
|
|
txtp->add("end\n");
|
|
} else {
|
|
m_comboAssignsp = new AstTextBlock{fl};
|
|
txtp->add(m_comboAssignsp);
|
|
}
|
|
txtp->add("end\n\n");
|
|
|
|
// Final
|
|
txtp->add("final " + m_libName + "_protectlib_final(handle__V);\n\n");
|
|
|
|
txtp->add("endmodule\n");
|
|
|
|
configSection(modp, txtp, fl);
|
|
|
|
m_vfilep->tblockp(txtp);
|
|
}
|
|
|
|
void castPtr(FileLine* fl, AstTextBlock* txtp) {
|
|
txtp->add(m_topName
|
|
+ "_container* const handlep__V = " // LCOV_EXCL_LINE // lcov bug
|
|
"static_cast<"
|
|
+ m_topName + "_container*>(vhandlep__V);\n");
|
|
}
|
|
|
|
void createCppFile(FileLine* fl) {
|
|
// Comments
|
|
AstTextBlock* const txtp = new AstTextBlock{fl};
|
|
addComment(txtp, fl, "Wrapper functions for DPI protected library\n");
|
|
|
|
// Includes
|
|
txtp->add("#include \"" + m_topName + ".h\"\n");
|
|
txtp->add("#include \"verilated_dpi.h\"\n\n");
|
|
txtp->add("#include <cstdio>\n");
|
|
txtp->add("#include <cstdlib>\n\n");
|
|
|
|
// Verilated module plus sequence number
|
|
addComment(txtp, fl, "Container class to house verilated object and sequence number");
|
|
txtp->add("class " + m_topName + "_container: public " + m_topName + " {\n");
|
|
txtp->add("public:\n");
|
|
txtp->add("long long m_seqnum;\n");
|
|
txtp->add(m_topName + "_container(const char* scopep__V):\n");
|
|
txtp->add(m_topName + "(scopep__V) {}\n");
|
|
txtp->add("};\n\n");
|
|
|
|
// Extern C
|
|
txtp->add("extern \"C\" {\n\n");
|
|
|
|
// Hash check
|
|
hashComment(txtp, fl);
|
|
txtp->add("void " + m_libName
|
|
+ "_protectlib_check_hash"
|
|
"(int protectlib_hash__V) {\n");
|
|
m_cHashValuep = new AstTextBlock{fl, "const int expected_hash__V = ", "", ""};
|
|
txtp->add(m_cHashValuep);
|
|
txtp->add(/**/ "if (protectlib_hash__V != expected_hash__V) {\n");
|
|
txtp->add(/****/ "fprintf(stderr, \"%%Error: cannot use " + m_libName
|
|
+ " library, "
|
|
"Verliog (%u) and library (%u) hash values do not "
|
|
"agree\\n\", protectlib_hash__V, expected_hash__V);\n");
|
|
txtp->add(/****/ "std::exit(EXIT_FAILURE);\n");
|
|
txtp->add(/**/ "}\n");
|
|
txtp->add("}\n\n");
|
|
|
|
// Initial
|
|
initialComment(txtp, fl);
|
|
txtp->add("void* " + m_libName + "_protectlib_create(const char* scopep__V) {\n");
|
|
txtp->add(/**/ m_topName + "_container* const handlep__V = new " + m_topName
|
|
+ "_container{scopep__V};\n");
|
|
txtp->add(/**/ "return handlep__V;\n");
|
|
txtp->add("}\n\n");
|
|
|
|
// Updates
|
|
comboComment(txtp, fl);
|
|
m_cComboParamsp = new AstTextBlock{
|
|
fl, "long long " + m_libName + "_protectlib_combo_update(\n", ",\n", "\n) {\n"};
|
|
m_cComboParamsp->add("void* vhandlep__V");
|
|
txtp->add(m_cComboParamsp);
|
|
m_cComboInsp = new AstTextBlock{fl};
|
|
castPtr(fl, m_cComboInsp);
|
|
txtp->add(m_cComboInsp);
|
|
txtp->add("handlep__V->eval();\n");
|
|
m_cComboOutsp = new AstTextBlock{fl};
|
|
txtp->add(m_cComboOutsp);
|
|
txtp->add("return handlep__V->m_seqnum++;\n");
|
|
txtp->add("}\n\n");
|
|
|
|
if (m_hasClk) {
|
|
seqComment(txtp, fl);
|
|
m_cSeqParamsp = new AstTextBlock{
|
|
fl, "long long " + m_libName + "_protectlib_seq_update(\n", ",\n", "\n) {\n"};
|
|
m_cSeqParamsp->add("void* vhandlep__V");
|
|
txtp->add(m_cSeqParamsp);
|
|
m_cSeqClksp = new AstTextBlock{fl};
|
|
castPtr(fl, m_cSeqClksp);
|
|
txtp->add(m_cSeqClksp);
|
|
txtp->add("handlep__V->eval();\n");
|
|
m_cSeqOutsp = new AstTextBlock{fl};
|
|
txtp->add(m_cSeqOutsp);
|
|
txtp->add("return handlep__V->m_seqnum++;\n");
|
|
txtp->add("}\n\n");
|
|
}
|
|
|
|
comboIgnoreComment(txtp, fl);
|
|
m_cIgnoreParamsp = new AstTextBlock{
|
|
fl, "void " + m_libName + "_protectlib_combo_ignore(\n", ",\n", "\n)\n{ }\n\n"};
|
|
m_cIgnoreParamsp->add("void* vhandlep__V");
|
|
txtp->add(m_cIgnoreParamsp);
|
|
|
|
// Final
|
|
finalComment(txtp, fl);
|
|
txtp->add("void " + m_libName + "_protectlib_final(void* vhandlep__V) {\n");
|
|
castPtr(fl, txtp);
|
|
txtp->add(/**/ "handlep__V->final();\n");
|
|
txtp->add(/**/ "delete handlep__V;\n");
|
|
txtp->add("}\n\n");
|
|
|
|
txtp->add("}\n");
|
|
m_cfilep->tblockp(txtp);
|
|
}
|
|
|
|
void visit(AstVar* nodep) override {
|
|
if (!nodep->isIO()) return;
|
|
if (nodep->direction() == VDirection::INPUT) {
|
|
if (nodep->isPrimaryClock()) {
|
|
UASSERT_OBJ(m_hasClk, nodep, "checkIfClockExists() didn't find this clock");
|
|
handleClock(nodep);
|
|
} else {
|
|
handleDataInput(nodep);
|
|
}
|
|
} else if (nodep->direction() == VDirection::OUTPUT) {
|
|
handleOutput(nodep);
|
|
} else {
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: --lib-create port direction: "
|
|
<< nodep->direction().ascii());
|
|
}
|
|
}
|
|
|
|
void visit(AstNode*) override {}
|
|
|
|
string cInputConnection(AstVar* varp) {
|
|
return V3Task::assignDpiToInternal("handlep__V->" + varp->name(), varp);
|
|
}
|
|
|
|
void handleClock(AstVar* varp) {
|
|
handleInput(varp);
|
|
m_seqPortsp->add(varp->cloneTree(false));
|
|
if (m_hasClk) {
|
|
const std::string pname = varp->prettyName();
|
|
m_seqParamsp->add(pname);
|
|
m_clkSensp->add(pname);
|
|
}
|
|
m_cSeqParamsp->add(varp->dpiArgType(true, false));
|
|
m_cSeqClksp->add(cInputConnection(varp));
|
|
}
|
|
|
|
void handleDataInput(AstVar* varp) {
|
|
handleInput(varp);
|
|
m_comboPortsp->add(varp->cloneTree(false));
|
|
m_comboParamsp->add(varp->prettyName());
|
|
m_comboIgnorePortsp->add(varp->cloneTree(false));
|
|
if (m_hasClk) m_comboIgnoreParamsp->add(varp->prettyName());
|
|
m_cComboParamsp->add(varp->dpiArgType(true, false));
|
|
m_cComboInsp->add(cInputConnection(varp));
|
|
m_cIgnoreParamsp->add(varp->dpiArgType(true, false));
|
|
}
|
|
|
|
void handleInput(AstVar* varp) { m_modPortsp->add(varp->cloneTree(false)); }
|
|
|
|
static void addLocalVariable(AstTextBlock* textp, const AstVar* varp, const char* suffix) {
|
|
AstVar* const newVarp
|
|
= new AstVar{varp->fileline(), VVarType::VAR, varp->name() + suffix, varp->dtypep()};
|
|
textp->add(newVarp);
|
|
}
|
|
|
|
void handleOutput(AstVar* const varp) {
|
|
const std::string pname = varp->prettyName();
|
|
m_modPortsp->add(varp->cloneTree(false));
|
|
m_comboPortsp->add(varp->cloneTree(false));
|
|
m_comboParamsp->add(pname + "_combo__V");
|
|
if (m_hasClk) {
|
|
m_seqPortsp->add(varp->cloneTree(false));
|
|
m_seqParamsp->add(pname + "_tmp__V");
|
|
}
|
|
|
|
addLocalVariable(m_comboDeclsp, varp, "_combo__V");
|
|
|
|
if (m_hasClk) {
|
|
addLocalVariable(m_seqDeclsp, varp, "_seq__V");
|
|
addLocalVariable(m_tmpDeclsp, varp, "_tmp__V");
|
|
|
|
m_nbAssignsp->add(pname + "_seq__V <= " + pname + "_tmp__V;\n");
|
|
m_seqAssignsp->add(pname + " = " + pname + "_seq__V;\n");
|
|
}
|
|
m_comboAssignsp->add(pname + " = " + pname + "_combo__V;\n");
|
|
m_cComboParamsp->add(varp->dpiArgType(true, false));
|
|
m_cComboOutsp->add(V3Task::assignInternalToDpi(varp, true, "", "", "handlep__V->") + "\n");
|
|
if (m_hasClk) {
|
|
m_cSeqParamsp->add(varp->dpiArgType(true, false));
|
|
m_cSeqOutsp->add(V3Task::assignInternalToDpi(varp, true, "", "", "handlep__V->")
|
|
+ "\n");
|
|
}
|
|
}
|
|
|
|
static bool checkIfClockExists(const AstNodeModule* modp) {
|
|
for (const AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
|
if (const AstVar* const varp = VN_CAST(stmtp, Var)) {
|
|
if (varp->direction() == VDirection::INPUT && varp->isPrimaryClock()) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
explicit ProtectVisitor(AstNode* nodep)
|
|
: m_libName{v3Global.opt.libCreate()}
|
|
, m_topName{v3Global.opt.prefix()} {
|
|
iterate(nodep);
|
|
}
|
|
};
|
|
|
|
//######################################################################
|
|
// ProtectLib class functions
|
|
|
|
void V3ProtectLib::protect() {
|
|
UINFO(2, __FUNCTION__ << ":");
|
|
ProtectVisitor{v3Global.rootp()};
|
|
}
|