Support hierarchical verilation using protect lib (#2206)

This commit is contained in:
Yutetsu TAKATSUKASA 2020-08-15 22:43:53 +09:00 committed by GitHub
parent f3b28c5c74
commit 953a442827
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 2283 additions and 43 deletions

View File

@ -327,6 +327,7 @@ arguments.
--generate-key Create random key for --protect-key
--getenv <var> Get environment variable with defaults
--help Display this help
--hierarchical Enable hierarchical Verilation
-I<dir> Directory to search for includes
-j <jobs> Parallelism for --build
--gate-stmts <value> Tune gate optimizer depth
@ -951,6 +952,12 @@ immediately. This can be useful in makefiles. See also -V, and the various
Displays this message and program version and exits.
=item --hierarchical
Enable hierarchical Verilation otherwise /*verilator hier_block*/ metacomment
is ignored.
See L</"HIERARCHICAL VERILATION">.
=item -II<dir>
See -y.
@ -2208,10 +2215,13 @@ For --make gmake, it creates:
{prefix}.mk // Make include file for compiling
{prefix}_classes.mk // Make include file with class names
{prefix}_hier.mk // Make file for hierarchy blocks
{prefix}_hierMkArgs.f // Arguments for hierarchical Verilation.
For --make cmake, it creates:
{prefix}.cmake // CMake include script for compiling
{prefix}_hierCMakeArgs.f // Arguments for hierarchical Verilation.
For -cc and -sc mode, it also creates:
@ -2223,6 +2233,13 @@ For -cc and -sc mode, it also creates:
{prefix}{each_verilog_module}.h // Lower level internal header files
{prefix}{each_verilog_module}{__n}.cpp // Additional lower C++ files (--output-split)
For --hierarchy mode, it creates:
{prefix}__hierVer.d // Make dependencies of the top module in the
hierarchical Verilation
{prefix}__hier.dir/ // Directory to store .dot, .vpp, .tree of the
top module in the hierarchical Verilation
V{hier_block}/ // Directory to Verilate each hierarchy block
In certain debug and other modes, it also creates:
{prefix}.xml // XML tree information (--xml)
@ -2830,6 +2847,71 @@ here, use DIRECTORY or PREFIX.
=back
=head1 HIERARCHICAL VERILATION
Large designs may take long (e.g. 10+ minutes) and huge memory (e.g. 100+GB)
to Verilate. One workaround is hierarchical Verilation, it is to Verilate
each moderate size of building blocks and finally combine the building blocks.
The building block will be called "hierarchy block" later.
The current hierarchical Verilation is based on protect-lib. Each hierarchy
block is Verilated to protect-lib. User modules of the hierarchy blocks will see
a tiny wrapper generated by protect-lib instead of the actual design.
=head2 Usage
All user need to do is mark moderate size of module as hierarchy block and pass
--hierarchical option to verilator command.
There are two ways to mark a module:
a) Write /* verilator hier_block */ metacomment in HDL code
See L</"LANGUAGE EXTENSIONS"> for more detail.
b) add hier_block line in the configuration file.
See C<hier_block> in L</"CONFIGURATION FILES"> for example.
You don't have to take care of hierarchical blocks when compiling
Verilated C++ code. You can compile as usual.
make -C obj_dir -f Vtop_module_name.mk
See also "Overlapping Verilation and compilation" to get executable quickly.
=head2 Limitations
Because hierarchy blocks are Verilated to protect-lib, they have some
limitations such as:
The block cannot be accessed using dot (.) from upper module.
Signals in the block cannot be traced.
Modport cannot be used at the hirarchical block boundary.
On the other hand, the following usage is supported.
- Nested hierarchy block. A hierarchy block may instantiate other
hierarchy blocks.
- Parameterized hierarchy block. Parameters of a hierarchy block can be
overridden using #(.param_name(value)) construct.
The simulation speed may not be as fast as flat Verilation, in which
all modules are globally scheduled.
=head2 Overlapping Verilation and compilation
Verilator needs to run N + 2 times in hierarchical Verilation, where N is
the number of hierarchy blocks.
1 of 2 is for the top module which refers wrappers of all other hierarchy
blocks.
The other 1 of 2 is the initial run that searches modules marked with
/*verilator hier_block*/ metacomment and creates a plan and write in
(prefix)_hier.mk.
This initial run internally invokes other N + 1 runs, so you don't have
to care about these N + 1 times of run.
If -j <jobs> option is specified, Verilation for hierarchy blocks
runs in parallel.
If --build option is also specified, C++ compilation also runs as soon as
a hierarchy block is Verilated. C++ compilation and Verilation for
other hierarchy blocks run simultaneously.
=head1 MULTITHREADING
@ -3049,6 +3131,12 @@ formally prove the directive to be true, or failing that, will insert
the appropriate code to detect failing cases at simulation runtime and
print an "Assertion failed" error message.
=item hier_block -module "<modulename>"
Specifies that the module is a unit of hierarchical Verilation.
Note that the setting is ignored unless --hierachical option is specified.
See L</"HIERARCHICAL VERILATION"> for more information.
=item inline -module "<modulename>"
Specifies the module may be inlined into any modules that use this module.
@ -3397,6 +3485,17 @@ Specifies that following lines of code should have coverage re-enabled (if
appropriate --coverage flags are passed) after being disabled earlier with
/*verilator coverage_off*/.
=item /*verilator hier_block*/
Specifies that the module is a unit of hierarchical Verilation.
This metacomment must be between
"module module_name(...);" and "endmodule".
The module will not be inlined nor uniquified for each instance in
hierarchical Verilation.
Note that the metacomment is ignored unless --hierachical option is specified.
See L</"HIERARCHICAL VERILATION"> for more information.
=item /*verilator inline_module*/
Specifies the module the comment appears in may be inlined into any modules
@ -4348,6 +4447,10 @@ Deprecated and no longer used as a warning. Used to indicate that the
specified signal was is generated inside the model, and also being used as
a clock.
=item HIERBLOCK
Warns that the top module is marked as a hierarchy block
by hier_block metacomment, which is not legal. This setting on the top module will be ignored.
=item IFDEPTH
Warns that if/if else statements have exceeded the depth specified with

View File

@ -200,9 +200,25 @@ else
VK_OBJS += $(VK_FAST_OBJS) $(VK_SLOW_OBJS)
endif
$(VM_PREFIX)__ALL.a: $(VK_OBJS)
$(AR) -cr $@ $^
$(RANLIB) $@
# When archiving just objects (.o), single $(AR) run is enough.
# When merging objects (.o) and archives (.a), the following step is taken.
# 1. create a temporary archive ($*__tmp.a) which contains only .o
# 2. create a thin archive that refers all archives including $*__tmp.a
# 3. convert the thin archive to the ordinal archive
%.a:
if test $(words $(filter %.a,$^)) -eq 0; then \
$(AR) -cr $@ $^; \
$(RANLIB) $@; \
else \
$(RM) -f $*__tmp.a; \
$(AR) -cr $*__tmp.a $(filter-out %.a,$^); \
$(AR) -cqT $@ $*__tmp.a $(filter %.a,$^); \
printf "create $@\n addlib $@\n save\\n end" | $(AR) -M; \
$(RM) -f $*__tmp.a; \
fi
$(VM_PREFIX)__ALL.a: $(VK_OBJS) $(VM_HIER_LIBS)
######################################################################
### Compile rules

View File

@ -203,6 +203,7 @@ RAW_OBJS = \
V3GraphPathChecker.o \
V3GraphTest.o \
V3Hashed.o \
V3HierBlock.o \
V3Inline.o \
V3Inst.o \
V3InstrCount.o \

View File

@ -165,6 +165,7 @@ public:
enum en {
ILLEGAL,
COVERAGE_BLOCK_OFF,
HIER_BLOCK,
INLINE_MODULE,
NO_INLINE_MODULE,
NO_INLINE_TASK,
@ -2788,6 +2789,7 @@ private:
bool m_modTrace : 1; // Tracing this module
bool m_inLibrary : 1; // From a library, no error if not used, never top level
bool m_dead : 1; // LinkDot believes is dead; will remove in Dead visitors
bool m_hierBlock : 1; // Hiearchical Block marked by HIER_BLOCK pragma
bool m_internal : 1; // Internally created
bool m_recursive : 1; // Recursive module
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise NULL
@ -2806,6 +2808,7 @@ public:
, m_modTrace(false)
, m_inLibrary(false)
, m_dead(false)
, m_hierBlock(false)
, m_internal(false)
, m_recursive(false)
, m_recursiveClone(false)
@ -2840,6 +2843,8 @@ public:
bool modTrace() const { return m_modTrace; }
void dead(bool flag) { m_dead = flag; }
bool dead() const { return m_dead; }
void hierBlock(bool flag) { m_hierBlock = flag; }
bool hierBlock() const { return m_hierBlock; }
void internal(bool flag) { m_internal = flag; }
bool internal() const { return m_internal; }
void recursive(bool flag) { m_recursive = flag; }

View File

@ -22,7 +22,7 @@
#include "V3Global.h"
#include "V3Graph.h"
#include "V3PartitionGraph.h" // Just for mtask dumping
#include "V3String.h" // For VString::parseDouble
#include "V3String.h"
#include "V3EmitCBase.h"
#include <iomanip>

View File

@ -1851,6 +1851,7 @@ private:
bool m_isDpiOpenArray : 1; // DPI import open array
bool m_noReset : 1; // Do not do automated reset/randomization
bool m_noSubst : 1; // Do not substitute out references
bool m_overridenParam : 1; // Overridden parameter by #(...) or defparam
bool m_trace : 1; // Trace this variable
VLifetime m_lifetime; // Lifetime
VVarAttrClocker m_attrClocker;
@ -1887,6 +1888,7 @@ private:
m_isDpiOpenArray = false;
m_noReset = false;
m_noSubst = false;
m_overridenParam = false;
m_trace = false;
m_attrClocker = VVarAttrClocker::CLOCKER_UNKNOWN;
}
@ -2036,6 +2038,8 @@ public:
bool noReset() const { return m_noReset; }
void noSubst(bool flag) { m_noSubst = flag; }
bool noSubst() const { return m_noSubst; }
void overriddenParam(bool flag) { m_overridenParam = flag; }
bool overriddenParam() const { return m_overridenParam; }
void trace(bool flag) { m_trace = flag; }
// METHODS
virtual void name(const string& name) { m_name = name; }

View File

@ -329,7 +329,12 @@ private:
if (nodep->isSigPublic()) return false; // Can't elim publics!
if (nodep->isIO() || nodep->isClassMember()) return false;
if (nodep->isTemp() && !nodep->isTrace()) return true;
if (nodep->isParam() && !nodep->isTrace() && !v3Global.opt.xmlOnly()) return true;
if (nodep->isParam()) {
const bool overriddenForHierBlock
= m_modp && m_modp->hierBlock() && nodep->overriddenParam();
if (!nodep->isTrace() && !overriddenForHierBlock && !v3Global.opt.xmlOnly())
return true;
}
return m_elimUserVars; // Post-Trace can kill most anything
}

View File

@ -21,6 +21,7 @@
#include "V3Os.h"
#include "V3EmitCMake.h"
#include "V3EmitCBase.h"
#include "V3HierBlock.h"
#include <memory>
@ -39,11 +40,11 @@ class CMakeEmitter {
string s;
if (strs.begin() != strs.end()) {
s.append("\"");
s.append(*strs.begin());
s.append(VString::quoteAny(*strs.begin(), '"', '\\'));
s.append("\"");
for (typename List::const_iterator it = ++strs.begin(); it != strs.end(); ++it) {
s.append(" \"");
s.append(*it);
s.append(VString::quoteAny(*it, '"', '\\'));
s.append("\"");
}
}
@ -195,6 +196,57 @@ class CMakeEmitter {
*of << "# User .cpp files (from .cpp's on Verilator command line)\n";
cmake_set_raw(*of, name + "_USER_CLASSES", deslash(cmake_list(v3Global.opt.cppFiles())));
if (const V3HierBlockPlan* planp = v3Global.hierPlanp()) {
*of << "# Verilate hierarchical blocks\n";
// Sorted hierarchical blocks in order of leaf-first.
const V3HierBlockPlan::HierVector& hierBlocks = planp->hierBlocksSorted();
const string topTarget = v3Global.opt.protectLib().empty() ? v3Global.opt.prefix()
: v3Global.opt.protectLib();
for (V3HierBlockPlan::HierVector::const_iterator it = hierBlocks.begin();
it != hierBlocks.end(); ++it) {
const V3HierBlock* hblockp = *it;
const V3HierBlock::HierBlockSet& children = hblockp->children();
const string prefix = hblockp->hierPrefix();
*of << "add_library(" << prefix << " STATIC)\n";
*of << "target_link_libraries(" << topTarget << " PRIVATE " << prefix << ")\n";
if (!children.empty()) {
*of << "target_link_libraries(" << prefix << " INTERFACE";
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
child != children.end(); ++child) {
*of << " " << (*child)->hierPrefix();
}
*of << ")\n";
}
*of << "verilate(" << prefix << " PREFIX " << prefix << " TOP_MODULE "
<< hblockp->modp()->name() << " DIRECTORY "
<< deslash("${CMAKE_CURRENT_BINARY_DIR}/" + prefix) << " SOURCES ";
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
child != children.end(); ++child) {
*of << deslash(" ${CMAKE_CURRENT_BINARY_DIR}/" + (*child)->hierWrapper(true));
}
*of << " ";
const V3StringList& vFiles = v3Global.opt.vFiles();
for (V3StringList::const_iterator it = vFiles.begin(); it != vFiles.end(); ++it) {
*of << V3Os::filenameRealPath(*it);
}
*of << " VERILATOR_ARGS ";
*of << "-f " << deslash(hblockp->commandArgsFileName(true))
<< " -CFLAGS -fPIC" // hierarchical block will be static, but may be linked
// with .so
<< ")\n";
}
*of << "\n# Verilate the top module that refers protect-lib wrappers of above\n";
*of << "verilate(" << topTarget << " PREFIX " << v3Global.opt.prefix()
<< " TOP_MODULE " << v3Global.rootp()->topModulep()->name() << " DIRECTORY "
<< deslash("${CMAKE_CURRENT_BINARY_DIR}/" + topTarget + ".dir") << " SOURCES ";
for (V3HierBlockPlan::const_iterator it = planp->begin(); it != planp->end(); ++it) {
*of << deslash(" ${CMAKE_CURRENT_BINARY_DIR}/" + it->second->hierWrapper(true));
}
*of << " " << deslash(cmake_list(v3Global.opt.vFiles()));
*of << " VERILATOR_ARGS ";
*of << "-f " << deslash(planp->topCommandArgsFileName(true));
*of << ")\n";
}
}
public:

View File

@ -18,6 +18,7 @@
#include "verilatedos.h"
#include "V3Global.h"
#include "V3HierBlock.h"
#include "V3Os.h"
#include "V3EmitMk.h"
#include "V3EmitCBase.h"
@ -94,7 +95,10 @@ public:
of.puts(support == 2 ? "VM_GLOBAL" : support == 1 ? "VM_SUPPORT" : "VM_CLASSES");
of.puts(slow ? "_SLOW" : "_FAST");
of.puts(" += \\\n");
if (support == 2 && !slow) {
if (support == 2 && v3Global.opt.hierChild()) {
// Do nothing because VM_GLOBAL is necessary per executable. Top module will
// have them.
} else if (support == 2 && !slow) {
putMakeClassEntry(of, "verilated.cpp");
if (v3Global.dpi()) { putMakeClassEntry(of, "verilated_dpi.cpp"); }
if (v3Global.opt.vpi()) { putMakeClassEntry(of, "verilated_vpi.cpp"); }
@ -224,6 +228,10 @@ public:
of.puts("\n### Default rules...\n");
of.puts("# Include list of all generated classes\n");
of.puts("include " + v3Global.opt.prefix() + "_classes.mk\n");
if (v3Global.opt.hierTop()) {
of.puts("# Include rules for hierarchical blocks\n");
of.puts("include " + v3Global.opt.prefix() + "_hier.mk\n");
}
of.puts("# Include global rules\n");
of.puts("include $(VERILATOR_ROOT)/include/verilated.mk\n");
@ -241,17 +249,17 @@ public:
of.puts("\n### Link rules... (from --exe)\n");
of.puts(v3Global.opt.exeName()
+ ": $(VK_USER_OBJS) $(VK_GLOBAL_OBJS) $(VM_PREFIX)__ALL.a\n");
+ ": $(VK_USER_OBJS) $(VK_GLOBAL_OBJS) $(VM_PREFIX)__ALL.a $(VM_HIER_LIBS)\n");
of.puts("\t$(LINK) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) $(LIBS) $(SC_LIBS) -o $@\n");
of.puts("\n");
}
if (!v3Global.opt.protectLib().empty()) {
const string protectLibDeps
= "$(VK_OBJS) $(VK_GLOBAL_OBJS) " + v3Global.opt.protectLib() + ".o";
const string protectLibDeps = "$(VK_OBJS) $(VK_GLOBAL_OBJS) "
+ v3Global.opt.protectLib() + ".o $(VM_HIER_LIBS)";
of.puts("\n### Library rules from --protect-lib\n");
// The rule to create .a is defined in verilated.mk, so just define dependency here.
of.puts(v3Global.opt.protectLibName(false) + ": " + protectLibDeps + "\n");
of.puts("\tar rc $@ $^\n");
of.puts("\n");
of.puts(v3Global.opt.protectLibName(true) + ": " + protectLibDeps + "\n");
of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -shared -o $@ $^\n");
@ -273,6 +281,133 @@ public:
virtual ~EmitMk() {}
};
//######################################################################
class EmitMkHierVerilation {
const V3HierBlockPlan* const m_planp;
const string m_makefile; // path of this makefile
void emitCommonOpts(V3OutMkFile& of) const {
const string cwd = V3Os::filenameRealPath(".");
of.puts("# Verilation of hierarchical blocks are executed in this directory\n");
of.puts("VM_HIER_RUN_DIR := " + cwd + "\n");
of.puts("# Common options for hierarchical blocks\n");
const string fullpath_bin = V3Os::filenameRealPath(v3Global.opt.bin());
const string perl_wrapper = V3Os::filenameDir(fullpath_bin) + "/verilator";
of.puts("VM_HIER_VERILATOR := " + perl_wrapper + "\n");
of.puts("VM_HIER_INPUT_FILES := \\\n");
const V3StringList& vFiles = v3Global.opt.vFiles();
for (V3StringList::const_iterator it = vFiles.begin(); it != vFiles.end(); ++it) {
of.puts("\t" + V3Os::filenameRealPath(*it) + " \\\n");
}
of.puts("\n");
const V3StringSet& libraryFiles = v3Global.opt.libraryFiles();
of.puts("VM_HIER_VERILOG_LIBS := \\\n");
for (V3StringSet::const_iterator it = libraryFiles.begin(); it != libraryFiles.end();
++it) {
of.puts("\t" + V3Os::filenameRealPath(*it) + " \\\n");
}
of.puts("\n");
}
void emitOpts(V3OutMkFile& of, const V3StringList& opts) const {
for (V3StringList::const_iterator it = opts.begin(); it != opts.end(); ++it) {
of.puts("\t\t" + *it + " \\\n");
}
}
void emitLaunchVerilator(V3OutMkFile& of, const string& argsFile) const {
of.puts("\t@$(MAKE) -C $(VM_HIER_RUN_DIR) -f " + m_makefile
+ " hier_launch_verilator \\\n");
of.puts("\t\tVM_HIER_LAUNCH_VERILATOR_ARGSFILE=\"" + argsFile + "\"\n");
}
void emit(V3OutMkFile& of) const {
of.puts("# Hierarchical Verilation -*- Makefile -*-\n");
of.puts("# DESCR"
"IPTION: Verilator output: Makefile for hierarchical verilatrion\n");
of.puts("#\n");
of.puts("# The main makefile " + v3Global.opt.prefix() + ".mk calls this makefile\n");
of.puts("\n");
of.puts("ifndef VM_HIER_VERILATION_INCLUDED\n");
of.puts("VM_HIER_VERILATION_INCLUDED = 1\n\n");
of.puts(".SUFFIXES:\n");
of.puts(".PHONY: hier_build hier_verilation hier_launch_verilator\n");
of.puts("# Libraries of hierarchical blocks\n");
of.puts("VM_HIER_LIBS := \\\n");
for (V3HierBlockPlan::const_iterator it = m_planp->begin(); it != m_planp->end(); ++it) {
of.puts("\t" + it->second->hierLib(true) + " \\\n");
}
of.puts("\n");
// Build hierarchical libraries as soon as possible to get maximum parallelism
of.puts("hier_build: $(VM_HIER_LIBS) " + v3Global.opt.prefix() + ".mk\n");
of.puts("\t$(MAKE) -f " + v3Global.opt.prefix() + ".mk\n");
of.puts("hier_verilation: " + v3Global.opt.prefix() + ".mk\n");
emitCommonOpts(of);
// Instead of direct execute of "cd $(VM_HIER_RUN_DIR) && $(VM_HIER_VERILATOR)",
// call via make to get message of "Entering directory" and "Leaving directory".
// This will make some editors and IDEs happy when viewing a logfile.
of.puts("# VM_HIER_LAUNCH_VERILATOR_ARGSFILE must be passed as a command argument\n");
of.puts("hier_launch_verilator:\n");
of.puts("\t$(VM_HIER_VERILATOR) -f $(VM_HIER_LAUNCH_VERILATOR_ARGSFILE)\n");
// Top level module
{
const string argsFile = v3Global.hierPlanp()->topCommandArgsFileName(false);
of.puts("\n# Verilate the top module\n");
of.puts(v3Global.opt.prefix()
+ ".mk: $(VM_HIER_INPUT_FILES) $(VM_HIER_VERILOG_LIBS) ");
of.puts(V3Os::filenameNonDir(argsFile) + " ");
for (V3HierBlockPlan::const_iterator it = m_planp->begin(); it != m_planp->end();
++it) {
of.puts(it->second->hierWrapper(true) + " ");
}
of.puts("\n");
emitLaunchVerilator(of, argsFile);
}
// Rules to process hierarchical blocks
of.puts("\n# Verilate hierarchical blocks\n");
for (V3HierBlockPlan::const_iterator it = m_planp->begin(); it != m_planp->end(); ++it) {
const string prefix = it->second->hierPrefix();
const string argsFile = it->second->commandArgsFileName(false);
of.puts(it->second->hierGenerated(true));
of.puts(": $(VM_HIER_INPUT_FILES) $(VM_HIER_VERILOG_LIBS) ");
of.puts(V3Os::filenameNonDir(argsFile) + " ");
const V3HierBlock::HierBlockSet& children = it->second->children();
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
child != children.end(); ++child) {
of.puts((*child)->hierWrapper(true) + " ");
}
of.puts("\n");
emitLaunchVerilator(of, argsFile);
// Rule to build lib*.a
of.puts(it->second->hierLib(true));
of.puts(": ");
of.puts(it->second->hierMk(true));
of.puts(" ");
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
child != children.end(); ++child) {
of.puts((*child)->hierLib(true));
of.puts(" ");
}
of.puts("\n\t$(MAKE) -f " + it->second->hierMk(false) + " -C " + prefix);
of.puts(" VM_PREFIX=" + prefix);
of.puts("\n\n");
}
of.puts("endif # Guard\n");
}
public:
explicit EmitMkHierVerilation(const V3HierBlockPlan* planp)
: m_planp(planp)
, m_makefile(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "_hier.mk") {
V3OutMkFile of(m_makefile);
emit(of);
}
VL_DEBUG_FUNC; // Declare debug()
};
//######################################################################
// Gate class functions
@ -280,3 +415,8 @@ void V3EmitMk::emitmk() {
UINFO(2, __FUNCTION__ << ": " << endl);
EmitMk emitter;
}
void V3EmitMk::emitHierVerilation(const V3HierBlockPlan* planp) {
UINFO(2, __FUNCTION__ << ": " << endl);
EmitMkHierVerilation emitter(planp);
}

View File

@ -20,11 +20,14 @@
#include "config_build.h"
#include "verilatedos.h"
class V3HierBlockPlan;
//============================================================================
class V3EmitMk {
public:
static void emitmk();
static void emitHierVerilation(const V3HierBlockPlan* planp);
};
#endif // Guard

View File

@ -80,6 +80,7 @@ public:
DEPRECATED, // Feature will be deprecated
ENDLABEL, // End lable name mismatch
GENCLK, // Generated Clock
HIERBLOCK, // Ignored hierarchical block setting
IFDEPTH, // If statements too deep
IGNOREDRETURN, // Ignoring return value (function as task)
IMPERFECTSCH, // Imperfect schedule (disabled by default)
@ -152,7 +153,7 @@ public:
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CDCRSTLOGIC", "CLKDATA",
"CMPCONST", "COLONPLUS", "COMBDLY", "CONTASSREG",
"DEFPARAM", "DECLFILENAME", "DEPRECATED",
"ENDLABEL", "GENCLK",
"ENDLABEL", "GENCLK", "HIERBLOCK",
"IFDEPTH", "IGNOREDRETURN",
"IMPERFECTSCH", "IMPLICIT", "IMPORTSTAR", "IMPURE",
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",

View File

@ -326,6 +326,7 @@ void V3File::createMakeDir() {
if (!created) {
created = true;
V3Os::createDir(v3Global.opt.makeDir());
if (v3Global.opt.hierTop()) { V3Os::createDir(v3Global.opt.hierTopDataDir()); }
}
}

View File

@ -20,6 +20,7 @@
#include "V3Global.h"
#include "V3Ast.h"
#include "V3File.h"
#include "V3HierBlock.h"
#include "V3LinkCells.h"
#include "V3Parse.h"
#include "V3ParseSym.h"
@ -34,6 +35,10 @@ AstNetlist* V3Global::makeNetlist() {
return newp;
}
void V3Global::shutdown() {
VL_DO_CLEAR(delete m_hierPlanp, m_hierPlanp = NULL); // delete nullptr is safe
}
void V3Global::checkTree() { rootp()->checkTree(); }
void V3Global::readFiles() {

View File

@ -34,6 +34,7 @@
#include VL_INCLUDE_UNORDERED_MAP
class AstNetlist;
class V3HierBlockPlan;
//======================================================================
// Statics
@ -69,6 +70,7 @@ inline bool operator==(VWidthMinUsage::en lhs, const VWidthMinUsage& rhs) {
class V3Global {
// Globals
AstNetlist* m_rootp; // Root of entire netlist
V3HierBlockPlan* m_hierPlanp; // Hierarchical verilation plan, NULL unless hier_block
VWidthMinUsage m_widthMinUsage; // What AstNode::widthMin() is used for
int m_debugFileNumber; // Number to append to debug files created
@ -91,6 +93,7 @@ public:
// CONSTRUCTORS
V3Global()
: m_rootp(NULL) // created by makeInitNetlist() so static constructors run first
, m_hierPlanp(NULL) // Set via hierPlanp(V3HierBlockPlan*) when use hier_block
, m_widthMinUsage(VWidthMinUsage::LINT_WIDTH)
, m_debugFileNumber(0)
, m_assertDTypesResolved(false)
@ -105,6 +108,7 @@ public:
UASSERT(!m_rootp, "call once");
m_rootp = makeNetlist();
}
void shutdown(); // Release allocated resorces
// ACCESSORS (general)
AstNetlist* rootp() const { return m_rootp; }
VWidthMinUsage widthMinUsage() const { return m_widthMinUsage; }
@ -124,7 +128,7 @@ public:
if (newNumber) m_debugFileNumber = newNumber;
char digits[100];
sprintf(digits, "%03d", m_debugFileNumber);
return opt.makeDir() + "/" + opt.prefix() + "_" + digits + "_" + nameComment;
return opt.hierTopDataDir() + "/" + opt.prefix() + "_" + digits + "_" + nameComment;
}
bool needC11() const { return m_needC11; }
void needC11(bool flag) { m_needC11 = flag; }
@ -134,6 +138,11 @@ public:
void needTraceDumper(bool flag) { m_needTraceDumper = flag; }
bool dpi() const { return m_dpi; }
void dpi(bool flag) { m_dpi = flag; }
V3HierBlockPlan* hierPlanp() const { return m_hierPlanp; }
void hierPlanp(V3HierBlockPlan* plan) {
UASSERT(!m_hierPlanp, "call once");
m_hierPlanp = plan;
}
void useParallelBuild(bool flag) { m_useParallelBuild = flag; }
bool useParallelBuild() const { return m_useParallelBuild; }
const std::string& ptrToId(const void* p);

434
src/V3HierBlock.cpp Normal file
View File

@ -0,0 +1,434 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Hierarchical verilation for large designs
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2020 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
//
//*************************************************************************
// Hierarchical Verilation is useful for large designs.
// It reduces
// - time and memory for Verilation
// - compilation time especially when a hierarchical block is used many times
//
// Hierarchical Verilation internally creates protect-lib for each hierarchical block.
// Upper modules read wrapper for the protect-lib instead of the actual design.
//
// Hierarchical Verilation runs as the following step
// 1) Find modules marked by /*verilator hier_block*/ metacomment
// 2) Generate ${prefix}_hier.mk to create protected-lib for hierarchical blocks and
// final Verilation to process the top module, that refers wrappers
// 3) Call child Verilator process via ${prefix}_hier.mk
//
// There are 3 kinds of Verilator run.
// a) To create ${prefix}_hier.mk (--hierarchical)
// b) To create protect-lib for each hierarchical block (--hierarchical-child)
// c) To load wrappers and Verilate the top module (... what primary flags?)
//
// Then user can build Verilated module as usual.
//
// Here is more detailed internal process.
// 1) Parser adds AstPragmaType::HIER_BLOCK of AstPragma to modules
// that are marked with /*verilator hier_block*/ metacomment in Verilator run a).
// 2) AstModule with HIER_BLOCK pragma is marked modp->hier_block(true)
// in V3LinkResolve.cpp during run a).
// 3) In V3LinkCells.cpp, the following things are done during run b) and c).
// 3-1) Delete the upper modules of the hierarchical block because the top module in run b) is
// hierarchical block, not the top module of run c).
// 3-2) If the top module of the run b) or c) instantiates other hierarchical blocks that is
// parameterized,
// module and task names are renamed to the original name to be compatible with the
// hier module to be called.
//
// Parameterized modules have unique name by V3Param.cpp. The unique name contains '__' and
// Verilator encodes '__' when loading such symbols.
// 4) V3LinkDot.cpp checks dotted access across hierarchical block boundary.
// 5) In V3Dead.cpp, some parameters of parameterized modules are protected not to be deleted even
// if the parameter is not referred. This protection is necessary to match step 6) below.
// 6) In V3Param.cpp, use protect-lib wrapper of parameterized module made in b) and c).
// If a hierarchical block is a parameterized module and instantiated in multiple locations,
// all parameters must exactly match.
// 7) In V3HierBlock.cpp, relationship among hierarchical blocks are checked in run a).
// (which block uses other blocks..)
// 8) In V3EmitMk.cpp, ${prefix}_hier.mk is created in run a).
//
// There are two hidden command options.
// --hierarchical-child is added to Verilator run b).
// --hierarchical-block module_name,mangled_name,name0,value0,name1,value1,...
// module_name :The original modulename
// mangled_name :Mangled name of parameterized modules (named in V3Param.cpp).
// Same as module_name for non-parameterized hierarchical block.
// name :The name of the parameter
// value :Overridden value of the parameter
//
// Used for b) and c).
// This options is repeated for all instantiating hierarchical blocks.
#include <memory>
#include <sstream>
#include <utility>
#include <vector>
#include "V3Ast.h"
#include "V3Error.h"
#include "V3File.h"
#include "V3HierBlock.h"
#include "V3String.h"
#include "V3Stats.h"
static string V3HierCommandArgsFileName(const string& prefix, bool forCMake) {
return v3Global.opt.makeDir() + "/" + prefix
+ (forCMake ? "_hierCMakeArgs.f" : "_hierMkArgs.f");
}
static void V3HierWriteCommonInputs(std::ostream* of, bool forCMake) {
if (!forCMake) {
const V3StringList& vFiles = v3Global.opt.vFiles();
for (V3StringList::const_iterator it = vFiles.begin(); it != vFiles.end(); ++it) {
*of << *it << "\n";
}
}
const V3StringSet& libraryFiles = v3Global.opt.libraryFiles();
for (V3StringSet::const_iterator it = libraryFiles.begin(); it != libraryFiles.end(); ++it) {
*of << "-v " << *it << "\n";
}
}
//######################################################################
V3HierBlock::StrGParams V3HierBlock::stringifyParams(const GParams& gparams, bool forGOption) {
StrGParams strParams;
for (V3HierBlock::GParams::const_iterator gparamIt = gparams.begin();
gparamIt != gparams.end(); ++gparamIt) {
if (const AstConst* constp = VN_CAST((*gparamIt)->valuep(), Const)) {
string s;
// Only constant parameter needs to be set to -G because already checked in
// V3Param.cpp. See also ParamVisitor::checkSupportedParam() in the file.
if (constp->isDouble()) {
// 64 bit width of hex can be expressed with 16 chars.
// 32 chars must be long enough for hexadecial floating point
// considering prefix of '0x', '.', and 'P'.
std::vector<char> hexFpStr(32, '\0');
const int len = VL_SNPRINTF(hexFpStr.data(), hexFpStr.size(), "%a",
constp->num().toDouble());
UASSERT_OBJ(0 < len && static_cast<size_t>(len) < hexFpStr.size(), constp,
" is not properly converted to string");
s = hexFpStr.data();
} else if (constp->isString()) {
s = constp->num().toString();
if (!forGOption) s = VString::quoteBackslash(s);
s = VString::quoteStringLiteralForShell(s);
} else { // Either signed or unsigned integer.
s = constp->num().ascii(true, true);
s = VString::quoteAny(s, '\'', '\\');
}
strParams.push_back(std::make_pair((*gparamIt)->name(), s));
}
}
return strParams;
}
V3HierBlock::~V3HierBlock() {
UASSERT(m_children.empty(), "at least one module must be a leaf");
for (HierBlockSet::const_iterator child = m_children.begin(); child != m_children.end();
++child) {
const bool deleted = (*child)->m_children.erase(this);
UASSERT_OBJ(deleted, m_modp, " is not registered");
}
}
V3StringList V3HierBlock::commandArgs(bool forCMake) const {
V3StringList opts;
const string prefix = hierPrefix();
if (!forCMake) {
opts.push_back(" --prefix " + prefix);
opts.push_back(" --mod-prefix " + prefix);
opts.push_back(" --top-module " + modp()->name());
}
opts.push_back(" --protect-lib " + modp()->name()); // mangled name
opts.push_back(" --protect-key " + v3Global.opt.protectKeyDefaulted());
opts.push_back(" --hierarchical-child");
const StrGParams gparamsStr = stringifyParams(gparams(), true);
for (StrGParams::const_iterator paramIt = gparamsStr.begin(); paramIt != gparamsStr.end();
++paramIt) {
opts.push_back("-G" + paramIt->first + "=" + paramIt->second + "");
}
return opts;
}
V3StringList V3HierBlock::hierBlockArgs() const {
V3StringList opts;
const StrGParams gparamsStr = stringifyParams(gparams(), false);
opts.push_back("--hierarchical-block ");
string s = modp()->origName(); // origName
s += "," + modp()->name(); // mangledName
for (StrGParams::const_iterator paramIt = gparamsStr.begin(); paramIt != gparamsStr.end();
++paramIt) {
s += "," + paramIt->first;
s += "," + paramIt->second;
}
opts.back() += s;
return opts;
}
string V3HierBlock::hierPrefix() const { return "V" + modp()->name(); }
string V3HierBlock::hierSomeFile(bool withDir, const char* prefix, const char* suffix) const {
string s;
if (withDir) s = hierPrefix() + '/';
s += prefix + modp()->name() + suffix;
return s;
}
string V3HierBlock::hierWrapper(bool withDir) const { return hierSomeFile(withDir, "", ".sv"); }
string V3HierBlock::hierMk(bool withDir) const { return hierSomeFile(withDir, "V", ".mk"); }
string V3HierBlock::hierLib(bool withDir) const { return hierSomeFile(withDir, "lib", ".a"); }
string V3HierBlock::hierGenerated(bool withDir) const {
return hierWrapper(withDir) + ' ' + hierMk(withDir);
}
void V3HierBlock::writeCommandArgsFile(bool forCMake) const {
vl_unique_ptr<std::ofstream> of(V3File::new_ofstream(commandArgsFileName(forCMake)));
*of << "--cc\n";
if (!forCMake) {
for (V3HierBlock::HierBlockSet::const_iterator child = m_children.begin();
child != m_children.end(); ++child) {
*of << v3Global.opt.makeDir() << "/" << (*child)->hierWrapper(true) << "\n";
}
}
*of << "-Mdir " << v3Global.opt.makeDir() << "/" << hierPrefix() << " \n";
V3HierWriteCommonInputs(of.get(), forCMake);
const V3StringList& commandOpts = commandArgs(false);
for (V3StringList::const_iterator it = commandOpts.begin(); it != commandOpts.end(); ++it) {
*of << (*it) << "\n";
}
*of << hierBlockArgs().front() << "\n";
for (HierBlockSet::const_iterator child = m_children.begin(); child != m_children.end();
++child) {
*of << (*child)->hierBlockArgs().front() << "\n";
}
*of << v3Global.opt.allArgsStringForHierBlock(false) << "\n";
}
string V3HierBlock::commandArgsFileName(bool forCMake) const {
return V3HierCommandArgsFileName(hierPrefix(), forCMake);
}
//######################################################################
// Collect how hierarchical blocks are used
class HierBlockUsageCollectVisitor : public AstNVisitor {
// NODE STATE
// AstNode::user1() -> bool. Processed
AstUser1InUse m_inuser1;
// STATE
typedef std::set<const AstModule*> ModuleSet;
V3HierBlockPlan* const m_planp;
AstModule* m_modp; // The current module
AstModule* m_hierBlockp; // The nearest parent module that is a hierarchical block
ModuleSet m_referred; // Modules that have hier_block pragma
V3HierBlock::GParams m_gparams; // list of variables that is AstVarType::GPARAM
virtual void visit(AstModule* nodep) VL_OVERRIDE {
// Don't visit twice
if (nodep->user1SetOnce()) return;
UINFO(5, "Checking " << nodep->prettyNameQ() << " from "
<< (m_hierBlockp ? m_hierBlockp->prettyNameQ() : string("null"))
<< std::endl);
AstModule* const prevModp = m_modp;
AstModule* const prevHierBlockp = m_hierBlockp;
ModuleSet prevReferred;
V3HierBlock::GParams prevGParams;
m_modp = nodep;
if (nodep->hierBlock()) {
m_hierBlockp = nodep;
prevReferred.swap(m_referred);
}
prevGParams.swap(m_gparams);
iterateChildren(nodep);
if (nodep->hierBlock()) {
m_planp->add(nodep, m_gparams);
for (ModuleSet::const_iterator it = m_referred.begin(); it != m_referred.end(); ++it) {
m_planp->registerUsage(nodep, *it);
}
m_hierBlockp = prevHierBlockp;
m_referred = prevReferred;
}
m_modp = prevModp;
m_gparams = prevGParams;
}
virtual void visit(AstCell* nodep) VL_OVERRIDE {
// Visit used module here to know that the module is hier_block or not.
// This visitor behaves almost depth first search
if (AstModule* modp = VN_CAST(nodep->modp(), Module)) {
iterate(modp);
m_referred.insert(modp);
}
// Nothing to do for interface because hierarchical block does not exist
// beyond interface.
}
virtual void visit(AstVar* nodep) VL_OVERRIDE {
if (m_modp && m_modp->hierBlock() && nodep->isIfaceRef() && !nodep->isIfaceParent()) {
nodep->v3error("Modport cannot be used at the hierarchical block boundary");
}
if (nodep->isGParam() && nodep->overriddenParam()) m_gparams.push_back(nodep);
}
virtual void visit(AstNodeMath*) VL_OVERRIDE {} // Accelerate
virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); }
public:
HierBlockUsageCollectVisitor(V3HierBlockPlan* planp, AstNetlist* netlist)
: m_planp(planp)
, m_modp(NULL)
, m_hierBlockp(NULL) {
iterateChildren(netlist);
}
VL_DEBUG_FUNC; // Declare debug()
};
//######################################################################
V3HierBlockPlan::V3HierBlockPlan() {}
bool V3HierBlockPlan::isHierBlock(const AstNodeModule* modp) const {
return m_blocks.find(modp) != m_blocks.end();
}
void V3HierBlockPlan::add(const AstNodeModule* modp, const std::vector<AstVar*>& gparams) {
iterator it = m_blocks.find(modp);
if (it == m_blocks.end()) {
V3HierBlock* hblockp = new V3HierBlock(modp, gparams);
UINFO(3, "Add " << modp->prettyNameQ() << " with " << gparams.size() << " parameters"
<< std::endl);
m_blocks.insert(std::make_pair(modp, hblockp));
}
}
void V3HierBlockPlan::registerUsage(const AstNodeModule* parentp, const AstNodeModule* childp) {
iterator parent = m_blocks.find(parentp);
UASSERT_OBJ(parent != m_blocks.end(), parentp, "must be added");
iterator child = m_blocks.find(childp);
if (child != m_blocks.end()) {
UINFO(3, "Found usage relation " << parentp->prettyNameQ() << " uses "
<< childp->prettyNameQ() << std::endl);
parent->second->addChild(child->second);
child->second->addParent(parent->second);
}
}
void V3HierBlockPlan::createPlan(AstNetlist* nodep) {
// When processing a hierarchical block, no need to create a plan anymore.
if (v3Global.opt.hierChild()) return;
AstNodeModule* modp = nodep->topModulep();
if (modp->hierBlock()) {
modp->v3warn(HIERBLOCK,
"Top module illegally marked hierarchical block, ignoring marking\n"
+ V3Error::warnMore()
+ "... Suggest remove verilator hier_block on this module");
modp->hierBlock(false);
}
vl_unique_ptr<V3HierBlockPlan> planp(new V3HierBlockPlan());
{ HierBlockUsageCollectVisitor visitor(planp.get(), nodep); }
V3Stats::addStat("HierBlock, Hierarchical blocks", planp->m_blocks.size());
// No hierarchical block is found, nothing to do.
if (planp->empty()) return;
v3Global.hierPlanp(planp.release());
}
V3HierBlockPlan::HierVector V3HierBlockPlan::hierBlocksSorted() const {
typedef std::map<const V3HierBlock*, std::set<const V3HierBlock*> > ChildrenMap;
ChildrenMap childrenOfHierBlock;
HierVector sorted;
for (const_iterator it = begin(); it != end(); ++it) {
if (!it->second->hasChild()) { // No children, already leaf
sorted.push_back(it->second);
} else {
ChildrenMap::value_type::second_type& childrenSet
= childrenOfHierBlock[it->second]; // insert
const V3HierBlock::HierBlockSet& c = it->second->children();
childrenSet.insert(c.begin(), c.end());
}
}
// Use index instead of iterator because new elements will be added in this loop
for (size_t i = 0; i < sorted.size(); ++i) {
// This hblockp is already leaf.
const V3HierBlock* hblockp = sorted[i];
const V3HierBlock::HierBlockSet& p = hblockp->parents();
for (V3HierBlock::HierBlockSet::const_iterator it = p.begin(); it != p.end(); ++it) {
// Delete hblockp from parrents. If a parent does not have a child anymore, then it is
// a leaf too.
const ChildrenMap::iterator parentIt = childrenOfHierBlock.find(*it);
UASSERT_OBJ(parentIt != childrenOfHierBlock.end(), (*it)->modp(), "must be included");
const V3HierBlock::HierBlockSet::size_type erased = parentIt->second.erase(hblockp);
UASSERT_OBJ(erased == 1, hblockp->modp(),
" must be a child of " << parentIt->first->modp());
if (parentIt->second.empty()) { // Now parentIt is leaf
sorted.push_back(parentIt->first);
childrenOfHierBlock.erase(parentIt);
}
}
}
return sorted;
}
void V3HierBlockPlan::writeCommandArgsFiles(bool forCMake) const {
for (const_iterator it = begin(); it != end(); ++it) {
it->second->writeCommandArgsFile(forCMake);
}
// For the top module
vl_unique_ptr<std::ofstream> of(V3File::new_ofstream(topCommandArgsFileName(forCMake)));
if (!forCMake) {
// Load wrappers first not to be overwritten by the original HDL
for (const_iterator it = begin(); it != end(); ++it) {
*of << it->second->hierWrapper(true) << "\n";
}
}
V3HierWriteCommonInputs(of.get(), forCMake);
if (!forCMake) {
const V3StringSet& cppFiles = v3Global.opt.cppFiles();
for (V3StringSet::const_iterator it = cppFiles.begin(); it != cppFiles.end(); ++it) {
*of << *it << "\n";
}
*of << "--top-module " << v3Global.rootp()->topModulep()->name() << "\n";
*of << "--prefix " << v3Global.opt.prefix() << "\n";
*of << "-Mdir " << v3Global.opt.makeDir() << "\n";
*of << "--mod-prefix " << v3Global.opt.modPrefix() << "\n";
}
for (const_iterator it = begin(); it != end(); ++it) {
*of << it->second->hierBlockArgs().front() << "\n";
}
if (!v3Global.opt.protectLib().empty()) {
*of << "--protect-lib " << v3Global.opt.protectLib() << "\n";
*of << "--protect-key " << v3Global.opt.protectKeyDefaulted() << "\n";
}
*of << (v3Global.opt.systemC() ? "--sc" : "--cc") << "\n";
*of << v3Global.opt.allArgsStringForHierBlock(true) << "\n";
}
string V3HierBlockPlan::topCommandArgsFileName(bool forCMake) const {
return V3HierCommandArgsFileName(v3Global.opt.prefix(), forCMake);
}

