diff --git a/Changes b/Changes index 6336e6c2d..6ecedd938 100644 --- a/Changes +++ b/Changes @@ -6,6 +6,8 @@ The contributors that suggested a given feature are shown in []. Thanks! **** Fix MacOS portability, bug1232. [Jeff Bush] +**** Detect MSB overflow when under VL_DEBUG, bug1238. [Junyi Xi] + * Verilator 3.914 2017-10-14 diff --git a/include/verilated.cpp b/include/verilated.cpp index 869520aa0..da56d1e6f 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1488,6 +1488,14 @@ const char* Verilated::commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE { return outstr; } +void Verilated::overWidthError(const char* signame) VL_MT_SAFE { + // Slowpath - Called only when signal sets too high of a bit + std::string msg = (std::string("Testbench C set input '") + + signame + + "' to value that overflows what the signal's width can fit"); + VL_FATAL_MT("unknown",0,"", msg.c_str()); +} + void Verilated::quiesce() VL_MT_SAFE { #ifdef VL_THREADED // Wait until all threads under this evaluation are quiet diff --git a/include/verilated.h b/include/verilated.h index 96d1ccdcd..a39eab78c 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -429,9 +429,14 @@ public: // METHODS - INTERNAL USE ONLY (but public due to what uses it) // Internal: Create a new module name by concatenating two strings static const char* catName(const char* n1, const char* n2); // Returns static data + + // Internal: Throw signal assertion + static void overWidthError(const char* signame) VL_MT_SAFE; + // Internal: Find scope static const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE; static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE; + // Internal: Get and set DPI context static const VerilatedScope* dpiScope() VL_MT_SAFE { return t_s.t_dpiScopep; } static void dpiScope(const VerilatedScope* scopep) VL_MT_SAFE { t_s.t_dpiScopep=scopep; } @@ -442,6 +447,7 @@ public: static const char* dpiFilenamep() VL_MT_SAFE { return t_s.t_dpiFilename; } static int dpiLineno() VL_MT_SAFE { return t_s.t_dpiLineno; } static int exportFuncNum(const char* namep) VL_MT_SAFE; + static size_t serializedSize() VL_PURE { return sizeof(s_s); } static void* serializedPtr() VL_MT_UNSAFE { return &s_s; } // Unsafe, for Serialize only #ifdef VL_THREADED diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index cc1414aec..a5e322fbe 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -2878,7 +2878,7 @@ private: bool m_unique0Pragma; // unique0 case bool m_priorityPragma; // priority case public: - AstIf(FileLine* fileline, AstNode* condp, AstNode* ifsp, AstNode* elsesp) + AstIf(FileLine* fileline, AstNode* condp, AstNode* ifsp, AstNode* elsesp=NULL) : AstNodeIf(fileline, condp, ifsp, elsesp) { m_uniquePragma=false; m_unique0Pragma=false; m_priorityPragma=false; } @@ -5153,6 +5153,7 @@ private: string m_cname; // C name, for dpiExports string m_rtnType; // void, bool, or other return type string m_argTypes; + string m_ifdef; // #ifdef symbol around this function bool m_dontCombine:1; // V3Combine shouldn't compare this func tree, it's special bool m_skipDecl:1; // Don't declare it bool m_declPrivate:1; // Declare it private @@ -5225,6 +5226,8 @@ public: void funcPublic(bool flag) { m_funcPublic = flag; } void argTypes(const string& str) { m_argTypes = str; } string argTypes() const { return m_argTypes; } + void ifdef(const string& str) { m_ifdef = str; } + string ifdef() const { return m_ifdef; } void funcType(AstCFuncType flag) { m_funcType = flag; } AstCFuncType funcType() const { return m_funcType; } bool isInline() const { return m_isInline; } diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index c53e5ce40..3bb982af2 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -23,6 +23,8 @@ // for all AstCoverDecl, move the declaration into a _configure_coverage AstCFunc. // For each variable that needs reset, add a AstCReset node. // +// For primary inputs, add _eval_debug_assertions. +// // This transformation honors outputSplitCFuncs. //************************************************************************* #include "config_build.h" @@ -101,8 +103,46 @@ private: //###################################################################### +void V3CCtors::evalAsserts() { + AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module + AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", NULL, "void"); + funcp->declPrivate(true); + funcp->isStatic(false); + funcp->slow(false); + funcp->ifdef("VL_DEBUG"); + modp->addStmtp(funcp); + for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) { + if (AstVar* varp = np->castVar()) { + if (varp->isPrimaryIn() && !varp->isSc()) { + if (AstBasicDType* basicp = varp->dtypeSkipRefp()->castBasicDType()) { + int storedWidth = basicp->widthAlignBytes() * 8; + int lastWordWidth = varp->width() % storedWidth; + if (lastWordWidth != 0) { + // if (signal & CONST(upper_non_clean_mask)) { fail; } + AstNode* newp = new AstVarRef(varp->fileline(), varp, false); + if (varp->isWide()) { + newp = new AstWordSel(varp->fileline(), newp, + new AstConst(varp->fileline(), varp->widthWords()-1)); + } + uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth); + V3Number num (varp->fileline(), storedWidth, value); + newp = new AstAnd(varp->fileline(), newp, + new AstConst(varp->fileline(), num)); + AstNodeIf* ifp = new AstIf(varp->fileline(), newp, + new AstCStmt(varp->fileline(), "Verilated::overWidthError(\""+varp->prettyName()+"\");")); + ifp->branchPred(AstBranchPred::BP_UNLIKELY); + newp = ifp; + funcp->addStmtsp(newp); + } + } + } + } + } +} + void V3CCtors::cctorsAll() { UINFO(2,__FUNCTION__<<": "<modulesp(); modp; modp=modp->nextp()->castNodeModule()) { // Process each module in turn { diff --git a/src/V3CCtors.h b/src/V3CCtors.h index ab7764b11..c8c844f5b 100644 --- a/src/V3CCtors.h +++ b/src/V3CCtors.h @@ -30,6 +30,8 @@ class V3CCtors { public: static void cctorsAll(); +private: + static void evalAsserts(); }; diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 9b430f089..dc0a252ae 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -872,6 +872,7 @@ class EmitCImp : EmitCStmts { splitSizeInc(nodep); puts("\n"); + if (nodep->ifdef()!="") puts("#ifdef "+nodep->ifdef()+"\n"); if (nodep->isInline()) puts("VL_INLINE_OPT "); puts(nodep->rtnTypeVoid()); puts(" "); puts(modClassName(m_modp)+"::"+nodep->name() @@ -908,6 +909,7 @@ class EmitCImp : EmitCStmts { //puts("__Vm_activity = true;\n"); puts("}\n"); + if (nodep->ifdef()!="") puts("#endif // "+nodep->ifdef()+"\n"); } void emitChangeDet() { @@ -1731,14 +1733,18 @@ void EmitCImp::emitSensitives() { void EmitCImp::emitWrapEval(AstNodeModule* modp) { puts("\nvoid "+modClassName(modp)+"::eval() {\n"); + puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate "+modClassName(modp)+"::eval\\n\"); );\n"); puts(EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp; // Setup global symbol table\n"); puts(EmitCBaseVisitor::symTopAssign()+"\n"); + puts("#ifdef VL_DEBUG\n"); + putsDecoration("// Debug assertions\n"); + puts("_eval_debug_assertions();\n"); + puts("#endif // VL_DEBUG\n"); putsDecoration("// Initialize\n"); puts("if (VL_UNLIKELY(!vlSymsp->__Vm_didInit)) _eval_initial_loop(vlSymsp);\n"); if (v3Global.opt.inhibitSim()) { puts("if (VL_UNLIKELY(__Vm_inhibitSim)) return;\n"); } - puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate "+modClassName(modp)+"::eval\\n\"); );\n"); if (v3Global.opt.threads()) { // THREADED-TODO move to per-train uint32_t trainId = 0; @@ -1859,9 +1865,11 @@ void EmitCImp::emitIntFuncDecls(AstNodeModule* modp) { AstCFunc* funcp = *it; if (!funcp->dpiImport()) { // DPI is prototyped in __Dpi.h ofp()->putsPrivate(funcp->declPrivate()); + if (funcp->ifdef()!="") puts("#ifdef "+funcp->ifdef()+"\n"); if (funcp->isStatic()) puts("static "); puts(funcp->rtnTypeVoid()); puts(" "); puts(funcp->name()); puts("("+cFuncArgs(funcp)+");\n"); + if (funcp->ifdef()!="") puts("#endif // "+funcp->ifdef()+"\n"); } } } diff --git a/test_regress/t/t_trace_cat.cpp b/test_regress/t/t_trace_cat.cpp index 08786d9ea..27978f647 100644 --- a/test_regress/t/t_trace_cat.cpp +++ b/test_regress/t/t_trace_cat.cpp @@ -43,7 +43,7 @@ int main(int argc, char **argv, char **env) { top->clk = 0; while (main_time < 190) { // Creates 2 files - top->clk = ~top->clk; + top->clk = !top->clk; top->eval(); if ((main_time % 100) == 0) { diff --git a/test_regress/t/t_trace_timescale.cpp b/test_regress/t/t_trace_timescale.cpp index 7c9b86a5f..284a8515a 100644 --- a/test_regress/t/t_trace_timescale.cpp +++ b/test_regress/t/t_trace_timescale.cpp @@ -32,7 +32,7 @@ int main(int argc, char **argv, char **env) { top->clk = 0; while (main_time < 190*VL_TIME_MULTIPLIER) { - top->clk = ~top->clk; + top->clk = !top->clk; top->eval(); tfp->dump((unsigned int)(main_time)); // Advance by 0.5 time units, to make sure our fractional diff --git a/test_regress/t/t_var_overwidth_bad.cpp b/test_regress/t/t_var_overwidth_bad.cpp new file mode 100644 index 000000000..e75242abd --- /dev/null +++ b/test_regress/t/t_var_overwidth_bad.cpp @@ -0,0 +1,44 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2010-2011 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License. +// Version 2.0. +// +// Verilator is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +//************************************************************************* + +#include "Vt_var_overwidth_bad.h" +#include "verilated.h" + +//====================================================================== + +double main_time; + +double sc_time_stamp () { + return main_time; +} + +int main(int argc, char **argv, char **env) { + Verilated::debug(0); + + VM_PREFIX* topp = new VM_PREFIX (""); // Note null name - we're flattening it out + + main_time = 0; + + topp->clk = 0; + topp->eval(); + main_time += 10; + + topp->clk = 0x2; // ILLEGAL + topp->eval(); + topp->final(); + + delete topp; topp=NULL; + exit(0L); +} diff --git a/test_regress/t/t_var_overwidth_bad.pl b/test_regress/t/t_var_overwidth_bad.pl new file mode 100755 index 000000000..16a1b18c2 --- /dev/null +++ b/test_regress/t/t_var_overwidth_bad.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2010 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. + +compile ( + make_main => 0, + verilator_flags2 => ["--exe $Self->{t_dir}/t_var_overwidth_bad.cpp"], + ); + +execute ( + fails=>1, + expect=> +qr{%Error: unknown:0: Testbench C set input 'clk' to value that overflows what the signal's width can fit +Aborting....*} + ); + +ok(1); +1; diff --git a/test_regress/t/t_var_overwidth_bad.v b/test_regress/t/t_var_overwidth_bad.v new file mode 100644 index 000000000..52fe460f7 --- /dev/null +++ b/test_regress/t/t_var_overwidth_bad.v @@ -0,0 +1,19 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2010 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + always @ (posedge clk) begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule