Detect MSB overflow when under VL_DEBUG, bug1238.

This commit is contained in:
Wilson Snyder 2017-11-05 21:47:55 -05:00
parent 4d074b5414
commit cbb7cd16d0
12 changed files with 159 additions and 4 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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; }

View File

@ -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__<<": "<<endl);
evalAsserts();
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=modp->nextp()->castNodeModule()) {
// Process each module in turn
{

View File

@ -30,6 +30,8 @@
class V3CCtors {
public:
static void cctorsAll();
private:
static void evalAsserts();
};

View File

@ -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");
}
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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