forked from github/verilator
Support hierarchical verilation using protect lib (#2206)
This commit is contained in:
parent
f3b28c5c74
commit
953a442827
103
bin/verilator
103
bin/verilator
@ -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
|
||||
|
@ -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
|
||||
|
@ -203,6 +203,7 @@ RAW_OBJS = \
|
||||
V3GraphPathChecker.o \
|
||||
V3GraphTest.o \
|
||||
V3Hashed.o \
|
||||
V3HierBlock.o \
|
||||
V3Inline.o \
|
||||
V3Inst.o \
|
||||
V3InstrCount.o \
|
||||
|
@ -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; }
|
||||
|
@ -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>
|
||||
|
@ -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; }
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
150
src/V3EmitMk.cpp
150
src/V3EmitMk.cpp
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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()); }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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
434
src/V3HierBlock.cpp
Normal 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
128
src/V3HierBlock.h
Normal 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
|
@ -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() {}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
246
src/V3Param.cpp
246
src/V3Param.cpp
@ -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");
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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
|
||||
|
9
test_regress/t/t_flag_hier0_bad.out
Normal file
9
test_regress/t/t_flag_hier0_bad.out
Normal 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
|
38
test_regress/t/t_flag_hier0_bad.pl
Executable file
38
test_regress/t/t_flag_hier0_bad.pl
Executable 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;
|
17
test_regress/t/t_hier_block.cpp
Normal file
17
test_regress/t/t_hier_block.cpp
Normal 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
38
test_regress/t/t_hier_block.pl
Executable 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;
|
230
test_regress/t/t_hier_block.v
Normal file
230
test_regress/t/t_hier_block.v
Normal 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
|
12
test_regress/t/t_hier_block0_bad.out
Normal file
12
test_regress/t/t_hier_block0_bad.out
Normal 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
|
19
test_regress/t/t_hier_block0_bad.pl
Executable file
19
test_regress/t/t_hier_block0_bad.pl
Executable 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;
|
59
test_regress/t/t_hier_block0_bad.v
Normal file
59
test_regress/t/t_hier_block0_bad.v
Normal 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
|
15
test_regress/t/t_hier_block1_bad.out
Normal file
15
test_regress/t/t_hier_block1_bad.out
Normal 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
|
19
test_regress/t/t_hier_block1_bad.pl
Executable file
19
test_regress/t/t_hier_block1_bad.pl
Executable 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;
|
46
test_regress/t/t_hier_block1_bad.v
Normal file
46
test_regress/t/t_hier_block1_bad.v
Normal 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
|
52
test_regress/t/t_hier_block_cmake.pl
Executable file
52
test_regress/t/t_hier_block_cmake.pl
Executable 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;
|
38
test_regress/t/t_hier_block_nohier.pl
Executable file
38
test_regress/t/t_hier_block_nohier.pl
Executable 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;
|
65
test_regress/t/t_hier_block_prot_lib.pl
Executable file
65
test_regress/t/t_hier_block_prot_lib.pl
Executable 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;
|
41
test_regress/t/t_hier_block_sc.pl
Executable file
41
test_regress/t/t_hier_block_sc.pl
Executable 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;
|
39
test_regress/t/t_hier_block_vlt.pl
Executable file
39
test_regress/t/t_hier_block_vlt.pl
Executable 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;
|
8
test_regress/t/t_hier_block_vlt.vlt
Normal file
8
test_regress/t/t_hier_block_vlt.vlt
Normal 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"
|
Loading…
Reference in New Issue
Block a user