Add --lib-create, similar to --protect-lib but without protections (#3200).

This commit is contained in:
Wilson Snyder 2021-11-14 09:39:31 -05:00
parent 4b593f8eb3
commit 899de9a282
16 changed files with 155 additions and 72 deletions

View File

@ -11,11 +11,15 @@ contributors that suggested a given feature are shown in []. Thanks!
Verilator 4.215 devel
==========================
**Major:**
* Add --lib-create, similar to --protect-lib but without protections.
**Minor:**
* Internal code cleanups and improvements. [Geza Lore]
* Improve --thread verilation-time performance.
* Fix array method names with parens (#3181) (#3183). [Teng Huang]
* Fix array method names with parenthesis (#3181) (#3183). [Teng Huang]
* Fix split_var assign merging (#3177) (#3179). [Yutetsu TAKATSUKASA]
* Fix nested generate if genblk naming (#3189). [yanx21]

View File

@ -336,6 +336,7 @@ detailed descriptions of these arguments.
-LDFLAGS <flags> Linker pre-object arguments for makefile
--l2-name <value> Verilog scope name of the top module
--language <lang> Default language standard to parse
--lib-create <name> Create a DPI library
+libext+<ext>+[ext]... Extensions for finding modules
--lint-only Lint, but do not make output
-MAKEFLAGS <flags> Arguments to pass to make during --build

View File

@ -575,6 +575,23 @@ Summary:
"+libext+" is fairly standard across Verilog tools. Defaults to
".v+.sv".
.. option:: --lib-create <name>
Produces C++, Verilog wrappers and a Makefile which can in turn produce
a DPI library which can be used by Verilator or other simulators along
with the corresponding Verilog wrapper. The Makefile will build both a
static and dynamic version of the library named :file:`lib<name>.a` and
:file:`lib<name>.so` respectively. This is done because some simulators
require a dynamic library, but the static library is arguably easier to
use if possible. :vlopt:`--protect-lib` implies :vlopt:`--protect-ids`.
When using :vlopt:`--lib-create` it is advised to also use
:vlopt:`--timescale-override /1fs <--timescale-override>` to ensure the
model has a time resolution that is always compatible with the time
precision of the upper instantiating module.
See also :vlopt:`--protect-lib`.
.. option:: --lint-only
Check the files for lint violations only, do not create any other
@ -872,24 +889,15 @@ Summary:
.. option:: --protect-lib <name>
Produces C++, Verilog wrappers and a Makefile which can in turn produce
a DPI library which can be used by Verilator or other simulators along
with the corresponding Verilog wrapper. The Makefile will build both a
static and dynamic version of the library named :file:`lib<name>.a` and
:file:`lib<name>.so` respectively. This is done because some simulators
require a dynamic library, but the static library is arguably easier to
use if possible. :vlopt:`--protect-lib` implies :vlopt:`--protect-ids`.
Produces a DPI library similar to :vlopt:`--lib-create`, but hides
internal design details. :vlopt:`--protect-lib` implies
:vlopt:`--protect-ids`, and :vlopt:`--lib-create`.
This allows for the secure delivery of sensitive IP without the need for
encrypted RTL (i.e. IEEE P1735). See :file:`examples/make_protect_lib`
in the distribution for a demonstration of how to build and use the DPI
library.
When using :vlopt:`--protect-lib` it is advised to also use
:vlopt:`--timescale-override /1fs <--timescale-override>` to ensure the
model has a time resolution that is always compatible with the time
precision of the upper instantiating module.
.. option:: --private
Opposite of :vlopt:`--public`. Is the default; this option exists for

View File

@ -70,10 +70,9 @@ Verilator is run in hierarchical mode on the whole SoC. Verilator will
make two models, one for the CPU hierarchy block, and one for the SoC. The
Verialted code for the SoC will automatically call the CPU Verilated model.
The current hierarchical Verilation is based on protect-lib. Each hierarchy
block is Verilated to a protect-lib. User modules of the hierarchy blocks
will see a tiny wrapper generated by protect-lib instead of the actual
design.
The current hierarchical Verilation is based on :vlopt:`--lib-create`. Each
hierarchy block is Verilated into a library. User modules of the hierarchy
blocks will see a tiny wrapper generated by :vlopt:`--lib-create`.
Usage

View File

@ -25,7 +25,7 @@
//=========================================================================
// Internal note:
//
// verilated.o may exist both in protect-lib (incrementally linked .a/.so)
// verilated.o may exist both in --lib-create (incrementally linked .a/.so)
// and the main module. Both refer the same instance of static
// variables/VL_THREAD_LOCAL in verilated.o such as Verilated, or
// VerilatedImpData. This is important to share that state, but the

View File

@ -178,8 +178,8 @@ class CMakeEmitter final {
if (v3Global.opt.mtasks()) {
global.emplace_back("${VERILATOR_ROOT}/include/verilated_threads.cpp");
}
if (!v3Global.opt.protectLib().empty()) {
global.emplace_back(v3Global.opt.makeDir() + "/" + v3Global.opt.protectLib() + ".cpp");
if (!v3Global.opt.libCreate().empty()) {
global.emplace_back(v3Global.opt.makeDir() + "/" + v3Global.opt.libCreate() + ".cpp");
}
*of << "# Global classes, need linked once per executable\n";
@ -238,7 +238,7 @@ class CMakeEmitter final {
// with .so
<< ")\n";
}
*of << "\n# Verilate the top module that refers protect-lib wrappers of above\n";
*of << "\n# Verilate the top module that refers to lib-create wrappers of above\n";
*of << "verilate(${TOP_TARGET_NAME} PREFIX " << v3Global.opt.prefix() << " TOP_MODULE "
<< v3Global.rootp()->topModulep()->name() << " DIRECTORY "
<< deslash(v3Global.opt.makeDir()) << " SOURCES ";

View File

@ -145,8 +145,8 @@ public:
if (v3Global.opt.exe()) {
of.puts("default: " + v3Global.opt.exeName() + "\n");
} else if (!v3Global.opt.protectLib().empty()) {
of.puts("default: lib" + v3Global.opt.protectLib() + "\n");
} else if (!v3Global.opt.libCreate().empty()) {
of.puts("default: lib" + v3Global.opt.libCreate() + "\n");
} else {
of.puts("default: " + v3Global.opt.prefix() + "__ALL.a\n");
}
@ -188,7 +188,7 @@ public:
of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n");
of.puts("VM_USER_CFLAGS = \\\n");
if (!v3Global.opt.protectLib().empty()) of.puts("\t-fPIC \\\n");
if (!v3Global.opt.libCreate().empty()) of.puts("\t-fPIC \\\n");
const V3StringList& cFlags = v3Global.opt.cFlags();
for (const string& i : cFlags) of.puts("\t" + i + " \\\n");
of.puts("\n");
@ -243,19 +243,19 @@ public:
of.puts("\n");
}
if (!v3Global.opt.protectLib().empty()) {
const string protectLibDeps = "$(VK_OBJS) $(VK_GLOBAL_OBJS) "
+ v3Global.opt.protectLib() + ".o $(VM_HIER_LIBS)";
of.puts("\n### Library rules from --protect-lib\n");
if (!v3Global.opt.libCreate().empty()) {
const string libCreateDeps = "$(VK_OBJS) $(VK_GLOBAL_OBJS) " + v3Global.opt.libCreate()
+ ".o $(VM_HIER_LIBS)";
of.puts("\n### Library rules from --lib-create\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(v3Global.opt.libCreateName(false) + ": " + libCreateDeps + "\n");
of.puts("\n");
if (v3Global.opt.hierChild()) {
// Hierarchical child does not need .so because hierTop() will create .so from .a
of.puts("lib" + v3Global.opt.protectLib() + ": "
+ v3Global.opt.protectLibName(false) + "\n");
of.puts("lib" + v3Global.opt.libCreate() + ": " + v3Global.opt.libCreateName(false)
+ "\n");
} else {
of.puts(v3Global.opt.protectLibName(true) + ": " + protectLibDeps + "\n");
of.puts(v3Global.opt.libCreateName(true) + ": " + libCreateDeps + "\n");
// Linker on mac emits an error if all symbols are not found here,
// but some symbols that are referred as "DPI-C" can not be found at this moment.
// So add dynamic_lookup
@ -267,9 +267,8 @@ public:
"\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -shared -o $@ $^\n");
of.puts("endif\n");
of.puts("\n");
of.puts("lib" + v3Global.opt.protectLib() + ": "
+ v3Global.opt.protectLibName(false) + " "
+ v3Global.opt.protectLibName(true) + "\n");
of.puts("lib" + v3Global.opt.libCreate() + ": " + v3Global.opt.libCreateName(false)
+ " " + v3Global.opt.libCreateName(true) + "\n");
}
}

View File

@ -18,8 +18,9 @@
// - 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 internally uses --lib-create for each
// hierarchical block. Upper modules read the wrapper from --lib-create
// instead of the Verilog design.
//
// Hierarchical Verilation runs as the following step
// 1) Find modules marked by /*verilator hier_block*/ metacomment
@ -29,7 +30,7 @@
//
// 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)
// b) To --lib-create on 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.
@ -52,7 +53,7 @@
// 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).
// 6) In V3Param.cpp, use --lib-create wrapper of the 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).
@ -152,8 +153,9 @@ V3StringList V3HierBlock::commandArgs(bool forCMake) const {
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(" --lib-create " + modp()->name()); // possibly mangled name
if (v3Global.opt.protectKeyProvided())
opts.push_back(" --protect-key " + v3Global.opt.protectKeyDefaulted());
opts.push_back(" --hierarchical-child");
const StrGParams gparamsStr = stringifyParams(gparams(), true);
@ -418,8 +420,10 @@ void V3HierBlockPlan::writeCommandArgsFiles(bool forCMake) const {
*of << it->second->hierBlockArgs().front() << "\n";
}
if (!v3Global.opt.protectLib().empty()) {
*of << "--protect-lib " << v3Global.opt.protectLib() << "\n";
if (!v3Global.opt.libCreate().empty()) {
*of << "--lib-create " << v3Global.opt.libCreate() << "\n";
}
if (v3Global.opt.protectKeyProvided()) {
*of << "--protect-key " << v3Global.opt.protectKeyDefaulted() << "\n";
}
if (v3Global.opt.threads() > 0) {

View File

@ -263,9 +263,9 @@ private:
virtual void visit(AstPragma* nodep) override {
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,
// If this is hierarchical mode which is to lib-create,
// 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
// But .vlt files may still mark a module which is actually a lib-create wrapper
// hier_block. AstNodeModule::hierBlock() can be true only when --hierarchical is
// specified.
m_modp->hierBlock(v3Global.opt.hierarchical());

View File

@ -500,9 +500,9 @@ string V3Options::filePathCheckOneDir(const string& modname, const string& dirna
// 1: Delete the option which has no argument
// 2: Delete the option and its argument
int V3Options::stripOptionsForChildRun(const string& opt, bool forTop) {
if (opt == "Mdir" || opt == "clk" || opt == "f" || opt == "j" || opt == "l2-name"
|| opt == "mod-prefix" || opt == "prefix" || opt == "protect-lib" || opt == "protect-key"
|| opt == "threads" || opt == "top-module" || opt == "v") {
if (opt == "Mdir" || opt == "clk" || opt == "lib-create" || opt == "f" || opt == "j"
|| opt == "l2-name" || opt == "mod-prefix" || opt == "prefix" || opt == "protect-lib"
|| opt == "protect-key" || opt == "threads" || opt == "top-module" || opt == "v") {
return 2;
}
if (opt == "build" || (!forTop && (opt == "cc" || opt == "exe" || opt == "sc"))
@ -732,11 +732,6 @@ void V3Options::notify() {
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()) {
@ -1121,6 +1116,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
};
DECL_OPTION("-default-language", CbVal, setLang);
DECL_OPTION("-language", CbVal, setLang);
DECL_OPTION("-lib-create", Set, &m_libCreate);
DECL_OPTION("-lint-only", OnOff, &m_lintOnly);
DECL_OPTION("-l2-name", Set, &m_l2Name);
DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical
@ -1237,7 +1233,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
DECL_OPTION("-protect-ids", OnOff, &m_protectIds);
DECL_OPTION("-protect-key", Set, &m_protectKey);
DECL_OPTION("-protect-lib", CbVal, [this](const char* valp) {
m_protectLib = valp;
m_libCreate = valp;
m_protectIds = true;
});
DECL_OPTION("-public", OnOff, &m_public);

View File

@ -321,12 +321,12 @@ private:
string m_exeName; // main switch: -o {name}
string m_flags; // main switch: -f {name}
string m_l2Name; // main switch: --l2name; "" for top-module's name
string m_libCreate; // main switch: --lib-create {lib_name}
string m_makeDir; // main switch: -Mdir
string m_modPrefix; // main switch: --mod-prefix
string m_pipeFilter; // main switch: --pipe-filter
string m_prefix; // main switch: --prefix
string m_protectKey; // main switch: --protect-key
string m_protectLib; // main switch: --protect-lib {lib_name}
string m_topModule; // main switch: --top-module
string m_unusedRegexp; // main switch: --unused-regexp
string m_waiverOutput; // main switch: --waiver-output {filename}
@ -526,14 +526,9 @@ public:
string exeName() const { return m_exeName != "" ? m_exeName : prefix(); }
string l2Name() const { return m_l2Name; }
string makeDir() const { return m_makeDir; }
string modPrefix() const { return m_modPrefix; }
string pipeFilter() const { return m_pipeFilter; }
string prefix() const { return m_prefix; }
string protectKeyDefaulted(); // Set default key if not set by user
string protectLib() const { return m_protectLib; }
string protectLibName(bool shared) {
string libName = "lib" + protectLib();
string libCreate() const { return m_libCreate; }
string libCreateName(bool shared) {
string libName = "lib" + libCreate();
if (shared) {
libName += ".so";
} else {
@ -541,6 +536,13 @@ public:
}
return libName;
}
string makeDir() const { return m_makeDir; }
string modPrefix() const { return m_modPrefix; }
string pipeFilter() const { return m_pipeFilter; }
string prefix() const { return m_prefix; }
// Not just called protectKey() to avoid bugs of not using protectKeyDefaulted()
bool protectKeyProvided() const { return !m_protectKey.empty(); }
string protectKeyDefaulted(); // Set default key if not set by user
string topModule() const { return m_topModule; }
string unusedRegexp() const { return m_unusedRegexp; }
string waiverOutput() const { return m_waiverOutput; }

View File

@ -127,7 +127,7 @@ public:
if (m_hierBlockOptsByOrigName.find(origName) == m_hierBlockOptsByOrigName.end()) {
return nullptr;
}
// This module is a hierarchical block. Need to replace it by the protect-lib wrapper.
// This module is a hierarchical block. Need to replace it by the --lib-create wrapper.
const std::pair<HierMapIt, HierMapIt> candidates
= m_hierBlockOptsByOrigName.equal_range(origName);
const auto paramsIt = m_modParams.find(origName);
@ -168,7 +168,7 @@ public:
}
if (found && paramIdx == hierIt->second->params().size()) break;
}
UASSERT_OBJ(hierIt != candidates.second, firstPinp, "No protect-lib wrapper found");
UASSERT_OBJ(hierIt != candidates.second, firstPinp, "No --lib-create wrapper found");
// parameter settings will be removed in the bottom of caller visitCell().
const HierBlockModMap::const_iterator modIt
= m_hierBlockMod.find(hierIt->second->mangledName());
@ -258,7 +258,7 @@ class ParamProcessor final {
AstNodeModule* m_modp = nullptr; // Current module being processed
// Database to get protect-lib wrapper that matches parameters in hierarchical Verilation
// Database to get lib-create wrapper that matches parameters in hierarchical Verilation
ParameterizedHierBlocks m_hierBlocks;
// Default parameter values key:parameter name, value:default value (can be nullptr)
using DefaultValueMap = std::map<std::string, AstConst*>;

View File

@ -57,8 +57,8 @@ private:
AstTextBlock* m_cSeqClksp = nullptr; // Sequential clock copy list
AstTextBlock* m_cSeqOutsp = nullptr; // Sequential output copy list
AstTextBlock* m_cIgnoreParamsp = nullptr; // Combo ignore parameter list
string m_libName;
string m_topName;
const string m_libName;
const string m_topName;
bool m_foundTop = false; // Have seen the top module
bool m_hasClk = false; // True if the top module has sequential logic
@ -396,7 +396,7 @@ private:
} else if (nodep->direction() == VDirection::OUTPUT) {
handleOutput(nodep);
} else {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: protect-lib port direction: "
nodep->v3warn(E_UNSUPPORTED, "Unsupported: --lib-create port direction: "
<< nodep->direction().ascii());
}
}
@ -484,7 +484,7 @@ private:
public:
explicit ProtectVisitor(AstNode* nodep)
: m_libName{v3Global.opt.protectLib()}
: m_libName{v3Global.opt.libCreate()}
, m_topName{v3Global.opt.prefix()} {
iterate(nodep);
}

View File

@ -536,7 +536,7 @@ static void process() {
}
// Output DPI protected library files
if (!v3Global.opt.protectLib().empty()) {
if (!v3Global.opt.libCreate().empty()) {
V3ProtectLib::protect();
V3EmitV::emitvFiles();
V3EmitC::emitcFiles();

70
test_regress/t/t_lib.pl Executable file
View File

@ -0,0 +1,70 @@
#!/usr/bin/env perl
# Makes the test run with tracing enabled by default, can be overridden
# with --notrace
unshift(@ARGV, "--trace");
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2019 by Todd Strader. 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,
xsim => 1,
);
top_filename("t/t_lib_prot.v");
$Self->{sim_time} = $Self->{benchmark} * 100 if $Self->{benchmark};
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",
"--prefix",
"Vt_lib_prot_secret",
"-cc",
"-Mdir",
$secret_dir,
"--lib-create",
$secret_prefix,
"t/t_lib_prot_secret.v"],
verilator_run => 1,
);
last if $Self->{errors};
run(logfile => "$secret_dir/secret_gcc.log",
cmd=>[$ENV{MAKE},
"-C",
$secret_dir,
"-f",
"Vt_lib_prot_secret.mk"]);
last if $Self->{errors};
compile(
verilator_flags2 => ["$secret_dir/secret.sv",
"-LDFLAGS",
"$secret_prefix/libsecret.a"],
xsim_flags2 => ["$secret_dir/secret.sv"],
);
execute(
check_finished => 1,
xsim_run_flags2 => ["--sv_lib",
"$secret_dir/libsecret",
"--dpi_absolute"],
);
ok(1);
last;
}
1;

View File

@ -1,4 +1,4 @@
%Error-UNSUPPORTED: t/t_lib_prot_inout_bad.v:9:28: Unsupported: protect-lib port direction: INOUT
%Error-UNSUPPORTED: t/t_lib_prot_inout_bad.v:9:28: Unsupported: --lib-create port direction: INOUT
9 | inout z,
| ^
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest