Add --protect-lib to create protected libraries, bug1490.

This commit is contained in:
Todd Strader 2019-10-09 06:47:26 -04:00
parent 58a42834a6
commit da0da9e258
29 changed files with 1261 additions and 68 deletions

View File

@ -17,6 +17,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
* Verilator 4.020 2019-10-06
** Add --protect-lib, bug1490. [Todd Strader]
*** Support $fseek, $ftell, $frewind, bug1496. [Howard Su]
*** Add --public-flat-rw, bug1511. [Stefan Wallentowitz]

View File

@ -108,6 +108,7 @@ SUBDIRS = src test_regress \
examples/make_hello_sc \
examples/make_tracing_c \
examples/make_tracing_sc \
examples/make_protect_lib \
INFOS = README README.html README.pdf \
verilator.txt verilator.html verilator.pdf
@ -336,6 +337,7 @@ installdata:
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_hello_sc
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_tracing_c
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_tracing_sc
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_protect_lib
cd $(srcdir) \
; for p in $(VL_INST_DATA_SRCDIR_FILES) ; do \
$(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p; \
@ -360,6 +362,7 @@ uninstall:
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_hello_sc
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_tracing_c
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_tracing_sc
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_protect_lib
-rmdir $(DESTDIR)$(pkgdatadir)/examples
-rmdir $(DESTDIR)$(pkgdatadir)
-rmdir $(DESTDIR)$(pkgconfigdir)
@ -492,7 +495,7 @@ clean mostlyclean distclean maintainer-clean::
rm -f *.pg *.pgs *.toc *.tp *.tps *.vr *.vrs *.idx
rm -f *.ev *.evs *.ov *.ovs *.cv *.cvs *.ma *.mas
rm -f *.tex
rm -rf examples/*/obj_dir examples/*/logs
rm -rf examples/*/obj_dir* examples/*/logs
rm -rf test_*/obj_dir
rm -rf nodist/obj_dir

View File

@ -256,6 +256,7 @@ The directories in the package directory are as follows:
examples/make_hello_sc => Example GNU-make simple Verilog->SystemC conversion
examples/make_tracing_c => Example GNU-make Verilog->C++ with tracing
examples/make_tracing_sc => Example GNU-make Verilog->SystemC with tracing
examples/make_protect_lib => Example using --protect-lib
include/ => Files that should be in your -I compiler path
include/verilated*.cpp => Global routines to link into your simulator
include/verilated*.h => Global headers

View File

@ -354,6 +354,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
--prof-threads Enable generating gantt chart data for threads
--protect-key <key> Key for symbol protection
--protect-ids Hash identifier names for obscurity
--protect-lib <name> Create a DPI protected library
--private Debugging; see docs
--public Debugging; see docs
--public-flat-rw Mark all variables, etc as public_flat_rw
@ -1191,6 +1192,20 @@ of information disclosed, which is limited to the DPI function prototyptes.
Use of the VPI is not recommended as many design details may be exposed,
and an INSECURE warning will be issued.
=item --protect-lib I<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 libI<name>.a and libI<name>.so
respectively. This is done because some simulators require a dynamic
library, but the static library is arguably easier to use if possible.
--protect-lib implies --protect-ids.
This allows for the secure delivery of sensitive IP without the need for
encrypted RTL (i.e. IEEE P1735). See examples/make_protect_lib in the
distribution for a demonstration of how to build and use the DPI library.
=item --private
Opposite of --public. Is the default; this option exists for backwards
@ -3230,6 +3245,12 @@ Verilator does not support SEREs yet. All assertion and coverage
statements must be simple expressions that complete in one cycle.
(Arguably SEREs are much of the point, but one must start somewhere.)
=head2 Encrypted Verilog
Open source simulators like Verilator are unable to use encrypted RTL
(i.e. IEEE P1735). Talk to your IP vendor about delivering IP blocks
via Verilator's --protect-lib feature.
=head2 Language Keyword Limitations
This section describes specific limitations for each language keyword.

2
examples/make_protect_lib/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
obj_*
logs

View File

@ -0,0 +1,97 @@
######################################################################
#
# DESCRIPTION: Verilator Example: --protect-lib Makefile
#
# This calls the object directory makefiles. That allows the objects to
# be placed in the "current directory" which simplifies the Makefile.
#
# 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.
#
######################################################################
# Check for sanity to avoid later confusion
ifneq ($(words $(CURDIR)),1)
$(error Unsupported: GNU Make cannot build in directories containing spaces, build elsewhere: '$(CURDIR)')
endif
######################################################################
# Set up variables
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
# package install, and verilator is in your path. Otherwise find the
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
ifeq ($(VERILATOR_ROOT),)
VERILATOR = verilator
else
export VERILATOR_ROOT
VERILATOR = $(VERILATOR_ROOT)/bin/verilator
endif
VERILATOR_FLAGS =
# Generate C++
VERILATOR_FLAGS += -cc
# Optimize
VERILATOR_FLAGS += -O2 -x-assign 0
# Warn abount lint issues; may not want this on less solid designs
VERILATOR_FLAGS += -Wall
# Make waveforms
TOP_VERILATOR_FLAGS = $(VERILATOR_FLAGS) --trace
######################################################################
default: run
run:
@echo
@echo "-- Verilator --protect-lib example -_--------------------------"
@echo
@echo "-- VERILATE secret module -------------------------------------"
@echo " --protect-lib will produce both a static and shared library"
@echo " In this example the static library is used, but some"
@echo " simulators may require the shared library."
@echo "---------------------------------------------------------------"
$(VERILATOR) $(VERILATOR_FLAGS) --protect-lib verilated_secret -Mdir obj_dir_secret/ secret_impl.v
@echo
@echo "-- COMPILE protected library ----------------------------------"
@echo " This builds verilated_secret.sv, libverilated_secret.a and"
@echo " libverilated_secret.so which can be distributed apart from"
@echo " the source"
@echo "---------------------------------------------------------------"
$(MAKE) -j 4 -C obj_dir_secret -f Vsecret_impl.mk
@echo
@echo "-- VERILATE top module ----------------------------------------"
@echo " Use the SystemVerilog wrapper (verilated_secret.sv) and the"
@echo " library (libverilated_secret.a) generated from the previous"
@echo " step"
@echo "---------------------------------------------------------------"
$(VERILATOR) $(TOP_VERILATOR_FLAGS) --exe -LDFLAGS '-L../obj_dir_secret -lverilated_secret -static' top.v obj_dir_secret/verilated_secret.sv sim_main.cpp
@echo
@echo "-- COMPILE entire design --------------------------------------"
$(MAKE) -j 4 -C obj_dir -f Vtop.mk
@echo
@echo "-- RUN --------------------------------------------------------"
@mkdir -p logs
obj_dir/Vtop +trace
@echo
@echo "-- DONE -------------------------------------------------------"
@echo "To see waveforms, open logs/vlt_dump.vcd in a waveform viewer"
@echo
######################################################################
# Other targets
show-config:
$(VERILATOR) -V
maintainer-copy::
clean mostlyclean distclean maintainer-clean::
-rm -rf obj_dir* logs *.log core

View File

@ -0,0 +1,27 @@
// DESCRIPTION: Verilator: --protect-lib example secret module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2019 by Todd Strader.
// This module will be used as libsecret.a or libsecret.so without
// exposing the source.
module secret_impl(
input [31:0] a,
input [31:0] b,
output logic [31:0] x,
input clk);
logic [31:0] accum_q = 0;
logic [31:0] secret_value = 9;
initial $display("%m: initialized");
always @(posedge clk) begin
accum_q <= accum_q + a;
if (accum_q > 10)
x <= b;
else
x <= a + b + secret_value;
end
endmodule

View File

@ -0,0 +1,72 @@
// DESCRIPTION: Verilator: --protect-lib example module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2019 by Todd Strader.
//======================================================================
// See examples/tracing_c for notes on tracing
// Include common routines
#include <verilated.h>
#include "Vtop.h"
#if VM_TRACE
# include <verilated_vcd_c.h>
#endif
vluint64_t main_time = 0;
double sc_time_stamp() {
return main_time;
}
int main(int argc, char** argv, char** env) {
if (0 && argc && argv && env) {}
Verilated::debug(0);
Verilated::randReset(2);
Verilated::commandArgs(argc, argv);
// Construct the Verilated model, including the secret module
Vtop* top = new Vtop;
#if VM_TRACE
// When tracing, the contents of the secret module will not be seen
VerilatedVcdC* tfp = NULL;
const char* flag = Verilated::commandArgsPlusMatch("trace");
if (flag && 0==strcmp(flag, "+trace")) {
Verilated::traceEverOn(true);
VL_PRINTF("Enabling waves into logs/vlt_dump.vcd...\n");
tfp = new VerilatedVcdC;
top->trace(tfp, 99);
Verilated::mkdir("logs");
tfp->open("logs/vlt_dump.vcd");
}
#endif
top->clk = 0;
// Simulate until $finish
while (!Verilated::gotFinish()) {
main_time++;
top->clk = ~top->clk & 0x1;
top->eval();
#if VM_TRACE
if (tfp) tfp->dump(main_time);
#endif
}
// Final model cleanup
top->final();
// Close trace if opened
#if VM_TRACE
if (tfp) { tfp->close(); tfp = NULL; }
#endif
// Destroy model
delete top; top = NULL;
// Fin
exit(0);
}

View File

@ -0,0 +1,34 @@
// DESCRIPTION: Verilator: --protect-lib example module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2019 by Todd Strader.
// See also the EXAMPLE section in the verilator manpage/document.
module top (input clk);
integer cyc = 0;
logic [31:0] a = 0;
logic [31:0] b = 0;
logic [31:0] x;
verilated_secret secret (.a, .b, .x, .clk);
always @(posedge clk) begin
$display("[%0t] cyc=%0d a=%0d b=%0d x=%0d", $time, cyc, a, b, x);
cyc <= cyc + 1;
if (cyc == 0) begin
a <= 5;
b <= 7;
end else if (cyc == 1) begin
a <= 6;
b <= 2;
end else if (cyc == 2) begin
a <= 1;
b <= 9;
end else if (cyc > 3) begin
$display("Done");
$finish;
end
end
endmodule

View File

@ -221,6 +221,7 @@ RAW_OBJS = \
V3Partition.o \
V3PreShell.o \
V3Premit.o \
V3ProtectLib.o \
V3Reloop.o \
V3Scope.o \
V3Scoreboard.o \

View File

@ -137,6 +137,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 {
of.puts("default: "+v3Global.opt.prefix()+"__ALL.a\n");
}
@ -168,6 +170,9 @@ 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");
}
const V3StringList& cFlags = v3Global.opt.cFlags();
for (V3StringList::const_iterator it = cFlags.begin(); it != cFlags.end(); ++it) {
of.puts("\t"+*it+" \\\n");
@ -225,6 +230,24 @@ public:
of.puts("\n");
}
if (!v3Global.opt.protectLib().empty()) {
of.puts("\n### Library rules... (from --protect-lib)\n");
of.puts("# Using -fPIC objects for both static and dynamic libraries "
"(which appears to work)\n");
of.puts(v3Global.opt.protectLibName(false)+": $(VK_OBJS) $(VK_GLOBAL_OBJS)\n");
of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o "+
v3Global.opt.protectLib()+".o "+v3Global.opt.protectLib()+".cpp\n");
of.puts("\tar rc $@ $^ "+v3Global.opt.protectLib()+".o\n");
of.puts("\n");
of.puts(v3Global.opt.protectLibName(true)+": $(VM_PREFIX)__ALL.a $(VK_GLOBAL_OBJS)\n");
of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -shared -o $@ "+v3Global.opt.protectLib()+".cpp $^\n");
of.puts("\n");
of.puts("lib"+v3Global.opt.protectLib()+": "+v3Global.opt.protectLibName(false)+
" "+v3Global.opt.protectLibName(true)+"\n");
}
of.puts("\n");
of.putsHeader();
}

View File

@ -594,7 +594,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
puts(" ");
iterate(nodep->dtypep()); puts(" ");
puts(nodep->prettyName());
puts(";\n");
if (!m_suppressVarSemi) puts(";\n"); else puts("\n");
}
virtual void visit(AstActive* nodep) {
m_sensesp = nodep->sensesp();
@ -617,9 +617,11 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
}
public:
bool m_suppressVarSemi; // Suppress emitting semicolon for AstVars
explicit EmitVBaseVisitor(AstSenTree* domainp=NULL) {
// Domain for printing one a ALWAYS under a ACTIVE
m_suppressSemi = false;
m_suppressVarSemi = false;
m_sensesp = domainp;
}
virtual ~EmitVBaseVisitor() {}
@ -639,9 +641,11 @@ class EmitVFileVisitor : public EmitVBaseVisitor {
virtual void putqs(AstNode*, const string& str) { putbs(str); }
virtual void putsNoTracking(const string& str) { ofp()->putsNoTracking(str); }
public:
EmitVFileVisitor(AstNode* nodep, V3OutFile* ofp, bool trackText=false) {
EmitVFileVisitor(AstNode* nodep, V3OutFile* ofp, bool trackText=false,
bool suppressVarSemi=false) {
m_ofp = ofp;
m_trackText = trackText;
m_suppressVarSemi = suppressVarSemi;
iterate(nodep);
}
virtual ~EmitVFileVisitor() {}
@ -779,7 +783,7 @@ void V3EmitV::emitvFiles() {
if (vfilep && vfilep->tblockp()) {
V3OutVFile of(vfilep->name());
of.puts("// DESCR" "IPTION: Verilator generated Verilog\n");
EmitVFileVisitor visitor(vfilep->tblockp(), &of, true);
EmitVFileVisitor visitor(vfilep->tblockp(), &of, true, true);
}
}
}

View File

@ -921,6 +921,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
shift;
m_outputSplitCTrace = atoi(argv[i]);
}
else if (!strcmp(sw, "-protect-lib") && (i+1)<argc) {
shift; m_protectLib = argv[i];
m_protectIds = true;
}
else if (!strcmp(sw, "-trace-fst")) {
m_trace = true;
m_traceFormat = TraceFormat::FST;

View File

@ -191,6 +191,7 @@ class V3Options {
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_xAssign; // main switch: --x-assign
@ -358,6 +359,16 @@ class V3Options {
string prefix() const { return m_prefix; }
string protectKey() const { return m_protectKey; }
string protectKeyDefaulted(); // Set default key if not set by user
string protectLib() const { return m_protectLib; }
string protectLibName(bool shared) {
string libName = "lib"+protectLib();
if (shared) {
libName += ".so";
} else {
libName += ".a";
}
return libName;
}
string topModule() const { return m_topModule; }
string unusedRegexp() const { return m_unusedRegexp; }
string xAssign() const { return m_xAssign; }

453
src/V3ProtectLib.cpp Normal file
View File

@ -0,0 +1,453 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Build DPI protected C++ and SV
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2003-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.
//
// 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.
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3ProtectLib.h"
#include "V3File.h"
#include "V3Hashed.h"
#include "V3Task.h"
#include <list>
//######################################################################
// ProtectLib top-level visitor
class ProtectVisitor : public AstNVisitor {
private:
AstVFile* m_vfilep; // DPI-enabled Verilog wrapper
AstCFile* m_cfilep; // C implementation of DPI functions
// Verilog text blocks
AstTextBlock* m_modPortsp; // Module port list
AstTextBlock* m_comboPortsp; // Combo function port list
AstTextBlock* m_seqPortsp; // Sequential function port list
AstTextBlock* m_comboIgnorePortsp; // Combo ignore function port list
AstTextBlock* m_comboDeclsp; // Combo signal declaration list
AstTextBlock* m_seqDeclsp; // Sequential signal declaration list
AstTextBlock* m_tmpDeclsp; // Temporary signal declaration list
AstTextBlock* m_hashValuep; // CPP hash value
AstTextBlock* m_comboParamsp; // Combo function parameter list
AstTextBlock* m_clkSensp; // Clock sensitivity list
AstTextBlock* m_comboIgnoreParamsp; // Combo ignore parameter list
AstTextBlock* m_seqParamsp; // Sequential parameter list
AstTextBlock* m_nbAssignsp; // Non-blocking assignment list
AstTextBlock* m_seqAssignsp; // Sequential assignment list
AstTextBlock* m_comboAssignsp; // Combo assignment list
// C text blocks
AstTextBlock* m_cHashValuep; // CPP hash value
AstTextBlock* m_cComboParamsp; // Combo function parameter list
AstTextBlock* m_cComboInsp; // Combo input copy list
AstTextBlock* m_cComboOutsp; // Combo output copy list
AstTextBlock* m_cSeqParamsp; // Sequential parameter list
AstTextBlock* m_cSeqClksp; // Sequential clock copy list
AstTextBlock* m_cSeqOutsp; // Sequential output copy list
AstTextBlock* m_cIgnoreParamsp; // Combo ignore parameter list
string m_libName;
string m_topName;
// VISITORS
virtual void visit(AstNetlist* nodep) {
m_vfilep = new AstVFile(nodep->fileline(),
v3Global.opt.makeDir()+"/"+m_libName+".sv");
nodep->addFilesp(m_vfilep);
m_cfilep = new AstCFile(nodep->fileline(),
v3Global.opt.makeDir()+"/"+m_libName+".cpp");
nodep->addFilesp(m_cfilep);
iterateChildren(nodep);
}
virtual void visit(AstNodeModule* nodep) {
UASSERT_OBJ(!nodep->nextp(), nodep, "Multiple root modules");
FileLine* fl = nodep->fileline();
createSvFile(fl);
createCppFile(fl);
iterateChildren(nodep);
V3Hash hash = V3Hashed::uncachedHash(m_cfilep);
m_hashValuep->addText(fl, cvtToStr(hash.fullValue())+";\n");
m_cHashValuep->addText(fl, cvtToStr(hash.fullValue())+";\n");
}
void addComment(AstTextBlock* txtp, FileLine* fl, const string& comment) {
txtp->addNodep(new AstComment(fl, comment));
}
void hashComment(AstTextBlock* txtp, FileLine* fl) {
addComment(txtp, fl, "Checks to make sure the .sv wrapper and library agree");
}
void initialComment(AstTextBlock* txtp, FileLine* fl) {
addComment(txtp, fl, "Creates an instance of the secret module at initial-time");
addComment(txtp, fl, "(one for each instance in the user's design) also evaluates");
addComment(txtp, fl, "the secret module's initial process");
}
void comboComment(AstTextBlock* txtp, FileLine* fl) {
addComment(txtp, fl, "Updates all non-clock inputs and retrieves the results");
}
void seqComment(AstTextBlock* txtp, FileLine* fl) {
addComment(txtp, fl, "Updates all clocks and retrieves the results");
}
void comboIgnoreComment(AstTextBlock* txtp, FileLine* fl) {
addComment(txtp, fl, "Need to convince some simulators that the input to the module");
addComment(txtp, fl, "must be evaluated before evaluating the clock edge");
}
void finalComment(AstTextBlock* txtp, FileLine* fl) {
addComment(txtp, fl, "Evaluates the secret module's final process");
}
void createSvFile(FileLine* fl) {
// Comments
AstTextBlock* txtp = new AstTextBlock(fl);
addComment(txtp, fl, "Wrapper module for DPI protected library");
addComment(txtp, fl, "This module requires lib"+m_libName+
".a or lib"+m_libName+".so to work");
addComment(txtp, fl, "See instructions in your simulator for how"
" to use DPI libraries\n");
// Module declaration
m_modPortsp = new AstTextBlock(fl, "module "+m_libName+" (\n", false, true);
txtp->addNodep(m_modPortsp);
txtp->addText(fl, ");\n\n");
// DPI declarations
hashComment(txtp, fl);
txtp->addText(fl, "import \"DPI-C\" function void "+
m_libName+"_protectlib_check_hash (int protectlib_hash__V);\n\n");
initialComment(txtp, fl);
txtp->addText(fl, "import \"DPI-C\" function chandle "+
m_libName+"_protectlib_create (string scope__V);\n\n");
comboComment(txtp, fl);
m_comboPortsp = new AstTextBlock(fl, "import \"DPI-C\" function longint "+
m_libName+"_protectlib_combo_update "
"(\n", false, true);
m_comboPortsp->addText(fl, "chandle handle__V\n");
txtp->addNodep(m_comboPortsp);
txtp->addText(fl, ");\n\n");
seqComment(txtp, fl);
m_seqPortsp = new AstTextBlock(fl, "import \"DPI-C\" function longint "+
m_libName+"_protectlib_seq_update "
"(\n", false, true);
m_seqPortsp->addText(fl, "chandle handle__V\n");
txtp->addNodep(m_seqPortsp);
txtp->addText(fl, ");\n\n");
comboIgnoreComment(txtp, fl);
m_comboIgnorePortsp = new AstTextBlock(fl, "import \"DPI-C\" function void "+
m_libName+"_protectlib_combo_ignore "
"(\n", false, true);
m_comboIgnorePortsp->addText(fl, "chandle handle__V\n");
txtp->addNodep(m_comboIgnorePortsp);
txtp->addText(fl, ");\n\n");
finalComment(txtp, fl);
txtp->addText(fl, "import \"DPI-C\" function void "+
m_libName+"_protectlib_final (chandle handle__V);\n\n");
// Local variables
txtp->addText(fl, "chandle handle__V;\n\n");
m_comboDeclsp = new AstTextBlock(fl);
txtp->addNodep(m_comboDeclsp);
m_seqDeclsp = new AstTextBlock(fl);
txtp->addNodep(m_seqDeclsp);
m_tmpDeclsp = new AstTextBlock(fl);
txtp->addNodep(m_tmpDeclsp);
txtp->addText(fl, "\ntime last_combo_seqnum__V;\n");
txtp->addText(fl, "time last_seq_seqnum__V;\n\n");
// CPP hash value
addComment(txtp, fl, "Hash value to make sure this file and the corresponding");
addComment(txtp, fl, "library agree");
m_hashValuep = new AstTextBlock(fl, "localparam int protectlib_hash__V =\n");
txtp->addNodep(m_hashValuep);
txtp->addText(fl, "\n");
// Initial
txtp->addText(fl, "initial begin\n");
txtp->addText(fl, m_libName+"_protectlib_check_hash(protectlib_hash__V);\n");
txtp->addText(fl, "handle__V = "+m_libName+"_protectlib_create"
"($sformatf(\"%m\"));\n");
txtp->addText(fl, "end\n\n");
// Combinatorial process
addComment(txtp, fl, "Combinatorialy evaluate changes to inputs");
m_comboParamsp = new AstTextBlock(fl, "always @(*) begin\n"
"last_combo_seqnum__V = "+
m_libName+"_protectlib_combo_update(\n",
false, true);
m_comboParamsp->addText(fl, "handle__V\n");
txtp->addNodep(m_comboParamsp);
txtp->addText(fl, ");\n");
txtp->addText(fl, "end\n\n");
// Sequential process
addComment(txtp, fl, "Evaluate clock edges");
m_clkSensp = new AstTextBlock(fl, "always @(", false, true);
txtp->addNodep(m_clkSensp);
txtp->addText(fl, ") begin\n");
m_comboIgnoreParamsp = new AstTextBlock(fl, m_libName+"_protectlib_combo_ignore(\n",
false, true);
m_comboIgnoreParamsp->addText(fl, "handle__V\n");
txtp->addNodep(m_comboIgnoreParamsp);
txtp->addText(fl, ");\n");
m_seqParamsp = new AstTextBlock(fl, "last_seq_seqnum__V <= "+m_libName+
"_protectlib_seq_update(\n",
false, true);
m_seqParamsp->addText(fl, "handle__V\n");
txtp->addNodep(m_seqParamsp);
txtp->addText(fl, ");\n");
m_nbAssignsp = new AstTextBlock(fl);
txtp->addNodep(m_nbAssignsp);
txtp->addText(fl, "end\n\n");
// Select between combinatorial and sequential results
addComment(txtp, fl, "Select between combinatorial and sequential results");
txtp->addText(fl, "always @(*) begin\n");
m_seqAssignsp = new AstTextBlock(fl, "if (last_seq_seqnum__V > "
"last_combo_seqnum__V) begin\n");
txtp->addNodep(m_seqAssignsp);
m_comboAssignsp = new AstTextBlock(fl, "end else begin\n");
txtp->addNodep(m_comboAssignsp);
txtp->addText(fl, "end\n");
txtp->addText(fl, "end\n\n");
// Final
txtp->addText(fl, "final "+m_libName+"_protectlib_final(handle__V);\n\n");
txtp->addText(fl, "endmodule\n");
m_vfilep->tblockp(txtp);
}
void castPtr(FileLine* fl, AstTextBlock* txtp) {
txtp->addText(fl, m_topName+"_container* handlep__V = "
"static_cast<"+m_topName+"_container*>(vhandlep__V);\n");
}
void createCppFile(FileLine* fl) {
// Comments
AstTextBlock* txtp = new AstTextBlock(fl);
addComment(txtp, fl, "Wrapper functions for DPI protected library\n");
// Includes
txtp->addText(fl, "#include \""+m_topName+".h\"\n");
txtp->addText(fl, "#include \"verilated_dpi.h\"\n\n");
txtp->addText(fl, "#include <cstdio>\n");
txtp->addText(fl, "#include <cstdlib>\n\n");
// Verilated module plus sequence number
addComment(txtp, fl, "Container class to house verilated object and sequence number");
txtp->addText(fl, "class "+m_topName+"_container: public "+m_topName+" {\n");
txtp->addText(fl, "public:\n");
txtp->addText(fl, "long long m_seqnum;\n");
txtp->addText(fl, m_topName+"_container(const char* scopep__V):\n");
txtp->addText(fl, m_topName+"(scopep__V) {}\n");
txtp->addText(fl, "};\n\n");
// Extern C
txtp->addText(fl, "extern \"C\" {\n\n");
// Hash check
hashComment(txtp, fl);
txtp->addText(fl, "void "+m_libName+"_protectlib_check_hash"
" (int protectlib_hash__V) {\n");
m_cHashValuep = new AstTextBlock(fl, "int expected_hash__V =\n");
txtp->addNodep(m_cHashValuep);
txtp->addText(fl, "if (protectlib_hash__V != expected_hash__V) {\n");
txtp->addText(fl, "fprintf(stderr, \"%%Error: cannot use "+m_libName+" library, "
"Verliog (%u) and library (%u) hash values do not "
"agree\\n\", protectlib_hash__V, expected_hash__V);\n");
txtp->addText(fl, "exit(EXIT_FAILURE);\n");
txtp->addText(fl, "}\n");
txtp->addText(fl, "}\n\n");
// Initial
initialComment(txtp, fl);
txtp->addText(fl, "void* "+m_libName+"_protectlib_create"
" (const char* scopep__V) {\n");
txtp->addText(fl, m_topName+"_container* handlep__V = "
"new "+m_topName+"_container(scopep__V);\n");
txtp->addText(fl, "return handlep__V;\n");
txtp->addText(fl, "}\n\n");
// Updates
comboComment(txtp, fl);
m_cComboParamsp = new AstTextBlock(fl, "long long "+m_libName+"_protectlib_combo_update (\n",
false, true);
m_cComboParamsp->addText(fl, "void* vhandlep__V\n");
txtp->addNodep(m_cComboParamsp);
txtp->addText(fl, ")\n");
m_cComboInsp = new AstTextBlock(fl, "{\n");
castPtr(fl, m_cComboInsp);
txtp->addNodep(m_cComboInsp);
m_cComboOutsp = new AstTextBlock(fl, "handlep__V->eval();\n");
txtp->addNodep(m_cComboOutsp);
txtp->addText(fl, "return handlep__V->m_seqnum++;\n");
txtp->addText(fl, "}\n\n");
seqComment(txtp, fl);
m_cSeqParamsp = new AstTextBlock(fl, "long long "+m_libName+"_protectlib_seq_update (\n",
false, true);
m_cSeqParamsp->addText(fl, "void* vhandlep__V\n");
txtp->addNodep(m_cSeqParamsp);
txtp->addText(fl, ")\n");
m_cSeqClksp = new AstTextBlock(fl, "{\n");
castPtr(fl, m_cSeqClksp);
txtp->addNodep(m_cSeqClksp);
m_cSeqOutsp = new AstTextBlock(fl, "handlep__V->eval();\n");
txtp->addNodep(m_cSeqOutsp);
txtp->addText(fl, "return handlep__V->m_seqnum++;\n");
txtp->addText(fl, "}\n\n");
comboIgnoreComment(txtp, fl);
m_cIgnoreParamsp = new AstTextBlock(fl, "void "+m_libName+"_protectlib_combo_ignore (\n",
false, true);
m_cIgnoreParamsp->addText(fl, "void* vhandlep__V\n");
txtp->addNodep(m_cIgnoreParamsp);
txtp->addText(fl, ")\n");
txtp->addText(fl, "{ }\n\n");
// Final
finalComment(txtp, fl);
txtp->addText(fl, "void "+m_libName+"_protectlib_final (void* vhandlep__V) {\n");
castPtr(fl, txtp);
txtp->addText(fl, "handlep__V->final();\n");
txtp->addText(fl, "delete handlep__V;\n");
txtp->addText(fl, "}\n\n");
txtp->addText(fl, "}\n");
m_cfilep->tblockp(txtp);
}
virtual void visit(AstVar* nodep) {
if (!nodep->isIO()) return;
if (VN_IS(nodep->dtypep(), UnpackArrayDType)) {
nodep->v3fatalSrc("Unsupported: unpacked arrays with protect-lib");
}
if (nodep->direction() == VDirection::INPUT) {
if (nodep->isUsedClock()) {
handleClock(nodep);
} else {
handleDataInput(nodep);
}
} else if (nodep->direction() == VDirection::OUTPUT) {
handleOutput(nodep);
} else {
nodep->v3fatalSrc("Unsupported port direction for protect-lib: "<<
nodep->direction().ascii());
}
}
virtual void visit(AstNode* nodep) { }
string cInputConnection(AstVar* varp) {
string frstmt;
bool useSetWSvlv = V3Task::dpiToInternalFrStmt(varp, varp->name(), true, frstmt);
if (useSetWSvlv) {
return frstmt+" handlep__V->"+varp->name()+", "+varp->name()+");\n";
}
return "handlep__V->"+varp->name()+" = "+frstmt+";\n";
}
void handleClock(AstVar* varp) {
FileLine* fl = varp->fileline();
handleInput(varp);
m_seqPortsp->addNodep(varp->cloneTree(false));
m_seqParamsp->addText(fl, varp->name()+"\n");
m_clkSensp->addText(fl, "edge("+varp->name()+")");
m_cSeqParamsp->addText(fl, varp->dpiArgType(true, false)+"\n");
m_cSeqClksp->addText(fl, cInputConnection(varp));
}
void handleDataInput(AstVar* varp) {
FileLine* fl = varp->fileline();
handleInput(varp);
m_comboPortsp->addNodep(varp->cloneTree(false));
m_comboParamsp->addText(fl, varp->name()+"\n");
m_comboIgnorePortsp->addNodep(varp->cloneTree(false));
m_comboIgnoreParamsp->addText(fl, varp->name()+"\n");
m_cComboParamsp->addText(fl, varp->dpiArgType(true, false)+"\n");
m_cComboInsp->addText(fl, cInputConnection(varp));
m_cIgnoreParamsp->addText(fl, varp->dpiArgType(true, false)+"\n");
}
void handleInput(AstVar* varp) {
FileLine* fl = varp->fileline();
m_modPortsp->addNodep(varp->cloneTree(false));
}
void handleOutput(AstVar* varp) {
FileLine* fl = varp->fileline();
m_modPortsp->addNodep(varp->cloneTree(false));
m_comboPortsp->addNodep(varp->cloneTree(false));
m_comboParamsp->addText(fl, varp->name()+"_combo__V\n");
m_seqPortsp->addNodep(varp->cloneTree(false));
m_seqParamsp->addText(fl, varp->name()+"_tmp__V\n");
AstNodeDType* comboDtypep = varp->dtypep()->cloneTree(false);
m_comboDeclsp->addNodep(comboDtypep);
m_comboDeclsp->addText(fl, " "+varp->name()+"_combo__V;\n");
AstNodeDType* seqDtypep = varp->dtypep()->cloneTree(false);
m_seqDeclsp->addNodep(seqDtypep);
m_seqDeclsp->addText(fl, " "+varp->name()+"_seq__V;\n");
AstNodeDType* tmpDtypep = varp->dtypep()->cloneTree(false);
m_tmpDeclsp->addNodep(tmpDtypep);
m_tmpDeclsp->addText(fl, " "+varp->name()+"_tmp__V;\n");
m_nbAssignsp->addText(fl, varp->name()+"_seq__V <= "+varp->name()+"_tmp__V;\n");
m_seqAssignsp->addText(fl, varp->name()+" = "+varp->name()+"_seq__V;\n");
m_comboAssignsp->addText(fl, varp->name()+" = "+varp->name()+"_combo__V;\n");
m_cComboParamsp->addText(fl, varp->dpiArgType(true, false)+"\n");
m_cComboOutsp->addText(fl, V3Task::assignInternalToDpi(varp, false, true, "", "",
"handlep__V->"));
m_cSeqParamsp->addText(fl, varp->dpiArgType(true, false)+"\n");
m_cSeqOutsp->addText(fl, V3Task::assignInternalToDpi(varp, false, true, "", "",
"handlep__V->"));
}
public:
explicit ProtectVisitor(AstNode* nodep):
m_vfilep(NULL), m_cfilep(NULL), m_modPortsp(NULL),
m_comboPortsp(NULL), m_seqPortsp(NULL), m_comboIgnorePortsp(NULL), m_comboDeclsp(NULL),
m_seqDeclsp(NULL), m_tmpDeclsp(NULL), m_hashValuep(NULL), m_clkSensp(NULL),
m_comboIgnoreParamsp(NULL), m_seqParamsp(NULL), m_nbAssignsp(NULL), m_seqAssignsp(NULL),
m_comboAssignsp(NULL), m_cHashValuep(NULL), m_cComboParamsp(NULL), m_cComboInsp(NULL),
m_cComboOutsp(NULL), m_cSeqParamsp(NULL), m_cSeqClksp(NULL), m_cSeqOutsp(NULL),
m_cIgnoreParamsp(NULL), m_libName(v3Global.opt.protectLib()),
m_topName(v3Global.opt.prefix())
{
iterate(nodep);
}
};
//######################################################################
// ProtectLib class functions
void V3ProtectLib::protect() {
UINFO(2,__FUNCTION__<<": "<<endl);
ProtectVisitor visitor(v3Global.rootp());
}

37
src/V3ProtectLib.h Normal file
View File

@ -0,0 +1,37 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Buitd DPI protected C++ and SV
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2003-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.
//
// 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 _V3PROTECTLIB_H_
#define _V3PROTECTLIB_H_ 1
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3ProtectLib {
public:
static void protect();
};
#endif // Guard

View File

@ -614,42 +614,7 @@ private:
AstNode* createAssignInternalToDpi(AstVar* portp, bool isRtn, bool isPtr,
const string& frSuffix, const string& toSuffix) {
// Create assignment from internal format into DPI temporary
bool bitvec = (portp->basicp()->keyword().isDpiBitVal() && portp->width() > 32);
bool logicvec = (portp->basicp()->keyword().isDpiLogicVal() && portp->width() > 1);
if (isRtn && (bitvec || logicvec)) {
portp->v3error("DPI functions cannot return > 32 bits or four-state;"
" use a two-state type or task instead: "<<portp->prettyNameQ());
// Code below works, but won't compile right, and IEEE illegal
}
string stmt;
string ket;
// Someday we'll have better type support, and this can make variables and casts.
// But for now, we'll just text-bash it.
if (bitvec) {
if (portp->isWide()) {
stmt += ("VL_SET_SVBV_W("+cvtToStr(portp->width())
+", "+portp->name()+toSuffix+", "+portp->name()+frSuffix+")");
} else {
stmt += "VL_SET_WQ("+portp->name()+toSuffix+", "+portp->name()+frSuffix+")";
}
} else if (logicvec) {
stmt += ("VL_SET_SVLV_" + string(portp->dtypep()->charIQWN()) + "("
+ cvtToStr(portp->width())
+ ", "+portp->name()+toSuffix+", "+portp->name()+frSuffix+")");
} else {
if (isPtr) stmt += "*"; // DPI outputs are pointers
stmt += portp->name()+toSuffix+" = ";
if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::CHANDLE) {
stmt += "VL_CVT_Q_VP(";
ket += ")";
}
stmt += portp->name()+frSuffix;
if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::STRING) {
stmt += ".c_str()";
}
}
stmt += ket + ";\n";
string stmt = V3Task::assignInternalToDpi(portp, isRtn, isPtr, frSuffix, toSuffix);
return new AstCStmt(portp->fileline(), stmt);
}
@ -657,38 +622,13 @@ private:
// Create assignment from DPI temporary into internal format
AstVar* portp = portvscp->varp();
string frstmt;
if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::CHANDLE) {
frstmt = "VL_CVT_VP_Q("+frName+")";
}
else if (portp->basicp() && portp->basicp()->keyword().isDpiBitVal()
&& portp->width() != 1 && portp->isQuad()) {
// SV is vector, Verilator isn't
frstmt = "VL_SET_QW("+frName+")";
}
else if (portp->basicp() && portp->basicp()->keyword().isDpiLogicVal()
&& portp->width() != 1 && portp->isQuad()) {
frstmt = "VL_SET_Q_SVLV("+frName+")";
}
else if (portp->basicp() && portp->basicp()->keyword().isDpiLogicVal()
&& portp->width() != 1 && !portp->isWide()) {
frstmt = "VL_SET_I_SVLV("+frName+")";
}
else if (!cvt
&& portp->basicp() && portp->basicp()->keyword().isDpiBitVal()
&& portp->width() != 1 && !portp->isWide()) {
frstmt = "*"+frName; // it's a svBitVecVal, which other code won't think is arrayed (as WData aren't), but really is
}
else if (portp->basicp() && portp->basicp()->keyword().isDpiLogicVal()
&& portp->width() != 1 && portp->isWide()) {
// Need to convert to wide, using special function
AstNode* linesp = new AstText(portp->fileline(), "VL_SET_W_SVLV("+cvtToStr(portp->width()) + ",");
bool useSetWSvlv = V3Task::dpiToInternalFrStmt(portp, frName, cvt, frstmt);
if (useSetWSvlv) {
AstNode* linesp = new AstText(portp->fileline(), frstmt);
linesp->addNext(new AstVarRef(portp->fileline(), portvscp, true));
linesp->addNext(new AstText(portp->fileline(), ","+frName+");"));
return new AstCStmt(portp->fileline(), linesp);
}
else {
frstmt = frName;
}
// Use a AstCMath, as we want V3Clean to mask off bits that don't make sense.
int cwidth = VL_WORDSIZE;
if (portp->basicp()) cwidth = portp->basicp()->keyword().width();
@ -1495,6 +1435,84 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp)
return tconnects;
}
string V3Task::assignInternalToDpi(AstVar* portp, bool isRtn, bool isPtr,
const string& frSuffix, const string& toSuffix,
const string& frPrefix) {
// Create assignment from internal format into DPI temporary
bool bitvec = (portp->basicp()->keyword().isDpiBitVal() && portp->width() > 32);
bool logicvec = (portp->basicp()->keyword().isDpiLogicVal() && portp->width() > 1);
if (isRtn && (bitvec || logicvec)) {
portp->v3error("DPI functions cannot return > 32 bits or four-state;"
" use a two-state type or task instead: "<<portp->prettyNameQ());
// Code below works, but won't compile right, and IEEE illegal
}
string stmt;
string ket;
// Someday we'll have better type support, and this can make variables and casts.
// But for now, we'll just text-bash it.
string frName = frPrefix+portp->name()+frSuffix;
string toName = portp->name()+toSuffix;
if (bitvec) {
if (portp->isWide()) {
stmt += ("VL_SET_SVBV_W("+cvtToStr(portp->width())
+", "+toName+", "+frName+")");
} else {
stmt += "VL_SET_WQ("+toName+", "+frName+")";
}
} else if (logicvec) {
stmt += ("VL_SET_SVLV_" + string(portp->dtypep()->charIQWN()) + "("
+ cvtToStr(portp->width())
+ ", "+toName+", "+frName+")");
} else {
if (isPtr) stmt += "*"; // DPI outputs are pointers
stmt += toName+" = ";
if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::CHANDLE) {
stmt += "VL_CVT_Q_VP(";
ket += ")";
}
stmt += frName;
if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::STRING) {
stmt += ".c_str()";
}
}
stmt += ket + ";\n";
return stmt;
}
bool V3Task::dpiToInternalFrStmt(AstVar* portp, const string& frName, bool cvt, string& frstmt) {
if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::CHANDLE) {
frstmt = "VL_CVT_VP_Q("+frName+")";
}
else if (portp->basicp() && portp->basicp()->keyword().isDpiBitVal()
&& portp->width() != 1 && portp->isQuad()) {
// SV is vector, Verilator isn't
frstmt = "VL_SET_QW("+frName+")";
}
else if (portp->basicp() && portp->basicp()->keyword().isDpiLogicVal()
&& portp->width() != 1 && portp->isQuad()) {
frstmt = "VL_SET_Q_SVLV("+frName+")";
}
else if (portp->basicp() && portp->basicp()->keyword().isDpiLogicVal()
&& portp->width() != 1 && !portp->isWide()) {
frstmt = "VL_SET_I_SVLV("+frName+")";
}
else if (!cvt
&& portp->basicp() && portp->basicp()->keyword().isDpiBitVal()
&& portp->width() != 1 && !portp->isWide()) {
frstmt = "*"+frName; // it's a svBitVecVal, which other code won't think is arrayed (as WData aren't), but really is
}
else if (portp->basicp() && portp->basicp()->keyword().isDpiLogicVal()
&& portp->width() != 1 && portp->isWide()) {
// Need to convert to wide, using special function
frstmt = "VL_SET_W_SVLV("+cvtToStr(portp->width()) + ",";
return true;
}
else {
frstmt = frName;
}
return false;
}
void V3Task::taskAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
{

View File

@ -41,6 +41,11 @@ public:
static void taskAll(AstNetlist* nodep);
/// Return vector of [port, pin-connects-to] (SLOW)
static V3TaskConnects taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp);
static string assignInternalToDpi(AstVar* portp, bool isRtn, bool isPtr,
const string& frSuffix, const string& toSuffix,
const string& frPrefix="");
static bool dpiToInternalFrStmt(AstVar* portp, const string& frName, bool cvt,
string& frstmt);
};
#endif // Guard

