diff --git a/Changes b/Changes index 7b0c95540..5da48a5c3 100644 --- a/Changes +++ b/Changes @@ -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] diff --git a/Makefile.in b/Makefile.in index 5b9385eff..5ce1c1ab0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 diff --git a/README.pod b/README.pod index c551224ba..3587f61a7 100644 --- a/README.pod +++ b/README.pod @@ -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 diff --git a/bin/verilator b/bin/verilator index 0e34999e7..58a5bd21f 100755 --- a/bin/verilator +++ b/bin/verilator @@ -354,6 +354,7 @@ detailed descriptions in L for more information. --prof-threads Enable generating gantt chart data for threads --protect-key Key for symbol protection --protect-ids Hash identifier names for obscurity + --protect-lib 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 + +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.a and libI.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. diff --git a/examples/make_protect_lib/.gitignore b/examples/make_protect_lib/.gitignore new file mode 100644 index 000000000..b2398843e --- /dev/null +++ b/examples/make_protect_lib/.gitignore @@ -0,0 +1,2 @@ +obj_* +logs diff --git a/examples/make_protect_lib/Makefile b/examples/make_protect_lib/Makefile new file mode 100644 index 000000000..0f9f2e1a3 --- /dev/null +++ b/examples/make_protect_lib/Makefile @@ -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 diff --git a/examples/make_protect_lib/secret_impl.v b/examples/make_protect_lib/secret_impl.v new file mode 100644 index 000000000..a72344960 --- /dev/null +++ b/examples/make_protect_lib/secret_impl.v @@ -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 diff --git a/examples/make_protect_lib/sim_main.cpp b/examples/make_protect_lib/sim_main.cpp new file mode 100644 index 000000000..b457fc1ae --- /dev/null +++ b/examples/make_protect_lib/sim_main.cpp @@ -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 + +#include "Vtop.h" + +#if VM_TRACE +# include +#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); +} diff --git a/examples/make_protect_lib/top.v b/examples/make_protect_lib/top.v new file mode 100644 index 000000000..76410e2c7 --- /dev/null +++ b/examples/make_protect_lib/top.v @@ -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 diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 9a084e3d7..74a3b0173 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -221,6 +221,7 @@ RAW_OBJS = \ V3Partition.o \ V3PreShell.o \ V3Premit.o \ + V3ProtectLib.o \ V3Reloop.o \ V3Scope.o \ V3Scoreboard.o \ diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index 7ec891267..6fe968e53 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -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(); } diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index ba0f474c5..801323d3c 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -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); } } } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index d1044a926..735b710af 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -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) + + +//###################################################################### +// 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 \n"); + txtp->addText(fl, "#include \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__<<": "<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: "<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: "<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__<<": "< 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; diff --git a/test_regress/t/t_prot_lib.v b/test_regress/t/t_prot_lib.v new file mode 100644 index 000000000..a7ca0f5fc --- /dev/null +++ b/test_regress/t/t_prot_lib.v @@ -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 diff --git a/test_regress/t/t_prot_lib_inout_bad.out b/test_regress/t/t_prot_lib_inout_bad.out new file mode 100644 index 000000000..485850149 --- /dev/null +++ b/test_regress/t/t_prot_lib_inout_bad.out @@ -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 diff --git a/test_regress/t/t_prot_lib_inout_bad.pl b/test_regress/t/t_prot_lib_inout_bad.pl new file mode 100755 index 000000000..99abf81a8 --- /dev/null +++ b/test_regress/t/t_prot_lib_inout_bad.pl @@ -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; diff --git a/test_regress/t/t_prot_lib_inout_bad.v b/test_regress/t/t_prot_lib_inout_bad.v new file mode 100644 index 000000000..2d62a1ecc --- /dev/null +++ b/test_regress/t/t_prot_lib_inout_bad.v @@ -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 diff --git a/test_regress/t/t_prot_lib_secret.pl b/test_regress/t/t_prot_lib_secret.pl new file mode 100755 index 000000000..2650b9092 --- /dev/null +++ b/test_regress/t/t_prot_lib_secret.pl @@ -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; diff --git a/test_regress/t/t_prot_lib_secret.v b/test_regress/t/t_prot_lib_secret.v new file mode 100644 index 000000000..54a075f74 --- /dev/null +++ b/test_regress/t/t_prot_lib_secret.v @@ -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 diff --git a/test_regress/t/t_prot_lib_unpacked_bad.out b/test_regress/t/t_prot_lib_unpacked_bad.out new file mode 100644 index 000000000..4448cde29 --- /dev/null +++ b/test_regress/t/t_prot_lib_unpacked_bad.out @@ -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 diff --git a/test_regress/t/t_prot_lib_unpacked_bad.pl b/test_regress/t/t_prot_lib_unpacked_bad.pl new file mode 100755 index 000000000..99abf81a8 --- /dev/null +++ b/test_regress/t/t_prot_lib_unpacked_bad.pl @@ -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; diff --git a/test_regress/t/t_prot_lib_unpacked_bad.v b/test_regress/t/t_prot_lib_unpacked_bad.v new file mode 100644 index 000000000..9c10eb26a --- /dev/null +++ b/test_regress/t/t_prot_lib_unpacked_bad.v @@ -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