128
src/V3HierBlock.h Normal file
View File

@ -0,0 +1,128 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Plan for Hierarchical Verilation
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2020 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.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3HIERBLOCK_H_
#define _V3HIERBLOCK_H_ 1
#include "verilatedos.h"
#include "V3Global.h"
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
class AstNodeModule;
class AstNetlist;
class AstVar;
//######################################################################
class V3HierBlock {
public:
typedef std::vector<AstVar*> GParams;
typedef std::set<V3HierBlock*> HierBlockSet;
typedef std::set<const AstNodeModule*> NodeModuleSet;
private:
// TYPES
// Parameter name, stringified value
typedef std::vector<std::pair<string, string> > StrGParams;
// MEMBERS
const AstNodeModule* m_modp; // Hierarchical block module
// Hierarchical blocks that directly or indirectly instantiate this block
HierBlockSet m_parents;
// Hierarchical blocks that this block directly or indirectly instantiates
HierBlockSet m_children;
// Parameters that are overridden by #(.param(value)) syntax.
GParams m_gparams;
// METHODS
VL_UNCOPYABLE(V3HierBlock);
static StrGParams stringifyParams(const GParams& gparams, bool forGOption);
public:
V3HierBlock(const AstNodeModule* modp, const GParams& gparams)
: m_modp(modp)
, m_gparams(gparams) {}
~V3HierBlock();
VL_DEBUG_FUNC; // Declare debug()
void addParent(V3HierBlock* parentp) { m_parents.insert(parentp); }
void addChild(V3HierBlock* childp) { m_children.insert(childp); }
bool hasChild() const { return !m_children.empty(); }
const HierBlockSet& parents() const { return m_parents; }
const HierBlockSet& children() const { return m_children; }
const GParams& gparams() const { return m_gparams; }
const AstNodeModule* modp() const { return m_modp; }
// For emitting Makefile and CMakeLists.txt
V3StringList commandArgs(bool forCMake) const;
V3StringList hierBlockArgs() const;
string hierPrefix() const;
string hierSomeFile(bool withDir, const char* prefix, const char* suffix) const;
string hierWrapper(bool withDir) const;
string hierMk(bool withDir) const;
string hierLib(bool withDir) const;
string hierGenerated(bool withDir) const;
// Write command line argumuents to .f file for this hierarchical block
void writeCommandArgsFile(bool forCMake) const;
string commandArgsFileName(bool forCMake) const;
};
//######################################################################
// Holds relashonship between AstNodeModule and V3HierBlock
class V3HierBlockPlan {
typedef std::map<const AstNodeModule*, V3HierBlock*> HierMap;
HierMap m_blocks;
V3HierBlockPlan();
VL_UNCOPYABLE(V3HierBlockPlan);
public:
typedef HierMap::iterator iterator;
typedef HierMap::const_iterator const_iterator;
typedef std::vector<const V3HierBlock*> HierVector;
VL_DEBUG_FUNC; // Declare debug()
bool isHierBlock(const AstNodeModule* modp) const;
void add(const AstNodeModule* modp, const std::vector<AstVar*>& gparams);
void registerUsage(const AstNodeModule* parentp, const AstNodeModule* childp);
const_iterator begin() const { return m_blocks.begin(); }
const_iterator end() const { return m_blocks.end(); }
bool empty() const { return m_blocks.empty(); }
// Returns all hierarchical blocks that sorted in leaf-first order.
// Latter block refers only already appeared hierarchical blocks.
HierVector hierBlocksSorted() const;
// Write command line arguments to .f files for child Verilation run
void writeCommandArgsFiles(bool forCMake) const;
string topCommandArgsFileName(bool forCMake) const;
static void createPlan(AstNetlist* nodep);
};
#endif // guard

View File

@ -115,6 +115,7 @@ private:
LibraryVertex* m_libVertexp; // Vertex at root of all libraries
V3GraphVertex* m_topVertexp; // Vertex of top module
vl_unordered_set<string> m_declfnWarned; // Files we issued DECLFILENAME on
string m_origTopModuleName; // original name of the top module
VL_DEBUG_FUNC; // Declare debug()
@ -290,6 +291,16 @@ private:
// m_mod, if 0 never did it, if !=, it is an unprocessed clone
bool cloned = (nodep->user1p() && nodep->user1p() != m_modp);
if (nodep->user1p() == m_modp) return; // AstBind and AstNodeModule may call a cell twice
if (v3Global.opt.hierChild() && nodep->modName() == m_origTopModuleName) {
if (nodep->modName() == m_modp->origName()) {
// Only the root of the recursive instantiation can be a hierarhcical block.
nodep->modName(m_modp->name());
} else {
// In hierarchical mode, non-top module can be the top module of this run
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
}
nodep->user1p(m_modp);
//
if (!nodep->modp() || cloned) {
@ -453,13 +464,23 @@ private:
// METHODS
void readModNames() {
// mangled_name, BlockOptions
const V3HierBlockOptSet& hierBlocks = v3Global.opt.hierBlocks();
V3HierBlockOptSet::const_iterator hierIt = hierBlocks.find(v3Global.opt.topModule());
UASSERT((hierIt != hierBlocks.end()) == v3Global.opt.hierChild(),
"information of the top module must exist if --hierarchical-child is set");
// Look at all modules, and store pointers to all module names
for (AstNodeModule *nextp, *nodep = v3Global.rootp()->modulesp(); nodep; nodep = nextp) {
nextp = VN_CAST(nodep->nextp(), NodeModule);
if (v3Global.opt.hierChild() && nodep->name() == hierIt->second.origName()) {
nodep->name(hierIt->first); // Change name of this module to be mangled name
// considering parameter
}
AstNodeModule* foundp = findModuleSym(nodep->name());
if (foundp && foundp != nodep) {
if (!(foundp->fileline()->warnIsOff(V3ErrorCode::MODDUP)
|| nodep->fileline()->warnIsOff(V3ErrorCode::MODDUP))) {
|| nodep->fileline()->warnIsOff(V3ErrorCode::MODDUP)
|| hierBlocks.find(nodep->name()) != hierBlocks.end())) {
nodep->v3warn(MODDUP, "Duplicate declaration of module: "
<< nodep->prettyNameQ() << endl
<< nodep->warnContextPrimary() << endl
@ -485,6 +506,18 @@ public:
m_modp = NULL;
m_libVertexp = NULL;
m_topVertexp = NULL;
if (v3Global.opt.hierChild()) {
const V3HierBlockOptSet& hierBlocks = v3Global.opt.hierBlocks();
UASSERT(!v3Global.opt.topModule().empty(),
"top module must be explicitly specified in hierarchical mode");
const V3HierBlockOptSet::const_iterator hierIt
= hierBlocks.find(v3Global.opt.topModule());
UASSERT(hierIt != hierBlocks.end(),
"top module must be listed in --hierarchical-block");
m_origTopModuleName = hierIt->second.origName();
} else {
m_origTopModuleName = v3Global.opt.topModule();
}
iterate(nodep);
}
virtual ~LinkCellsVisitor() {}

View File

@ -541,7 +541,7 @@ private:
}
public:
VSymEnt* findDotted(FileLine* /*refLocationp*/, VSymEnt* lookupSymp, const string& dotname,
VSymEnt* findDotted(FileLine* refLocationp, VSymEnt* lookupSymp, const string& dotname,
string& baddot, VSymEnt*& okSymp) {
// Given a dotted hierarchy name, return where in scope it is
// Note when dotname=="" we just fall through and return lookupSymp
@ -636,6 +636,15 @@ public:
return NULL; // Not found
}
}
if (lookupSymp) {
if (AstCell* cellp = VN_CAST(lookupSymp->nodep(), Cell)) {
if (AstNodeModule* modp = cellp->modp()) {
if (modp->hierBlock()) {
refLocationp->v3error("Cannot access inside hierarchical block");
}
}
}
}
firstId = false;
}
return lookupSymp;
@ -720,6 +729,11 @@ class LinkDotFindVisitor : public AstNVisitor {
m_statep->insertBlock(m_curSymp, newp->name(), newp, m_packagep);
}
bool isHierBlockWrapper(const string& name) const {
const V3HierBlockOptSet& hierBlocks = v3Global.opt.hierBlocks();
return hierBlocks.find(name) != hierBlocks.end();
}
// VISITs
virtual void visit(AstNetlist* nodep) VL_OVERRIDE {
// Process $unit or other packages
@ -801,6 +815,14 @@ class LinkDotFindVisitor : public AstNVisitor {
if (AstIface* ifacep = VN_CAST(nodep, Iface)) {
m_statep->insertIfaceModSym(ifacep, m_curSymp);
}
} else if (isHierBlockWrapper(nodep->name())) {
UINFO(5, "Module is hierarchical block, must not be dead: " << nodep << endl);
m_scope = nodep->name();
VSymEnt* upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp();
m_curSymp = m_modSymp
= m_statep->insertBlock(upperSymp, nodep->name() + "::", nodep, m_packagep);
iterateChildren(nodep);
nodep->user4(true);
} else { // !doit
// Will be optimized away later
// Can't remove now, as our backwards iterator will throw up

View File

@ -266,7 +266,17 @@ private:
}
virtual void visit(AstPragma* nodep) VL_OVERRIDE {
if (nodep->pragType() == AstPragmaType::PUBLIC_MODULE) {
if (nodep->pragType() == AstPragmaType::HIER_BLOCK) {
UASSERT_OBJ(m_modp, nodep, "HIER_BLOCK not under a module");
// If this is hierarchical mode which is to create protect-lib,
// sub modules do not have hier_block meta comment in the source code.
// But .vlt files may still mark a module which is actually a protect-lib wrapper
// hier_block. AstNodeModule::hierBlock() can be true only when --hierarchical is
// specified.
m_modp->hierBlock(v3Global.opt.hierarchical());
nodep->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else if (nodep->pragType() == AstPragmaType::PUBLIC_MODULE) {
UASSERT_OBJ(m_modp, nodep, "PUBLIC_MODULE not under a module");
m_modp->modPublic(true);
nodep->unlinkFrBack();

View File

@ -135,6 +135,95 @@ VTimescale::VTimescale(const string& value, bool& badr)
}
}
//######################################################################
// V3HierarchicalBlockOption class functions
// Parse "--hierarchical-block orig_name,mangled_name,param0_name,param0_value,... " option.
// The format of value is as same as -G option. (can be string literal surrounded by ")
V3HierarchicalBlockOption::V3HierarchicalBlockOption(const string& opts) {
V3StringList vals;
bool inStr = false;
string cur;
static const string hierBlock("--hierarchical-block");
FileLine cmdfl(FileLine::commandLineFilename());
// Split by ','. If ',' appears between "", that is not a separator.
for (string::const_iterator it = opts.begin(); it != opts.end();) {
if (inStr) {
if (*it == '\\') {
++it;
if (it == opts.end()) {
cmdfl.v3error(hierBlock + " must not end with \\");
break;
}
if (*it != '"' && *it != '\\') {
cmdfl.v3error(hierBlock + " does not allow '" + *it + "' after \\");
break;
}
cur.push_back(*it);
++it;
} else if (*it == '"') { // end of string
cur.push_back(*it);
vals.push_back(cur);
cur.clear();
++it;
if (it != opts.end()) {
if (*it != ',') {
cmdfl.v3error(hierBlock + " expects ',', but '" + *it + "' is passed");
break;
}
++it;
if (it == opts.end()) {
cmdfl.v3error(hierBlock + " must not end with ','");
break;
}
inStr = *it == '"';
cur.push_back(*it);
++it;
}
} else {
cur.push_back(*it);
++it;
}
} else {
if (*it == '"') {
cmdfl.v3error(hierBlock + " does not allow '\"' in the middle of literal");
break;
}
if (*it == ',') { // end of this parameter
vals.push_back(cur);
cur.clear();
++it;
if (it == opts.end()) {
cmdfl.v3error(hierBlock + " must not end with ','");
break;
}
inStr = *it == '"';
}
cur.push_back(*it);
++it;
}
}
if (!cur.empty()) vals.push_back(cur);
if (vals.size() >= 2) {
if (vals.size() % 2) {
cmdfl.v3error(hierBlock + " requires the number of entries to be even");
}
m_origName = vals[0];
m_mangledName = vals[1];
} else {
cmdfl.v3error(hierBlock + " requires at least two comma-separated values");
}
for (size_t i = 2; i + 1 < vals.size(); i += 2) {
const bool inserted = m_parameters.insert(std::make_pair(vals[i], vals[i + 1])).second;
if (!inserted) {
cmdfl.v3error("Module name '" + vals[i] + "' is duplicated in " + hierBlock);
}
}
}
//######################################################################
// V3Options class functions
void VTimescale::parseSlashed(FileLine* fl, const char* textp, VTimescale& unitr,
VTimescale& precr, bool allowEmpty) {
// Parse `timescale of <number><units> / <number><units>
@ -304,6 +393,42 @@ string V3Options::allArgsString() const {
return out;
}
// Delete some options for Verilation of the hierarchical blocks.
string V3Options::allArgsStringForHierBlock(bool forTop) const {
std::set<string> vFiles;
for (V3StringList::const_iterator it = m_vFiles.begin(); it != m_vFiles.end(); ++it)
vFiles.insert(*it);
string out;
for (std::list<string>::const_iterator it = m_impp->m_allArgs.begin();
it != m_impp->m_allArgs.end(); ++it) {
int skip = 0;
if (it->length() >= 2 && (*it)[0] == '-' && (*it)[1] == '-') {
skip = 2;
} else if (it->length() >= 1 && (*it)[0] == '-') {
skip = 1;
}
if (skip > 0) { // *it is an option
const string opt = it->substr(skip); // Remove '-' in the beginning
const int numStrip = stripOptionsForChildRun(opt, forTop);
if (numStrip) {
UASSERT(0 <= numStrip && numStrip <= 2, "should be one of 0, 1, 2");
if (numStrip == 2) ++it;
continue;
}
} else { // Not an option
if (vFiles.find(*it) != vFiles.end() // Remove HDL
|| m_cppFiles.find(*it) != m_cppFiles.end()) { // Remove C++
continue;
}
}
if (out != "") out += " ";
// Don't use opt here because '-' is removed in it
// Use double quote because *it may contain whitespaces
out += '"' + VString::quoteAny(*it, '"', '\\') + '"';
}
return out;
}
//######################################################################
// File searching
@ -383,6 +508,23 @@ string V3Options::filePathCheckOneDir(const string& modname, const string& dirna
return "";
}
// Checks if a option needs to be stripped for child run of hierarchical Verilation.
// 0: Keep the option including its argument
// 1: Delete the option which has no argument
// 2: Delete the option and its argument
int V3Options::stripOptionsForChildRun(const string& opt, bool forTop) const {
if (opt == "Mdir" || opt == "clk" || opt == "f" || opt == "j" || opt == "l2-name"
|| opt == "mod-prefix" || opt == "prefix" || opt == "protect-lib" || opt == "protect-key"
|| opt == "top-module" || opt == "v") {
return 2;
}
if (opt == "build" || (!forTop && (opt == "cc" || opt == "exe" || opt == "sc"))
|| opt == "hierarchical" || (opt.length() > 2 && opt.substr(0, 2) == "G=")) {
return 1;
}
return 0;
}
string V3Options::filePath(FileLine* fl, const string& modname, const string& lastpath,
const string& errmsg) { // Error prefix or "" to suppress error
// Find a filename to read the specified module name,
@ -599,6 +741,19 @@ void V3Options::notify() {
// Make sure at least one make system is enabled
if (!m_gmake && !m_cmake) m_gmake = true;
if (m_hierarchical && (m_hierChild || !m_hierBlocks.empty())) {
cmdfl->v3error(
"--hierarchical must not be set with --hierarchical-child or --hierarchical-block");
}
if (m_hierChild && m_hierBlocks.empty()) {
cmdfl->v3error("--hierarchical-block must be set when --hierarchical-child is set");
}
if (m_hierarchical && m_protectLib.empty() && m_protectKey.empty()) {
// Key for hierarchical Verilation is fixed to be ccache friendly when the aim of this run
// is not to create protec-lib.
m_protectKey = "VL-KEY-HIERARCHICAL";
}
if (protectIds()) {
if (allPublic()) {
// We always call protect() on names, we don't check if public or not
@ -896,6 +1051,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
m_exe = flag;
} else if (onoff(sw, "-flatten", flag /*ref*/)) {
m_flatten = flag;
} else if (onoff(sw, "-hierarchical", flag /*ref*/)) {
m_hierarchical = flag;
} else if (onoff(sw, "-hierarchical-child", flag /*ref*/)) {
m_hierChild = flag;
} else if (onoff(sw, "-ignc", flag /*ref*/)) {
m_ignc = flag;
} else if (onoff(sw, "-inhibit-sim", flag /*ref*/)) {
@ -1081,6 +1240,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
shift;
cout << V3Options::getenvBuiltins(argv[i]) << endl;
exit(0);
} else if (!strcmp(sw, "-hierarchical-block") && (i + 1) < argc) {
shift;
V3HierarchicalBlockOption opt(argv[i]);
m_hierBlocks.insert(std::make_pair(opt.mangledName(), opt));
} else if (!strncmp(sw, "-I", 2)) {
addIncDirUser(parseFileArg(optdir, string(sw + strlen("-I"))));
} else if (!strcmp(sw, "-if-depth") && (i + 1) < argc) {
@ -1648,10 +1811,12 @@ V3Options::V3Options() {
m_dumpDefines = false;
m_exe = false;
m_flatten = false;
m_gmake = false;
m_hierarchical = false;
m_hierChild = false;
m_ignc = false;
m_inhibitSim = false;
m_lintOnly = false;
m_gmake = false;
m_makePhony = false;
m_main = false;
m_orderClockDly = true;

View File

@ -195,6 +195,31 @@ inline bool operator==(TraceFormat::en lhs, const TraceFormat& rhs) { return lhs
typedef std::vector<string> V3StringList;
typedef std::set<string> V3StringSet;
//######################################################################
// Information given by --hierarchical-block option
class V3HierarchicalBlockOption {
public:
// key:parameter name, value:value (as string)
typedef std::map<string, string> ParamStrMap;
private:
string m_origName; // module name
// module name after uniquified
// same as m_origName for non-parameterized module
string m_mangledName;
// overriding parameter values specified by -G option
ParamStrMap m_parameters;
public:
explicit V3HierarchicalBlockOption(const string& optstring);
const string& origName() const { return m_origName; }
const string& mangledName() const { return m_mangledName; }
const ParamStrMap params() const { return m_parameters; }
};
typedef std::map<string, V3HierarchicalBlockOption> V3HierBlockOptSet;
//######################################################################
// V3Options - Command line options
@ -221,6 +246,7 @@ private:
DebugSrcMap m_debugSrcs; // argument: --debugi-<srcfile>=<level>
DebugSrcMap m_dumpTrees; // argument: --dump-treei-<srcfile>=<level>
std::map<string,string> m_parameters; // Parameters
std::map<string, V3HierarchicalBlockOption> m_hierBlocks; // main switch: --hierarchical-block
bool m_preprocOnly; // main switch: -E
bool m_makePhony; // main switch: -MP
@ -295,6 +321,8 @@ private:
int m_dumpTree; // main switch: --dump-tree
bool m_dumpTreeAddrids;// main switch: --dump-tree-addrids
int m_gateStmts; // main switch: --gate-stmts
bool m_hierarchical; // main switch: --hierarchical
bool m_hierChild; // main switch: --hierarchical-child
int m_ifDepth; // main switch: --if-depth
int m_inlineMult; // main switch: --inline-mult
VOptionBool m_makeDepend; // main switch: -MMD
@ -386,6 +414,7 @@ private:
string parseFileArg(const string& optdir, const string& relfilename);
bool parseLangExt(const char* swp, const char* langswp, const V3LangCode& lc);
string filePathCheckOneDir(const string& modname, const string& dirname);
int stripOptionsForChildRun(const string& opt, bool forTop) const;
// CONSTRUCTORS
VL_UNCOPYABLE(V3Options);
@ -595,10 +624,23 @@ public:
return m_traceFormat.sourceName() + (systemC() ? "_sc" : "_c");
}
bool hierarchical() const { return m_hierarchical; }
bool hierChild() const { return m_hierChild; }
bool hierTop() const { return !m_hierChild && !m_hierBlocks.empty(); }
const V3HierBlockOptSet& hierBlocks() const { return m_hierBlocks; }
// Directory to save .tree, .dot, .dat, .vpp for hierarchical block top
// Returns makeDir() unless top module of hierarchical verilation.
string hierTopDataDir() const {
return hierTop() ? (makeDir() + '/' + prefix() + "__hier.dir") : makeDir();
}
// METHODS (from main)
static string version();
static string argString(int argc, char** argv); ///< Return list of arguments as simple string
string allArgsString() const; ///< Return all passed arguments as simple string
// Return options for child hierarchical blocks when forTop==false, otherwise returns args for
// the top module.
string allArgsStringForHierBlock(bool forTop) const;
void bin(const string& flag) { m_bin = flag; }
void parseOpts(FileLine* fl, int argc, char** argv);
void parseOptsList(FileLine* fl, const string& optdir, int argc, char** argv);

View File

@ -52,14 +52,150 @@
#include "V3Ast.h"
#include "V3Case.h"
#include "V3Const.h"
#include "V3Os.h"
#include "V3Parse.h"
#include "V3Width.h"
#include "V3Unroll.h"
#include "V3Hashed.h"
#include <deque>
#include <map>
#include <map>
#include <vector>
//######################################################################
// Hierarchical block and parameter db (modules without parameter is also handled)
class ParameterizedHierBlocks {
typedef std::multimap<string, const V3HierarchicalBlockOption*> HierBlockOptsByOrigName;
typedef HierBlockOptsByOrigName::const_iterator HierMapIt;
typedef std::map<string, AstNodeModule*> HierBlockModMap;
typedef std::map<string, AstConst*> ParamConstMap;
typedef std::map<const V3HierarchicalBlockOption*, ParamConstMap> ParamsMap;
// MEMBERS
// key:Original module name, value:HiearchyBlockOption*
// If a module is parameterized, the module is uniquiefied to overridden parameters.
// This is why HierBlockOptsByOrigName is multimap.
HierBlockOptsByOrigName m_hierBlockOptsByOrigName;
// key:mangled module name, value:AstNodeModule*
HierBlockModMap m_hierBlockMod;
// Overridden parameters of the hierarchical block
ParamsMap m_params;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
static bool areSame(AstConst* pinValuep, AstConst* hierOptParamp) {
if (pinValuep->isString()) {
return pinValuep->num().toString() == hierOptParamp->num().toString();
}
// Bitwidth of hierOptParamp is accurate because V3Width already caluclated in the previous
// run. Bitwidth of pinValuep is before width analysis, so pinValuep is casted to
// hierOptParamp width.
V3Number varNum(pinValuep, hierOptParamp->num().width());
if (hierOptParamp->isDouble()) {
varNum.isDouble(true);
if (pinValuep->isDouble()) {
varNum.opAssign(pinValuep->num());
} else { // Cast from integer to real
varNum.opIToRD(pinValuep->num());
}
return v3EpsilonEqual(varNum.toDouble(), hierOptParamp->num().toDouble());
} else { // Now integer type is assumed
if (pinValuep->isDouble()) { // Need to cast to int
// Parameter is actually an integral type, but passed value is floating point.
// Conversion from real to integer uses rounding in V3Width.cpp
varNum.opRToIRoundS(pinValuep->num());
} else if (pinValuep->isSigned()) {
varNum.opExtendS(pinValuep->num(), pinValuep->num().width());
} else {
varNum.opAssign(pinValuep->num());
}
V3Number isEq(pinValuep, 1);
isEq.opEq(varNum, hierOptParamp->num());
return isEq.isNeqZero();
}
}
public:
ParameterizedHierBlocks(const V3HierBlockOptSet& hierOpts, AstNetlist* nodep) {
for (V3HierBlockOptSet::const_iterator it = hierOpts.begin(); it != hierOpts.end(); ++it) {
m_hierBlockOptsByOrigName.insert(std::make_pair(it->second.origName(), &it->second));
const V3HierarchicalBlockOption::ParamStrMap& params = it->second.params();
ParamConstMap& consts = m_params[&it->second];
for (V3HierarchicalBlockOption::ParamStrMap::const_iterator pIt = params.begin();
pIt != params.end(); ++pIt) {
AstConst* constp = AstConst::parseParamLiteral(
new FileLine(FileLine::EmptySecret()), pIt->second);
UASSERT(constp, pIt->second << " is not a valid parameter literal");
const bool inserted = consts.insert(std::make_pair(pIt->first, constp)).second;
UASSERT(inserted, pIt->first << " is already added");
}
}
for (AstNodeModule* modp = nodep->modulesp(); modp;
modp = VN_CAST(modp->nextp(), NodeModule)) {
if (hierOpts.find(modp->prettyName()) != hierOpts.end()) {
m_hierBlockMod.insert(std::make_pair(modp->name(), modp));
}
}
}
~ParameterizedHierBlocks() {
for (ParamsMap::const_iterator it = m_params.begin(); it != m_params.end(); ++it) {
for (ParamConstMap::const_iterator pIt = it->second.begin(); pIt != it->second.end();
++pIt) {
delete pIt->second;
}
}
}
AstNodeModule* findByParams(const string& origName, AstPin* firstPinp,
const AstNodeModule* modp) {
if (m_hierBlockOptsByOrigName.find(origName) == m_hierBlockOptsByOrigName.end()) {
return NULL;
}
// This module is a hierarchical block. Need to replace it by the protect-lib wrapper.
const std::pair<HierMapIt, HierMapIt> candidates
= m_hierBlockOptsByOrigName.equal_range(origName);
HierMapIt hierIt;
for (hierIt = candidates.first; hierIt != candidates.second; ++hierIt) {
bool found = true;
size_t paramIdx = 0;
const ParamConstMap& params = m_params[hierIt->second];
UASSERT(params.size() == hierIt->second->params().size(), "not match");
for (AstPin* pinp = firstPinp; pinp; pinp = VN_CAST(pinp->nextp(), Pin)) {
if (!pinp->exprp()) continue;
UASSERT_OBJ(!pinp->modPTypep(), pinp,
"module with type parameter must not be a hierarchical block");
if (AstVar* modvarp = pinp->modVarp()) {
AstConst* constp = VN_CAST(pinp->exprp(), Const);
UASSERT_OBJ(constp, pinp,
"parameter for a hierarchical block must have been constified");
ParamConstMap::const_iterator pIt = params.find(modvarp->name());
UINFO(5, "Comparing " << modvarp->name() << " " << constp << std::endl);
if (pIt == params.end() || paramIdx >= params.size()
|| !areSame(constp, pIt->second)) {
found = false;
break;
}
UINFO(5, "Matched " << modvarp->name() << " " << constp << " and "
<< pIt->second << std::endl);
++paramIdx;
}
}
if (found && paramIdx == hierIt->second->params().size()) break;
}
UASSERT_OBJ(hierIt != candidates.second, firstPinp, "No protect-lib wrapper found");
// parameter settings will be removed in the bottom of caller visitCell().
const HierBlockModMap::const_iterator modIt
= m_hierBlockMod.find(hierIt->second->mangledName());
UASSERT_OBJ(modIt != m_hierBlockMod.end(), firstPinp,
hierIt->second->mangledName() << " is not found");
HierBlockModMap::const_iterator it = m_hierBlockMod.find(hierIt->second->mangledName());
if (it == m_hierBlockMod.end()) return NULL;
return it->second;
}
};
//######################################################################
// Param state, as a visitor of each AstNode
@ -95,6 +231,10 @@ private:
LongMap m_longMap; // Hash of very long names to unique identity number
int m_longId;
// All module names that are loaded from source code
// Generated modules by this visitor is not included
V3StringSet m_allModuleNames;
typedef std::pair<int, string> ValueMapValue;
typedef std::map<V3Hash, ValueMapValue> ValueMap;
ValueMap m_valueMap; // Hash of node hash to (param value, name)
@ -114,7 +254,10 @@ private:
UnrollStateful m_unroller; // Loop unroller
string m_generateHierName; // Generate portion of hierarchical name
string m_generateHierName; // Generate portion of hierarchy name
// Database to get protect-lib wrapper that matches parameters in hierarchical Verilation
ParameterizedHierBlocks m_hierBlocks;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
@ -229,6 +372,75 @@ private:
}
}
}
void relinkPinsByName(AstPin* startpinp, AstNodeModule* modp) {
std::map<string, AstVar*> nameToPin;
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstVar* varp = VN_CAST(stmtp, Var)) {
if (varp->isIO() || varp->isGParam() || varp->isIfaceRef()) {
nameToPin.insert(make_pair(varp->name(), varp));
}
}
}
for (AstPin* pinp = startpinp; pinp; pinp = VN_CAST(pinp->nextp(), Pin)) {
if (AstVar* varp = pinp->modVarp()) {
std::map<string, AstVar*>::const_iterator varIt = nameToPin.find(varp->name());
UASSERT_OBJ(varIt != nameToPin.end(), varp,
"Not found in " << modp->prettyNameQ());
pinp->modVarp(varIt->second);
}
}
}
// Check if parameter setting during instantiation is simple enough for hierarchical verilation
void checkSupportedParam(AstNodeModule* modp, AstPin* pinp) const {
// InitArray and AstParamTypeDType are not supported because that can not be set via -G
// option.
if (pinp->modVarp()) {
bool supported = false;
if (AstConst* constp = VN_CAST(pinp->exprp(), Const)) {
supported = !constp->isOpaque();
}
if (!supported) {
pinp->v3error(AstNode::prettyNameQ(modp->origName())
<< " has hier_block metacomment, hierarchical verilation"
<< " supports only integer/floating point/string parameters");
}
} else if (VN_IS(pinp->modPTypep(), ParamTypeDType)) {
pinp->v3error(AstNode::prettyNameQ(modp->origName())
<< " has hier_block metacomment, but 'parameter type' is not supported");
}
}
bool moduleExists(const string& modName) const {
if (m_allModuleNames.find(modName) != m_allModuleNames.end()) return true;
if (m_modNameMap.find(modName) != m_modNameMap.end()) return true;
return false;
}
string parametrizedHierBlockName(const AstNodeModule* modp, AstPin* paramPinsp) const {
VHashSha256 hash;
// Calculate hash using module name, parameter name, and parameter value
// The hash is used as the module suffix to find a module name that is unique in the design
hash.insert(modp->name());
for (AstPin* pinp = paramPinsp; pinp; pinp = VN_CAST(pinp->nextp(), Pin)) {
if (AstVar* varp = pinp->modVarp()) hash.insert(varp->name());
if (AstConst* constp = VN_CAST(pinp->exprp(), Const)) {
hash.insert(constp->num().ascii(false));
}
}
while (true) {
// Copy VHashSha256 just in case of hash collision
VHashSha256 hashStrGen = hash;
// Hex string must be a safe suffix for any symbol
const string hashStr = hashStrGen.digestHex();
for (string::size_type i = 1; i < hashStr.size(); ++i) {
string newName = modp->name();
// Don't use '__' not to be encoded when this module is loaded later by Verilator
if (newName.at(newName.size() - 1) != '_') newName += '_';
newName += hashStr.substr(0, i);
if (!moduleExists(newName)) return newName;
}
// Hash collision. maybe just v3error is practically enough
hash.insert(V3Os::trueRandom(64));
}
}
void visitCell(AstCell* nodep, const string& hierName);
void visitModules() {
// Loop on all modules left to process
@ -571,11 +783,16 @@ private:
public:
// CONSTRUCTORS
explicit ParamVisitor(AstNetlist* nodep) {
explicit ParamVisitor(AstNetlist* nodep)
: m_hierBlocks(v3Global.opt.hierBlocks(), nodep) {
m_longId = 0;
m_ftaskp = NULL;
m_modp = NULL;
m_nextValue = 1;
for (AstNodeModule* modp = nodep->modulesp(); modp;
modp = VN_CAST(modp->nextp(), NodeModule)) {
m_allModuleNames.insert(modp->name());
}
//
iterate(nodep);
}
@ -740,19 +957,31 @@ void ParamVisitor::visitCell(AstCell* nodep, const string& hierName) {
if (!any_overrides) {
UINFO(8, "Cell parameters all match original values, skipping expansion.\n");
} else if (AstNodeModule* modp
= m_hierBlocks.findByParams(srcModp->name(), nodep->paramsp(), m_modp)) {
nodep->modp(modp);
nodep->modName(modp->name());
modp->dead(false);
// We need to relink the pins to the new module
relinkPinsByName(nodep->pinsp(), modp);
} else {
// If the name is very long, we don't want to overwhelm the filename limit
// We don't do this always, as it aids debugability to have intuitive naming.
// TODO can use new V3Name hash replacement instead of this
// Shorter name is convenient for hierarchical block
string newname = longname;
if (longname.length() > 30) {
if (longname.length() > 30 || srcModp->hierBlock()) {
LongMap::iterator iter = m_longMap.find(longname);
if (iter != m_longMap.end()) {
newname = iter->second;
} else {
newname = srcModp->name();
// We use all upper case above, so lower here can't conflict
newname += "__pi" + cvtToStr(++m_longId);
if (srcModp->hierBlock()) {
newname = parametrizedHierBlockName(srcModp, nodep->paramsp());
} else {
newname = srcModp->name();
// We use all upper case above, so lower here can't conflict
newname += "__pi" + cvtToStr(++m_longId);
}
m_longMap.insert(make_pair(longname, newname));
}
}
@ -772,6 +1001,8 @@ void ParamVisitor::visitCell(AstCell* nodep, const string& hierName) {
cellmodp->user5(false); // We need to re-recurse this module once changed
cellmodp->recursive(false);
cellmodp->recursiveClone(false);
// Only the first generation of clone holds this property
cellmodp->hierBlock(srcModp->hierBlock() && !srcModp->recursiveClone());
nodep->recursive(false);
// Recursion may need level cleanups
if (cellmodp->level() <= m_modp->level()) cellmodp->level(m_modp->level() + 1);
@ -814,17 +1045,18 @@ void ParamVisitor::visitCell(AstCell* nodep, const string& hierName) {
cloneIrefp->ifacep(pinIrefp->ifaceViaCellp());
UINFO(8, " IfaceNew " << cloneIrefp << endl);
}
// Assign parameters to the constants specified
// DOES clone() so must be finished with module clonep() before here
for (AstPin* pinp = nodep->paramsp(); pinp; pinp = VN_CAST(pinp->nextp(), Pin)) {
if (pinp->exprp()) {
if (cellmodp->hierBlock()) checkSupportedParam(cellmodp, pinp);
if (AstVar* modvarp = pinp->modVarp()) {
AstNode* newp = pinp->exprp(); // Const or InitArray
// Remove any existing parameter
if (modvarp->valuep()) modvarp->valuep()->unlinkFrBack()->deleteTree();
// Set this parameter to value requested by cell
modvarp->valuep(newp->cloneTree(false));
modvarp->overriddenParam(true);
} else if (AstParamTypeDType* modptp = pinp->modPTypep()) {
AstNodeDType* dtypep = VN_CAST(pinp->exprp(), NodeDType);
UASSERT_OBJ(dtypep, pinp, "unlinked param dtype");

View File

@ -292,7 +292,7 @@ void V3ParseImp::parseFile(FileLine* fileline, const string& modfilename, bool i
if (v3Global.opt.preprocOnly() || v3Global.opt.keepTempFiles()) {
// Create output file with all the preprocessor output we buffered up
string vppfilename
= v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "_" + modname + ".vpp";
= v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix() + "_" + modname + ".vpp";
std::ofstream* ofp = NULL;
std::ostream* osp;
if (v3Global.opt.preprocOnly()) {

View File

@ -231,7 +231,7 @@ void V3Stats::statsReport() {
UINFO(2, __FUNCTION__ << ": " << endl);
// Open stats file
string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__stats.txt";
string filename = v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix() + "__stats.txt";
std::ofstream* ofp(V3File::new_ofstream(filename));
if (ofp->fail()) v3fatal("Can't write " << filename);

View File

@ -84,15 +84,32 @@ string VString::upcase(const string& str) {
return out;
}
string VString::quotePercent(const string& str) {
string VString::quoteAny(const string& str, char tgt, char esc) {
string out;
for (string::const_iterator pos = str.begin(); pos != str.end(); ++pos) {
if (*pos == '%') out += '%';
if (*pos == tgt) out += esc;
out += *pos;
}
return out;
}
string VString::quoteStringLiteralForShell(const string& str) {
string result;
const char dquote = '"';
const char escape = '\\';
result.push_back(dquote); // Start quoted string
result.push_back(escape);
result.push_back(dquote); // "
for (string::const_iterator it = str.begin(); it != str.end(); ++it) {
if (*it == dquote || *it == escape) { result.push_back(escape); }
result.push_back(*it);
}
result.push_back(escape);
result.push_back(dquote); // "
result.push_back(dquote); // Terminate quoted string
return result;
}
string VString::spaceUnprintable(const string& str) {
string out;
for (string::const_iterator pos = str.begin(); pos != str.end(); ++pos) {

View File

@ -80,8 +80,15 @@ public:
static string downcase(const string& str);
// Convert string to upper case (toupper)
static string upcase(const string& str);
// Insert esc just before tgt
static string quoteAny(const string& str, char tgt, char esc);
// Replace any \'s with \\ (two consecutive backslashes)
static string quoteBackslash(const string& str) { return quoteAny(str, '\\', '\\'); }
// Replace any %'s with %%
static string quotePercent(const string& str);
static string quotePercent(const string& str) { return quoteAny(str, '%', '%'); }
// Surround a raw string by double quote and escape if necessary
// e.g. input abc's becomes "\"abc\'s\""
static string quoteStringLiteralForShell(const string& str);
// Replace any unprintable with space
// This includes removing tabs, so column tracking is correct
static string spaceUnprintable(const string& str);

View File

@ -52,6 +52,7 @@
#include "V3Gate.h"
#include "V3GenClk.h"
#include "V3Graph.h"
#include "V3HierBlock.h"
#include "V3Inline.h"
#include "V3Inst.h"
#include "V3Life.h"
@ -100,6 +101,13 @@
V3Global v3Global;
static void reportStatsIfEnabled() {
if (v3Global.opt.stats()) {
V3Stats::statsFinalAll(v3Global.rootp());
V3Stats::statsReport();
}
}
static void process() {
// Sort modules by level so later algorithms don't need to care
V3LinkLevel::modSortByLevel();
@ -139,6 +147,17 @@ static void process() {
V3Dead::deadifyModules(v3Global.rootp());
v3Global.checkTree();
// Create a hierarchical verilation plan
if (!v3Global.opt.lintOnly() && !v3Global.opt.xmlOnly() && v3Global.opt.hierarchical()) {
V3HierBlockPlan::createPlan(v3Global.rootp());
// If a plan is created, further analysis is not necessary.
// The actual Verilation will be done based on this plan.
if (v3Global.hierPlanp()) {
reportStatsIfEnabled();
return;
}
}
// Calculate and check widths, edit tree to TRUNC/EXTRACT any width mismatches
V3Width::width(v3Global.rootp());
@ -496,10 +515,7 @@ static void process() {
}
// Statistics
if (v3Global.opt.stats()) {
V3Stats::statsFinalAll(v3Global.rootp());
V3Stats::statsReport();
}
reportStatsIfEnabled();
if (!v3Global.opt.lintOnly() && !v3Global.opt.xmlOnly() && !v3Global.opt.dpiHdrOnly()) {
// Makefile must be after all other emitters
@ -517,8 +533,9 @@ static void verilate(const string& argString) {
// Can we skip doing everything if times are ok?
V3File::addSrcDepend(v3Global.opt.bin());
if (v3Global.opt.skipIdentical().isTrue()
&& V3File::checkTimes(
v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__verFiles.dat", argString)) {
&& V3File::checkTimes(v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix()
+ "__verFiles.dat",
argString)) {
UINFO(1, "--skip-identical: No change to any source files, exiting\n");
return;
}
@ -531,9 +548,9 @@ static void verilate(const string& argString) {
//--FRONTEND------------------
// Cleanup
V3Os::unlinkRegexp(v3Global.opt.makeDir(), v3Global.opt.prefix() + "_*.tree");
V3Os::unlinkRegexp(v3Global.opt.makeDir(), v3Global.opt.prefix() + "_*.dot");
V3Os::unlinkRegexp(v3Global.opt.makeDir(), v3Global.opt.prefix() + "_*.txt");
V3Os::unlinkRegexp(v3Global.opt.hierTopDataDir(), v3Global.opt.prefix() + "_*.tree");
V3Os::unlinkRegexp(v3Global.opt.hierTopDataDir(), v3Global.opt.prefix() + "_*.dot");
V3Os::unlinkRegexp(v3Global.opt.hierTopDataDir(), v3Global.opt.prefix() + "_*.txt");
// Internal tests (after option parsing as need debug() setting,
// and after removing files as may make debug output)
@ -567,15 +584,32 @@ static void verilate(const string& argString) {
V3Error::abortIfWarnings();
if (v3Global.hierPlanp()) { // This run is for just write a makefile
UASSERT(v3Global.opt.hierarchical(), "hierarchical must be set");
UASSERT(!v3Global.opt.hierChild(), "This must not be a hierarhcical-child run");
UASSERT(v3Global.opt.hierBlocks().empty(), "hierarchical-block must not be set");
if (v3Global.opt.gmake()) {
v3Global.hierPlanp()->writeCommandArgsFiles(false);
V3EmitMk::emitHierVerilation(v3Global.hierPlanp());
}
if (v3Global.opt.cmake()) {
v3Global.hierPlanp()->writeCommandArgsFiles(true);
V3EmitCMake::emit();
}
}
if (v3Global.opt.makeDepend().isTrue()) {
V3File::writeDepend(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__ver.d");
string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix();
filename += v3Global.opt.hierTop() ? "__hierVer.d" : "__ver.d";
V3File::writeDepend(filename);
}
if (v3Global.opt.protectIds()) {
VIdProtect::writeMapFile(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix()
VIdProtect::writeMapFile(v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix()
+ "__idmap.xml");
}
if (v3Global.opt.skipIdentical().isTrue() || v3Global.opt.makeDepend().isTrue()) {
V3File::writeTimes(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__verFiles.dat",
V3File::writeTimes(v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix()
+ "__verFiles.dat",
argString);
}
@ -624,6 +658,18 @@ static void execBuildJob() {
}
}
static void execHierVerilation() {
UASSERT(v3Global.hierPlanp(), "must be called only when plan exists");
const string makefile = v3Global.opt.prefix() + "_hier.mk ";
const string target = v3Global.opt.build() ? " hier_build" : " hier_verilation";
const string cmdStr = buildMakeCmd(makefile, target);
const int exit_code = V3Os::system(cmdStr);
if (exit_code != 0) {
v3error(cmdStr << " exitted with " << exit_code << std::endl);
exit(exit_code);
}
}
//######################################################################
int main(int argc, char** argv, char** env) {
@ -658,7 +704,14 @@ int main(int argc, char** argv, char** env) {
UINFO(1, "Option --no-verilate: Skip Verilation\n");
}
if (v3Global.opt.build()) execBuildJob();
if (v3Global.hierPlanp() && v3Global.opt.gmake()) {
execHierVerilation(); // execHierVerilation() takes care of --build too
} else if (v3Global.opt.build()) {
execBuildJob();
}
// Explicitly release resources
v3Global.shutdown();
UINFO(1, "Done, Exiting...\n");
}

View File

@ -116,6 +116,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"coverage_off" { FL; return yVLT_COVERAGE_OFF; }
"coverage_on" { FL; return yVLT_COVERAGE_ON; }
"full_case" { FL; return yVLT_FULL_CASE; }
"hier_block" { FL; return yVLT_HIER_BLOCK; }
"inline" { FL; return yVLT_INLINE; }
"isolate_assignments" { FL; return yVLT_ISOLATE_ASSIGNMENTS; }
"lint_off" { FL; return yVLT_LINT_OFF; }
@ -695,6 +696,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"/*verilator coverage_off*/" { FL_FWD; PARSEP->lexFileline()->coverageOn(false); FL_BRK; }
"/*verilator coverage_on*/" { FL_FWD; PARSEP->lexFileline()->coverageOn(true); FL_BRK; }
"/*verilator full_case*/" { FL; return yVL_FULL_CASE; }
"/*verilator hier_block*/" { FL; return yVL_HIER_BLOCK; }
"/*verilator inline_module*/" { FL; return yVL_INLINE_MODULE; }
"/*verilator isolate_assignments*/" { FL; return yVL_ISOLATE_ASSIGNMENTS; }
"/*verilator lint_off"[^*]*"*/" { FL; PARSEP->lexVerilatorCmtLint(yylval.fl, yytext, true); FL_BRK; }

View File

@ -360,6 +360,7 @@ BISONPRE_VERSION(3.0,%define parse.error verbose)
%token<fl> yVLT_COVERAGE_OFF "coverage_off"
%token<fl> yVLT_COVERAGE_ON "coverage_on"
%token<fl> yVLT_FULL_CASE "full_case"
%token<fl> yVLT_HIER_BLOCK "hier_block"
%token<fl> yVLT_INLINE "inline"
%token<fl> yVLT_ISOLATE_ASSIGNMENTS "isolate_assignments"
%token<fl> yVLT_LINT_OFF "lint_off"
@ -805,6 +806,7 @@ BISONPRE_VERSION(3.0,%define parse.error verbose)
%token<fl> yVL_CLOCK_ENABLE "/*verilator clock_enable*/"
%token<fl> yVL_COVERAGE_BLOCK_OFF "/*verilator coverage_block_off*/"
%token<fl> yVL_FULL_CASE "/*verilator full_case*/"
%token<fl> yVL_HIER_BLOCK "/*verilator hier_block*/"
%token<fl> yVL_INLINE_MODULE "/*verilator inline_module*/"
%token<fl> yVL_ISOLATE_ASSIGNMENTS "/*verilator isolate_assignments*/"
%token<fl> yVL_NO_CLOCKER "/*verilator no_clocker*/"
@ -2218,6 +2220,7 @@ non_port_module_item<nodep>: // ==IEEE: non_port_module_item
| yaSCIMPH { $$ = new AstScImpHdr($<fl>1,*$1); }
| yaSCCTOR { $$ = new AstScCtor($<fl>1,*$1); }
| yaSCDTOR { $$ = new AstScDtor($<fl>1,*$1); }
| yVL_HIER_BLOCK { $$ = new AstPragma($1,AstPragmaType::HIER_BLOCK); }
| yVL_INLINE_MODULE { $$ = new AstPragma($1,AstPragmaType::INLINE_MODULE); }
| yVL_NO_INLINE_MODULE { $$ = new AstPragma($1,AstPragmaType::NO_INLINE_MODULE); }
| yVL_PUBLIC_MODULE { $$ = new AstPragma($1,AstPragmaType::PUBLIC_MODULE); v3Global.dpi(true); }
@ -6184,6 +6187,8 @@ vltItem:
{ V3Config::addCaseFull(*$3, 0); }
| yVLT_FULL_CASE yVLT_D_FILE yaSTRING yVLT_D_LINES yaINTNUM
{ V3Config::addCaseFull(*$3, $5->toUInt()); }
| yVLT_HIER_BLOCK vltDModuleE
{ V3Config::addModulePragma(*$2, AstPragmaType::HIER_BLOCK); }
| yVLT_PARALLEL_CASE yVLT_D_FILE yaSTRING
{ V3Config::addCaseParallel(*$3, 0); }
| yVLT_PARALLEL_CASE yVLT_D_FILE yaSTRING yVLT_D_LINES yaINTNUM

View File

@ -0,0 +1,9 @@
%Error: --hierarchical-block requires the number of entries to be even
%Error: Module name 'param0' is duplicated in --hierarchical-block
%Error: --hierarchical-block requires at least two comma-separated values
%Error: --hierarchical-block must not end with \
%Error: --hierarchical-block does not allow 'a' after \
%Error: --hierarchical-block expects ',', but 'a' is passed
%Error: --hierarchical-block must not end with ','
%Error: --hierarchical-block does not allow '"' in the middle of literal
%Error: Exiting due to

View File

@ -0,0 +1,38 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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
scenarios(vlt => 1);
top_filename("t/t_hier_block.v");
lint(
fails => 1,
verilator_flags2 => ['--hierarchical-block',
'modName,mangledName,param0,"paramValue0",param0,"paramValue1",param1,2,param3',
'--hierarchical-block',
'modName',
'--hierarchical-block',
'mod0,mod1,\'"str\\\'', # end with backslash
'--hierarchical-block',
'mod2,mod3,\'"str\\a\'', # unexpected 'a' after backslash
'--hierarchical-block',
'mod4,mod5,\'"str"abc\',', # not end with "
'--hierarchical-block',
'mod6,mod7,\'"str"\',', # end with ,
'--hierarchical-block',
'mod8,mod9,\'s"tr"\',', # unexpected "
'--hierarchical-block',
'modA,modB,param,', # end with ,
],
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,17 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Copyright 2020 by Yutetsu TAKATSUKASA. 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
//
//*************************************************************************
extern "C" int dpi_export_func(int);
extern "C" int dpi_import_func(int v) {
return dpi_export_func(v) - 1;
}

38
test_regress/t/t_hier_block.pl Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
# stats will be deleted but generation will be skipped if libs of hierarchical blocks exist.
clean_objs();
scenarios(vlt_all => 1);
# Travis environment offers 2 VCPUs, 2 thread setting causes the following warning.
# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads.
# So use 6 threads here though it's not optimal in performace wise, but ok.
compile(
v_flags2 => ['t/t_hier_block.cpp'],
verilator_flags2 => ['--stats', ($Self->{vltmt} ? ' --threads 6' : ''),
'--hierarchical',
'--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus -time"'
],
);
execute(
check_finished => 1,
);
file_grep($Self->{obj_dir} . "/Vsub0/sub0.sv", /^module\s+(\S+)\s+/, "sub0");
file_grep($Self->{obj_dir} . "/Vsub1/sub1.sv", /^module\s+(\S+)\s+/, "sub1");
file_grep($Self->{obj_dir} . "/Vsub2/sub2.sv", /^module\s+(\S+)\s+/, "sub2");
file_grep($Self->{stats}, qr/HierBlock,\s+Hierarchical blocks\s+(\d+)/i, 10);
file_grep($Self->{run_log_filename}, qr/MACRO:(\S+) is defined/i, "cplusplus");
ok(1);
1;

View File

@ -0,0 +1,230 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 by Yutetsu TAKATSUKASA
`ifdef USE_VLT
`define HIER_BLOCK
`else
`define HIER_BLOCK /*verilator hier_block*/
`endif
interface byte_ifs(input clk);
logic [7:0] data;
modport sender(input clk, output data);
modport receiver(input clk, input data);
endinterface;
`ifdef AS_PROT_LIB
module secret (
clk
);
`else
module t (/*AUTOARG*/
// Inputs
clk
);
`endif
input clk;
`ifdef PROTLIB_TOP
secret i_secred(.clk(clk));
`else
wire [7:0] out0;
wire [7:0] out1;
wire [7:0] out2;
/* verilator lint_off UNOPTFLAT */
wire [7:0] out3;
wire [7:0] out3_2;
/* verilator lint_on UNOPTFLAT */
wire [7:0] out5;
wire [7:0] out6;
int count = 0;
non_hier_sub0 i_sub0(.clk(clk), .in(out3), .out(out0));
sub1 i_sub1(.clk(clk), .in(out0), .out(out1));
sub2 i_sub2(.clk(clk), .in(out1), .out(out2));
sub3 #(.P0(1)) i_sub3(.clk(clk), .in(out2), .out(out3));
// Must not use the same wrapper
sub3 #(.STR("abc"), .P0(1)) i_sub3_2(.clk(clk), .in(out2), .out(out3_2));
delay #(.N(2), 8) i_delay0(clk, out3, out5);
delay #(.N(3), 8) i_delay1(clk, out5, out6);
always_ff @(posedge clk) begin
if (out3 != out3_2) $stop;
$display("%d out0:%d %d %d %d %d", count, out0, out1, out2, out3, out5, out6);
if (count == 16) begin
if (out6 == 19) begin
$write("*-* All Finished *-*\n");
$finish;
end else begin
$write("Missmatch\n");
$stop;
end
end
count <= count + 1;
end
`ifdef CPP_MACRO
initial begin
$display("Macro for C++ compiler is defined for Verilator");
$stop;
end
`endif
`systemc_implementation
#include <iostream>
#define STRINGIFY_IMPL(str) #str
#define STRINGIFY(str) STRINGIFY_IMPL(str)
namespace {
struct statically_initialized {
statically_initialized() {
std::cout << "MACRO:" << STRINGIFY(CPP_MACRO) << " is defined" << std::endl;
}
} g_statically_initialized;
}
`verilog
`endif // PROTLIB_TOP
endmodule
module non_hier_sub0(
input wire clk,
input wire[7:0] in,
output wire [7:0] out);
sub0 i_sub0(.*);
endmodule
module sub0(
input wire clk,
input wire [7:0] in,
output wire [7:0] out); `HIER_BLOCK
logic [7:0] ff;
always_ff @(posedge clk) ff <= in;
assign out = ff;
endmodule
module sub1(
input wire clk,
input wire [7:0] in,
output wire [7:0] out); `HIER_BLOCK
logic [7:0] ff;
always_ff @(posedge clk) ff <= in + 1;
assign out = ff;
endmodule
module sub2(
input wire clk,
input wire [7:0] in,
output wire [7:0] out); `HIER_BLOCK
logic [7:0] ff;
// dpi_import_func returns (dpi_eport_func(v) -1)
import "DPI-C" context function int dpi_import_func(int v);
export "DPI-C" function dpi_export_func;
function int dpi_export_func(int v);
return v + 1;
endfunction
always_ff @(posedge clk) ff <= 8'(dpi_import_func({24'b0, in})) + 8'd2;
byte_ifs in_ifs(.clk(clk));
byte_ifs out_ifs(.clk(clk));
assign in_ifs.data = ff;
assign out = out_ifs.data;
non_hier_sub3 i_sub3(.in(in_ifs), .out(out_ifs));
always @(posedge clk)
// dotted access within a hierarchical block should be OK
if (i_sub3.in_wire != ff) begin
$display("Error mismatch in %m");
$stop;
end
endmodule
module non_hier_sub3(
byte_ifs.receiver in,
byte_ifs.sender out);
wire [7:0] in_wire, out_1, out_2;
assign in_wire = in.data;
localparam string sparam = "single quote escape comma:'\\,";
// Parameter appears in the different order from module declaration
sub3 #(.STR(sparam), .UNUSED(-16'sd3), .P0(8'd3)) i_sub3(.clk(in.clk), .in(in.data), .out(out_1));
// Instantiate again, should use the same wrapper
sub3 #(.STR(sparam), .UNUSED(-16'sd3), .P0(8'd3)) i_sub3_2(.clk(in.clk), .in(in.data), .out(out_2));
always @(posedge in.clk)
if (out_1 != out_2) $stop;
assign out.data = out_1;
endmodule
module sub3 #(
parameter logic [7:0] P0 = 2 + 1,
type TYPE = logic,
parameter int UNPACKED_ARRAY[2] = '{0, 1},
parameter logic signed [15:0] UNUSED = -3,
parameter string STR = "str") (
input wire clk,
input wire [7:0] in,
output wire [7:0] out); `HIER_BLOCK
initial $display("P0:%d UNUSED:%d %s", P0, UNUSED, STR);
TYPE [7:0] ff;
always_ff @(posedge clk) ff <= in + P0;
always_ff @(posedge clk) if (out4 != out4_2) $stop;
wire [7:0] out4;
wire [7:0] out4_2;
assign out = out4;
/* verilator lint_off REALCVT */
sub4 #(.P0(1.6), .P1(3.1), .P3(4.1)) i_sub4_0(.clk(clk), .in(ff), .out(out4)); // incr 2
sub4 #(.P0(2.4), .P1(3.1), .P3(5)) i_sub4_1(.clk(clk), .in(ff), .out(out4_2));
/* verilator lint_on REALCVT */
endmodule
module sub4 #(
parameter int P0 = 1.1,
parameter P1 = 2,
parameter real P3 = 3) (
input wire clk,
input wire [7:0] in,
output wire[7:0] out); `HIER_BLOCK
initial begin
if (P1 == 2) begin
$display("P1(%f) is not properly set", P1);
$stop;
end
end
reg [7:0] ff;
always_ff @(posedge clk) ff <= in + 8'(P0);
assign out = ff;
endmodule
module delay #(
parameter N = 1,
parameter WIDTH = 8) (
input wire clk,
input wire[WIDTH-1:0] in,
output wire [WIDTH-1:0]out); `HIER_BLOCK
reg [WIDTH-1:0] tmp;
always_ff @(posedge clk) tmp <= in;
if (N > 1) begin
delay #(.N(N - 1), WIDTH) i_delay(clk, tmp, out);
end else begin
assign out = tmp;
end
endmodule

View File

@ -0,0 +1,12 @@
%Error: t/t_hier_block0_bad.v:20:11: 'sub0' has hier_block metacomment, hierarchical verilation supports only integer/floating point/string parameters
: ... In instance t
20 | sub0 #(UNPACKED) i_sub0(.clk(clk), .in(8'(count)), .out(out0));
| ^~~~~~~~
%Error: t/t_hier_block0_bad.v:22:12: 'sub1' has hier_block metacomment, but 'parameter type' is not supported
: ... In instance t
22 | sub1 #(.T(logic[7:0])) i_sub1(.in(out0), .out(out1));
| ^
%Error: t/t_hier_block0_bad.v:26:42: Cannot access inside hierarchical block
26 | $display("%d %d %d", count, i_sub0.ff, out1);
| ^~
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
scenarios(vlt => 1);
lint(
fails => 1,
verilator_flags2 => ['--hierarchical'],
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,59 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 by Yutetsu TAKATSUKASA
`define HIER_BLOCK /*verilator hier_block*/
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
wire [7:0] out0;
wire [7:0] out1;
int count = 0;
// unpacked array cannot be passed to hierarchical block
localparam logic UNPACKED[0:1] = '{1'b1, 1'b0};
sub0 #(UNPACKED) i_sub0(.clk(clk), .in(8'(count)), .out(out0));
// Passing type parameter is not supported
sub1 #(.T(logic[7:0])) i_sub1(.in(out0), .out(out1));
always_ff @(posedge clk) begin
// dotted access under hierarchical block is not allowed
$display("%d %d %d", count, i_sub0.ff, out1);
if (count == 16) begin
if (out1 == 15) begin
$write("*-* All Finished *-*\n");
$finish;
end else begin
$write("Missmatch\n");
$stop;
end
end
count <= count + 1;
end
endmodule
module sub0 #(
parameter logic UNPACKED[0:1] = '{1'b0, 1'b1}
) (
input wire clk,
input wire [7:0] in,
output wire [7:0] out); `HIER_BLOCK
logic [7:0] ff;
always_ff @(posedge clk) ff <= in;
assign out = ff;
endmodule
module sub1 #(
parameter type T = logic
) (
input wire T in, output wire T out); `HIER_BLOCK
assign out = in;
endmodule

View File

@ -0,0 +1,15 @@
%Warning-HIERBLOCK: t/t_hier_block1_bad.v:15:8: Top module illegally marked hierarchical block, ignoring marking
: ... In instance t
... Suggest remove verilator hier_block on this module
15 | module t (
| ^
... Use "/* verilator lint_off HIERBLOCK */" and lint_on around source to disable this message.
%Error: t/t_hier_block1_bad.v:44:32: Modport cannot be used at the hierarchical block boundary
: ... In instance t.i_sub1
44 | module sub1 (byte_ifs.receiver in, byte_ifs.sender out); /*verilator hier_block*/
| ^~
%Error: t/t_hier_block1_bad.v:44:52: Modport cannot be used at the hierarchical block boundary
: ... In instance t.i_sub1
44 | module sub1 (byte_ifs.receiver in, byte_ifs.sender out); /*verilator hier_block*/
| ^~~
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
scenarios(vlt => 1);
lint(
fails => 1,
verilator_flags2 => ['--hierarchical'],
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,46 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 by Yutetsu TAKATSUKASA
`define HIER_BLOCK /*verilator hier_block*/
interface byte_ifs(input clk);
logic [7:0] data;
modport sender(input clk, output data);
modport receiver(input clk, input data);
endinterface;
module t (/*AUTOARG*/
// Inputs
clk
); `HIER_BLOCK // Top module can not be a hierarchy block
input wire clk;
wire [7:0] out0;
int count = 0;
byte_ifs in_ifs(.clk(clk));
byte_ifs out_ifs(.clk(clk));
assign in_ifs.data = out0;
sub0 i_sub0(.clk(clk), .in(count), .out(out0));
sub1 i_sub1(.in(in_ifs), .out(out_ifs));
endmodule
module sub0 (
input wire clk,
input wire [7:0] in,
output wire [7:0] out); `HIER_BLOCK
logic [7:0] ff;
always_ff @(posedge clk) ff <= in;
assign out = ff;
endmodule
module sub1 (byte_ifs.receiver in, byte_ifs.sender out); `HIER_BLOCK
assign out.data = in.data;
endmodule

View File

@ -0,0 +1,52 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
# If a test fails, broken .cmake may distrub the next run
clean_objs();
scenarios(simulator => 1);
top_filename("t/t_hier_block.v");
# Travis environment offers 2 VCPUs, 2 thread setting causes the following warning.
# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads.
# So use 6 threads here though it's not optimal in performace wise, but ok.
compile(
verilator_make_gmake => 0,
verilator_make_cmake => 1,
v_flags2 => ['t/t_hier_block.cpp'],
verilator_flags2 => ['--stats',
'--hierarchical',
'--CFLAGS', "'-pipe -DCPP_MACRO=cplusplus -time'",
'--make cmake',
($Self->{vltmt} ? ' --threads 6' : '')],
);
system("cmake --version");
if ($? != 0) {
skip("cmake is not installed");
} else {
my $cmakecache = $Self->{obj_dir}."/CMakeCache.txt";
if (! -e $cmakecache) {
error("$cmakecache does not exist.")
}
execute(
check_finished => 1,
);
file_grep($Self->{obj_dir} . "/Vsub0/sub0.sv", /^module\s+(\S+)\s+/, "sub0");
file_grep($Self->{obj_dir} . "/Vsub1/sub1.sv", /^module\s+(\S+)\s+/, "sub1");
file_grep($Self->{obj_dir} . "/Vsub2/sub2.sv", /^module\s+(\S+)\s+/, "sub2");
file_grep($Self->{obj_dir} . '/Vt_hier_block_cmake__stats.txt', qr/HierBlock,\s+Hierarchical blocks\s+(\d+)/i, 10);
file_grep($Self->{run_log_filename}, qr/MACRO:(\S+) is defined/i, "cplusplus");
}
ok(1);
1;

View File

@ -0,0 +1,38 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
# This test makes sure that the intenral check of t_hier_block.v is correct.
# --hierarchical option is not set intentionally.
# stats will be deleted but generation will be skipped if libs of hierarchical blocks exist.
clean_objs();
scenarios(vlt_all => 1);
top_filename("t/t_hier_block.v");
# Travis environment offers 2 VCPUs, 2 thread setting causes the following warning.
# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads.
# So use 6 threads here though it's not optimal in performace wise, but ok.
compile(
v_flags2 => ['t/t_hier_block.cpp'],
verilator_flags2 => ['--stats',
'+define+USE_VLT', 't/t_hier_block_vlt.vlt',
'--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus -time"',
($Self->{vltmt} ? ' --threads 6' : '')],
);
execute(
check_finished => 1,
);
file_grep_not($Self->{stats}, qr/HierBlock,\s+Hierarchical blocks\s+(\d+)/i);
file_grep($Self->{run_log_filename}, qr/MACRO:(\S+) is defined/i, "cplusplus");
ok(1);
1;

View File

@ -0,0 +1,65 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
top_filename("t/t_hier_block.v");
scenarios(vlt_all => 1, xsim => 1);
my $secret_prefix = "secret";
my $secret_dir = "$Self->{obj_dir}/$secret_prefix";
mkdir $secret_dir;
while (1) {
# Always compile the secret file with Verilator no matter what simulator
# we are testing with
run(logfile => "$secret_dir/vlt_compile.log",
cmd => ["perl",
"$ENV{VERILATOR_ROOT}/bin/verilator",
"-cc",
"--hierarchical",
"-Mdir",
$secret_dir,
"--protect-lib",
$secret_prefix,
"--protect-key",
"PROTECT_KEY",
"t/t_hier_block.v",
"-DAS_PROT_LIB",
'--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus -time"',
$Self->{vltmt} ? ' --threads 1' : '',
"--build"],
verilator_run => 1,
);
last if $Self->{errors};
compile(
v_flags2 => ['t/t_hier_block.cpp'],
verilator_flags2 => ["$secret_dir/secret.sv",
"-DPROTLIB_TOP",
"--top-module t",
"-LDFLAGS",
"'$secret_prefix/libsecret.a'"],
);
execute(
check_finished => 1,
);
ok(1);
last;
}
file_grep($secret_dir . "/Vsub0/sub0.sv", /^module\s+(\S+)\s+/, "sub0");
file_grep($secret_dir . "/Vsub1/sub1.sv", /^module\s+(\S+)\s+/, "sub1");
file_grep($secret_dir . "/Vsub2/sub2.sv", /^module\s+(\S+)\s+/, "sub2");
file_grep($Self->{run_log_filename}, qr/MACRO:(\S+) is defined/i, "cplusplus");
ok(1);
1;

View File

@ -0,0 +1,41 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
# stats will be deleted but generation will be skipped if libs of hierarchical blocks exist.
clean_objs();
top_filename("t/t_hier_block.v");
# Travis environment offers 2 VCPUs, 2 thread setting causes the following warning.
# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads.
# So use 6 threads here though it's not optimal in performace wise, but ok.
scenarios(vlt_all => 1);
compile(
v_flags2 => ['t/t_hier_block.cpp'],
verilator_flags2 => ['--sc',
'--stats',
'--hierarchical',
($Self->{vltmt} ? ' --threads 6' : ''),
'--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus -time"'
],
);
execute(
check_finished => 1,
);
file_grep($Self->{obj_dir} . "/Vsub0/sub0.sv", /^module\s+(\S+)\s+/, "sub0");
file_grep($Self->{obj_dir} . "/Vsub1/sub1.sv", /^module\s+(\S+)\s+/, "sub1");
file_grep($Self->{obj_dir} . "/Vsub2/sub2.sv", /^module\s+(\S+)\s+/, "sub2");
file_grep($Self->{stats}, qr/HierBlock,\s+Hierarchical blocks\s+(\d+)/i, 10);
file_grep($Self->{run_log_filename}, qr/MACRO:(\S+) is defined/i, "cplusplus");
ok(1);
1;

View File

@ -0,0 +1,39 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
# stats will be deleted but generation will be skipped if libs of hierarchical blocks exist.
clean_objs();
scenarios(vlt_all => 1);
top_filename("t/t_hier_block.v");
# Travis environment offers 2 VCPUs, 2 thread setting causes the following warning.
# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads.
# So use 6 threads here though it's not optimal in performace wise, but ok.
compile(
v_flags2 => ['t/t_hier_block.cpp'],
verilator_flags2 => ['--stats',
'--hierarchical',
'+define+USE_VLT', 't/t_hier_block_vlt.vlt',
'--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus -time"',
($Self->{vltmt} ? ' --threads 6' : '')],
);
execute(
check_finished => 1,
);
file_grep($Self->{obj_dir} . "/Vsub0/sub0.sv", /^module\s+(\S+)\s+/, "sub0");
file_grep($Self->{obj_dir} . "/Vsub1/sub1.sv", /^module\s+(\S+)\s+/, "sub1");
file_grep($Self->{obj_dir} . "/Vsub2/sub2.sv", /^module\s+(\S+)\s+/, "sub2");
file_grep($Self->{stats}, qr/HierBlock,\s+Hierarchical blocks\s+(\d+)/i, 10);
file_grep($Self->{run_log_filename}, qr/MACRO:(\S+) is defined/i, "cplusplus");
ok(1);
1;

View File

@ -0,0 +1,8 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 by Yutetsu TAKATSUKASA
`verilator_config
hier_block -module "sub?"
hier_block -module "delay"