View File

@ -73,6 +73,7 @@
#include "V3Partition.h"
#include "V3PreShell.h"
#include "V3Premit.h"
#include "V3ProtectLib.h"
#include "V3Reloop.h"
#include "V3Scope.h"
#include "V3Scoreboard.h"
@ -547,6 +548,13 @@ void process() {
V3EmitXml::emitxml();
}
// Output DPI protected library files
if (!v3Global.opt.protectLib().empty()) {
V3ProtectLib::protect();
V3EmitV::emitvFiles();
V3EmitC::emitcFiles();
}
// Statistics
if (v3Global.opt.stats()) {
V3Stats::statsFinalAll(v3Global.rootp());

53
test_regress/t/t_prot_lib.pl Executable file
View File

@ -0,0 +1,53 @@
#!/usr/bin/perl
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.
scenarios(
vlt => 1,
xsim => 1,
);
# Always compile the secret file with Verilator no matter what simulator
# we are testing with
my $cmd = ["t/t_prot_lib_secret.pl", "--vlt"];
my $secret_prefix = "t_prot_lib_secret";
my $secret_dir = "$Self->{obj_dir}/../../obj_vlt/$secret_prefix";
while (1) {
run(logfile => "$Self->{obj_dir}/secret_compile.log",
cmd => $cmd);
last if $Self->{errors};
compile(
verilator_flags2 => ["$secret_dir/secret.sv",
"--trace", "-LDFLAGS",
"'-L../$secret_prefix -lsecret -static'"],
xsim_flags2 => ["$secret_dir/secret.sv"],
);
execute(
check_finished => 1,
xsim_run_flags2 => ["--debug",
"all",
"--sv_lib",
"$secret_dir/libsecret",
"--dpi_absolute"],
);
if ($Self->{vlt}) {
# We can see the ports of the secret module
file_grep("$Self->{obj_dir}/simx.vcd", qr/accum_in/);
# but we can't see what's inside
file_grep_not("$Self->{obj_dir}/simx.vcd", qr/secret_/);
}
ok(1);
last;
}
1;

139
test_regress/t/t_prot_lib.v Normal file
View File

@ -0,0 +1,139 @@
// DESCRIPTION: Verilator: Verilog Test module
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2019 by Todd Strader.
`define DRIVE(sig) \
/* Just throw a bunch of bits at the input */ \
/* verilator lint_off WIDTH */ \
sig``_in <= {8{crc}}; \
/* verilator lint_on WIDTH */
`define CHECK(sig) \
if (cyc > 0 && sig``_in != sig``_out) begin \
$display(`"%%Error (%m) sig``_in (0x%0x) != sig``_out (0x%0x)`", \
sig``_in, sig``_out); \
$stop; \
end
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
genvar x;
generate
for (x = 0; x < 2; x = x + 1) begin: gen_loop
integer cyc = 0;
reg [63:0] crc = 64'h5aef0c8d_d70a4497;
logic [31:0] accum_in;
logic [31:0] accum_out;
logic accum_bypass;
logic [31:0] accum_bypass_out;
logic [31:0] accum_out_expect;
logic [31:0] accum_bypass_out_expect;
logic s1_in;
logic s1_out;
logic [1:0] s2_in;
logic [1:0] s2_out;
logic [7:0] s8_in;
logic [7:0] s8_out;
logic [32:0] s33_in;
logic [32:0] s33_out;
logic [63:0] s64_in;
logic [63:0] s64_out;
logic [64:0] s65_in;
logic [64:0] s65_out;
logic [128:0] s129_in;
logic [128:0] s129_out;
logic [3:0] [31:0] s4x32_in;
logic [3:0] [31:0] s4x32_out;
secret
secret (
.accum_in,
.accum_out,
.accum_bypass,
.accum_bypass_out,
.s1_in,
.s1_out,
.s2_in,
.s2_out,
.s8_in,
.s8_out,
.s33_in,
.s33_out,
.s64_in,
.s64_out,
.s65_in,
.s65_out,
.s129_in,
.s129_out,
.s4x32_in,
.s4x32_out,
.clk);
always @(posedge clk) begin
`ifdef TEST_VERBOSE
$display("[%0t] x=%0d, cyc=%0d accum_in=%0d accum_out=%0d accum_bypass_out=%0d",
$time, x, cyc, accum_in, accum_out, accum_bypass_out);
`endif
cyc <= cyc + 1;
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
accum_in <= accum_in + 5;
// 7 is the secret_value inside the secret module
accum_out_expect <= accum_in + accum_out_expect + 7;
`DRIVE(s1)
`DRIVE(s2)
`DRIVE(s8)
`DRIVE(s33)
`DRIVE(s64)
`DRIVE(s65)
`DRIVE(s129)
`DRIVE(s4x32)
if (cyc == 0) begin
accum_in <= x*100;
accum_bypass <= '0;
end else if (cyc > 0) begin
if (accum_out_expect != accum_out) begin
$display("%%Error: (%m) accum_out expected %0d got %0d",
accum_out_expect, accum_out);
$stop;
end
if (accum_bypass_out_expect != accum_bypass_out) begin
$display("%%Error: (%m) accum_bypass_out expected %0d got %0d",
accum_bypass_out_expect, accum_bypass_out);
$stop;
end
end
if (cyc == 5) accum_bypass <= '1;
if (x == 0 && cyc == 10) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
always @(*) begin
// XSim (and maybe all event simulators?) sees the moment where
// s1_in has not yet propagated to s1_out, however, they do always
// both change at the same time
/* verilator lint_off STMTDLY */
#1;
/* verilator lint_on STMTDLY */
`CHECK(s1)
`CHECK(s2)
`CHECK(s8)
`CHECK(s33)
`CHECK(s64)
`CHECK(s65)
`CHECK(s129)
`CHECK(s4x32)
end
assign accum_bypass_out_expect = accum_bypass ? accum_in :
accum_out_expect;
end
endgenerate
endmodule

View File

@ -0,0 +1,5 @@
%Error: Internal Error: t/t_prot_lib_inout_bad.v:8: ../V3ProtectLib.cpp:359: Unsupported port direction for protect-lib: INOUT
inout z,
^
... See the manual and http://www.veripool.org/verilator for more assistance.
%Error: Command Failed

View File

@ -0,0 +1,27 @@
#!/usr/bin/perl
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.
scenarios(vlt => 1);
compile (
verilator_flags2 => ["--protect-lib",
"secret"],
verilator_make_gcc => 0,
fails => 1,
expect_filename => $Self->{golden_filename},
);
#run(cmd=>["make",
# "-C",
# "$Self->{obj_dir}",
# "-f",
# "V$Self->{name}.mk"]);
ok(1);
1;

View File

@ -0,0 +1,14 @@
// DESCRIPTION: Verilator: Verilog Test module
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2019 by Todd Strader.
module secret_impl (
input a,
input oe,
inout z,
output y);
assign z = oe ? a : 1'bz;
assign y = z;
endmodule

View File

@ -0,0 +1,27 @@
#!/usr/bin/perl
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.
scenarios(vlt => 1);
compile (
verilator_flags2 => ["--protect-lib",
"secret",
"--protect-key",
"SECRET_FAKE_KEY"],
verilator_make_gcc => 0,
);
run(cmd=>["make",
"-C",
"$Self->{obj_dir}",
"-f",
"V$Self->{name}.mk"]);
ok(1);
1;

View File

@ -0,0 +1,57 @@
// DESCRIPTION: Verilator: Verilog Test module
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2019 by Todd Strader.
module secret_impl (
input [31:0] accum_in,
output wire [31:0] accum_out,
input accum_bypass,
output [31:0] accum_bypass_out,
input s1_in,
output logic s1_out,
input [1:0] s2_in,
output logic [1:0] s2_out,
input [7:0] s8_in,
output logic [7:0] s8_out,
input [32:0] s33_in,
output logic [32:0] s33_out,
input [63:0] s64_in,
output logic [63:0] s64_out,
input [64:0] s65_in,
output logic [64:0] s65_out,
input [128:0] s129_in,
output logic [128:0] s129_out,
input [3:0] [31:0] s4x32_in,
output logic [3:0] [31:0] s4x32_out,
input clk);
logic [31:0] secret_accum_q = 0;
logic [31:0] secret_value = 7;
initial $display("created %m");
always @(posedge clk) begin
secret_accum_q <= secret_accum_q + accum_in + secret_value;
end
// Test combinatorial paths of different sizes
always @(*) begin
s1_out = s1_in;
s2_out = s2_in;
s8_out = s8_in;
s33_out = s33_in;
s64_out = s64_in;
s65_out = s65_in;
s129_out = s129_in;
s4x32_out = s4x32_in;
end
// Test sequential path
assign accum_out = secret_accum_q;
// Test mixed combinatorial/sequential path
assign accum_bypass_out = accum_bypass ? accum_in : secret_accum_q;
final $display("destroying %m");
endmodule

View File

@ -0,0 +1,5 @@
%Error: Internal Error: t/t_prot_lib_unpacked_bad.v:6: ../V3ProtectLib.cpp:347: Unsupported: unpacked arrays with protect-lib
input unpacked_in [7:0],
^~~~~~~~~~~
... See the manual and http://www.veripool.org/verilator for more assistance.
%Error: Command Failed

View File

@ -0,0 +1,27 @@
#!/usr/bin/perl
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.
scenarios(vlt => 1);
compile (
verilator_flags2 => ["--protect-lib",
"secret"],
verilator_make_gcc => 0,
fails => 1,
expect_filename => $Self->{golden_filename},
);
#run(cmd=>["make",
# "-C",
# "$Self->{obj_dir}",
# "-f",
# "V$Self->{name}.mk"]);
ok(1);
1;

View File

@ -0,0 +1,16 @@
// DESCRIPTION: Verilator: Verilog Test module
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2019 by Todd Strader.
module secret_impl (
input unpacked_in [7:0],
output unpacked_out [7:0]);
genvar i;
generate
for (i = 0; i < 8; i = i + 1) begin
assign unpacked_out[i] = unpacked_in[i];
end
endgenerate
endmodule