From c38e8182c2960edc2e8c3eb73f54b9cc16d15d82 Mon Sep 17 00:00:00 2001 From: Cary R Date: Tue, 22 Apr 2008 11:32:10 -0700 Subject: [PATCH] Add checks that verify an always statement has delay. This patch adds check to determine if an always block has delay in it or not. If there is no delay a runtime infinite loop will occur. For the indeterminate case it will print a warning message if the new -Winfloop flag is given. This flag is not part of the -Wall check! --- compiler.h | 5 +- driver/Makefile.in | 2 +- driver/iverilog.man | 17 +++- driver/main.c | 10 ++- elaborate.cc | 23 +++++ main.cc | 109 ++--------------------- netlist.cc | 204 ++++++++++++++++++++++++++++++++++++++++++++ netlist.h | 14 +++ 8 files changed, 274 insertions(+), 110 deletions(-) diff --git a/compiler.h b/compiler.h index 0f78bdd73..99b6d52cb 100644 --- a/compiler.h +++ b/compiler.h @@ -1,7 +1,7 @@ #ifndef __compiler_H #define __compiler_H /* - * Copyright (c) 1999-2007 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -76,6 +76,9 @@ extern bool warn_timescale; /* Warn about legal but questionable module port bindings. */ extern bool warn_portbinding; +/* Warn about structures that may have infinite loops. */ +extern bool warn_inf_loop; + /* This is true if verbose output is requested. */ extern bool verbose_flag; diff --git a/driver/Makefile.in b/driver/Makefile.in index 037cd202e..9968923e9 100644 --- a/driver/Makefile.in +++ b/driver/Makefile.in @@ -21,7 +21,7 @@ # SHELL = /bin/sh -VERSION = 0.8 +VERSION = 0.9.devel prefix = @prefix@ exec_prefix = @exec_prefix@ diff --git a/driver/iverilog.man b/driver/iverilog.man index bc5ee4b28..8c1356e97 100644 --- a/driver/iverilog.man +++ b/driver/iverilog.man @@ -1,4 +1,4 @@ -.TH iverilog 1 "$Date: 2007/06/05 01:56:12 $" Version "$Date: 2007/06/05 01:56:12 $" +.TH iverilog 1 "April 22nd, 2008" Version "0.9.devel" .SH NAME iverilog - Icarus Verilog compiler @@ -251,6 +251,21 @@ inherit timescale from another file. Both probably mean that timescales are inconsistent, and simulation timing can be confusing and dependent on compilation order. +.TP 8 +.B infloop +This enables warnings for \fRalways\fP statements that may have runtime +infinite loops (has paths with no or zero delay). This class of warnings +is not included in \fB-Wall\fP and hence does not have a \fBno-\fP variant. +A fatal error message will always be printed when the compiler can +determine that there will definitely be an infinite loop (all paths have +no or zero delay). + +When you suspect an always statement is producing a runtime infinite loop +use this flag to find the always statements that need to have their logic +verified. It is expected that many of the warnings will be false +positives, since the code treats the value of all variables and signals +as indeterminate. + .SH "SYSTEM FUNCTION TABLE FILES" If the source file name as a \fB.sft\fP suffix, then it is taken to be a system function table file. A System function table file is used to diff --git a/driver/main.c b/driver/main.c index 0613a9f9a..e7f54b592 100644 --- a/driver/main.c +++ b/driver/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2007 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -358,6 +358,11 @@ static void process_warning_switch(const char*name) } else if (strcmp(name,"timescale") == 0) { if (! strchr(warning_flags, 't')) strcat(warning_flags, "t"); + /* Since the infinite loop check is not part of 'all' it + * does not have a no- version. */ + } else if (strcmp(name,"infloop") == 0) { + if (! strchr(warning_flags, 'l')) + strcat(warning_flags, "l"); } else if (strcmp(name,"no-implicit") == 0) { char*cp = strchr(warning_flags, 'i'); if (cp) while (*cp) { @@ -703,7 +708,7 @@ int main(int argc, char **argv) if (version_flag || verbose_flag) { printf("Icarus Verilog version " VERSION " (" VERSION_TAG ")\n\n"); - printf("Copyright 1998-2007 Stephen Williams\n"); + printf("Copyright 1998-2008 Stephen Williams\n"); puts(NOTICE); if (version_flag) @@ -841,4 +846,3 @@ int main(int argc, char **argv) return 0; } - diff --git a/elaborate.cc b/elaborate.cc index b363227db..ddf5725aa 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -3277,6 +3277,29 @@ bool PProcess::elaborate(Design*des, NetScope*scope) const verinum(1)); } while (0); + /* If this is an always block and we have no or zero delay then + * a runtime infinite loop will happen. If we possible have some + * delay then print a warning that an infinite loop is possible. + */ + if (type() == PProcess::PR_ALWAYS) { + DelayType dly_type = top->statement()->delay_type(); + + if (dly_type == NO_DELAY || dly_type == ZERO_DELAY) { + cerr << get_fileline() << ": error: always statement" + << " does not have any delay." << endl; + cerr << get_fileline() << ": : A runtime infinite" + << " loop will occur." << endl; + des->errors += 1; + return false; + + } else if (dly_type == POSSIBLE_DELAY && warn_inf_loop) { + cerr << get_fileline() << ": warning: always statement" + << " may not have any delay." << endl; + cerr << get_fileline() << ": : A runtime infinite" + << " loop may be possible." << endl; + } + } + return true; } diff --git a/main.cc b/main.cc index 23337cf9e..655413cc9 100644 --- a/main.cc +++ b/main.cc @@ -1,6 +1,6 @@ const char COPYRIGHT[] = - "Copyright (c) 1998-2005 Stephen Williams (steve@icarus.com)"; + "Copyright (c) 1998-2008 Stephen Williams (steve@icarus.com)"; /* * This source code is free software; you can redistribute it @@ -18,9 +18,6 @@ const char COPYRIGHT[] = * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: main.cc,v 1.95 2007/04/19 02:52:53 steve Exp $" -#endif # include "config.h" @@ -113,6 +110,7 @@ FILE *depend_file = NULL; bool warn_implicit = false; bool warn_timescale = false; bool warn_portbinding = false; +bool warn_inf_loop = false; bool error_implicit = false; @@ -434,6 +432,9 @@ static void read_iconfig_file(const char*ipath) case 'i': warn_implicit = true; break; + case 'l': + warn_inf_loop = true; + break; case 'p': warn_portbinding = true; break; @@ -830,103 +831,3 @@ int main(int argc, char*argv[]) return des? des->errors : 1; } - -/* - * $Log: main.cc,v $ - * Revision 1.95 2007/04/19 02:52:53 steve - * Add support for -v flag in command file. - * - * Revision 1.94 2007/03/07 04:24:59 steve - * Make integer width controllable. - * - * Revision 1.93 2006/09/28 04:35:18 steve - * Support selective control of specify and xtypes features. - * - * Revision 1.92 2005/07/14 23:38:43 steve - * Display as version 0.9.devel - * - * Revision 1.91 2005/07/07 16:22:49 steve - * Generalize signals to carry types. - * - * Revision 1.90 2005/06/28 04:25:55 steve - * Remove reference to SystemVerilog. - * - * Revision 1.89 2005/04/24 23:44:02 steve - * Update DFF support to new data flow. - * - * Revision 1.88 2005/01/22 01:06:55 steve - * Change case compare from logic to an LPM node. - * - * Revision 1.87 2004/12/11 02:31:26 steve - * Rework of internals to carry vectors through nexus instead - * of single bits. Make the ivl, tgt-vvp and vvp initial changes - * down this path. - * - * Revision 1.86 2004/10/04 01:10:53 steve - * Clean up spurious trailing white space. - * - * Revision 1.85 2004/09/25 01:58:44 steve - * Add a debug_elaborate flag - * - * Revision 1.84 2004/09/10 23:51:42 steve - * Fix the evaluation of constant ternary expressions. - * - * Revision 1.83 2004/09/05 17:44:42 steve - * Add support for module instance arrays. - * - * Revision 1.82 2004/03/10 04:51:24 steve - * Add support for system function table files. - * - * Revision 1.81 2004/02/18 17:11:56 steve - * Use perm_strings for named langiage items. - * - * Revision 1.80 2004/02/15 00:19:29 steve - * Report elaboration errors without crashing. - * - * Revision 1.79 2003/11/26 01:37:14 steve - * Properly initialize vpi_module_list with system. - * - * Revision 1.78 2003/11/13 05:55:33 steve - * Move the DLL= flag to target config files. - * - * Revision 1.77 2003/11/13 04:09:49 steve - * Pass flags through the temporary config file. - * - * Revision 1.76 2003/11/13 03:10:38 steve - * ivl -F and -t flags are onpassed throught the -C file. - * - * Revision 1.75 2003/11/10 20:59:03 steve - * Design::get_flag returns const char* instead of string. - * - * Revision 1.74 2003/11/01 04:22:30 steve - * Accept functors in the config file. - * - * Revision 1.73 2003/10/26 22:43:42 steve - * Improve -V messages, - * - * Revision 1.72 2003/09/26 02:17:14 steve - * Delete pform when done with it. - * - * Revision 1.71 2003/09/25 00:25:14 steve - * Summary list of missing modules. - * - * Revision 1.70 2003/09/23 05:57:36 steve - * Pass -m flag from driver via iconfig file. - * - * Revision 1.69 2003/09/22 01:12:08 steve - * Pass more ivl arguments through the iconfig file. - * - * Revision 1.68 2003/06/20 00:53:19 steve - * Module attributes from the parser - * through to elaborated form. - * - * Revision 1.67 2003/04/24 05:25:27 steve - * Dump design even on errors. - * - * Revision 1.66 2003/03/01 06:25:30 steve - * Add the lex_strings string handler, and put - * scope names and system task/function names - * into this table. Also, permallocate event - * names from the beginning. - */ - diff --git a/netlist.cc b/netlist.cc index a9aa26a21..25a262d91 100644 --- a/netlist.cc +++ b/netlist.cc @@ -2417,3 +2417,207 @@ const NetProc*NetTaskDef::proc() const { return proc_; } + +/* + * These are the delay_type() functions. They are used to determine + * the type of delay for the given object. + */ + +/* + * This function implements the following table: + * + * in_A in_B out + * NO NO NO + * NO ZERO ZERO + * NO POS POS + * NO DEF POS + * ZERO NO ZERO + * ZERO ZERO ZERO + * ZERO POS POS + * ZERO DEF POS + * POS NO POS + * POS ZERO POS + * POS POS POS + * POS DEF POS + * DEF NO POS + * DEF ZERO POS + * DEF POS POS + * DEF DEF DEF + * + * It is used to combine two delay values. + */ +static DelayType combine_delays(const DelayType a, const DelayType b) +{ + /* The default is POSSIBLE_DELAY. */ + DelayType result = POSSIBLE_DELAY; + + /* If both are no or zero delay then we return ZERO_DELAY. */ + if ((a == NO_DELAY || a == ZERO_DELAY) && + (b == NO_DELAY || b == ZERO_DELAY)) { + result = ZERO_DELAY; + } + + /* Except if both are no delay then we return NO_DELAY. */ + if (a == NO_DELAY && b == NO_DELAY) { + result = NO_DELAY; + } + + /* If both are definite delay then we return DEFINITE_DELAY. */ + if (a == DEFINITE_DELAY && b == DEFINITE_DELAY) { + result = DEFINITE_DELAY; + } + + return result; +} + +/* + * This is used to see what we can find out about the delay when it + * is given as an expression. We also use this for loop expressions. + */ +static DelayType delay_type_from_expr(const NetExpr*expr) +{ + DelayType result = POSSIBLE_DELAY; + + if (const NetEConst*e = dynamic_cast(expr)) { + if (e->value().is_zero()) result = ZERO_DELAY; + else result = DEFINITE_DELAY; + } + + if (const NetECReal*e = dynamic_cast(expr)) { + if (e->value().as_double() == 0.0) result = ZERO_DELAY; + else result = DEFINITE_DELAY; + } + + return result; +} + +/* + * The looping structures can use the same basic code so put it here + * instead of duplicating it for each one (repeat and while). + */ +static DelayType get_loop_delay_type(const NetExpr*expr, const NetProc*proc) +{ + DelayType result; + + switch (delay_type_from_expr(expr)) { + /* We have a constant false expression so the body never runs. */ + case ZERO_DELAY: + result = NO_DELAY; + break; + /* We have a constant true expression so the body always runs. */ + case DEFINITE_DELAY: + result = proc->delay_type(); + break; + /* We don't know if the body will run so reduce a DEFINITE_DELAY + * to a POSSIBLE_DELAY. All other stay the same. */ + case POSSIBLE_DELAY: + result = combine_delays(NO_DELAY, proc->delay_type()); + break; + /* This should never happen since delay_type_from_expr() only + * returns three different values. */ + default: + assert(0); + } + + return result; +} + +/* The default object does not have any delay. */ +DelayType NetProc::delay_type() const +{ + return NO_DELAY; +} + +DelayType NetBlock::delay_type() const +{ + DelayType result = NO_DELAY; + + for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { + DelayType dt = cur->delay_type(); + if (dt > result) result = dt; + } + + return result; +} + +DelayType NetCase::delay_type() const +{ + DelayType result = NO_DELAY; + bool def_stmt = false; + unsigned nstmts = nitems(); + + for (unsigned idx = 0; idx < nstmts; idx += 1) { + if (!expr(idx)) def_stmt = true; + if (idx == 0) { + result = stat(idx)->delay_type(); + } else { + result = combine_delays(result, stat(idx)->delay_type()); + } + } + + /* If we don't have a default statement we don't know for sure + * that we have a delay. */ + if (!def_stmt) result = combine_delays(NO_DELAY, result); + + return result; +} + +DelayType NetCondit::delay_type() const +{ + DelayType result; + + if (else_) { + result = combine_delays(if_->delay_type(), else_->delay_type()); + } else { + result = if_->delay_type(); + } + + return result; +} + +DelayType NetEvWait::delay_type() const +{ + return DEFINITE_DELAY; +} + +DelayType NetForever::delay_type() const +{ + return statement_->delay_type(); +} + +DelayType NetPDelay::delay_type() const +{ + if (expr_) { + return delay_type_from_expr(expr_); + } else { + if (delay() > 0) { + return DEFINITE_DELAY; + } else { + if (statement_) { + return statement_->delay_type(); + } else { + return NO_DELAY; + } + } + } +} + +DelayType NetRepeat::delay_type() const +{ + return get_loop_delay_type(expr_, statement_); +} + +DelayType NetTaskDef::delay_type() const +{ + return proc_->delay_type(); +} + +DelayType NetUTask::delay_type() const +{ + return task()->task_def()->delay_type(); +} + +DelayType NetWhile::delay_type() const +{ + return get_loop_delay_type(cond_, proc_); +} diff --git a/netlist.h b/netlist.h index b1d2d68b5..02f406029 100644 --- a/netlist.h +++ b/netlist.h @@ -1640,6 +1640,7 @@ class NetUDP : public NetNode { PUdp *udp; }; +enum DelayType { NO_DELAY, ZERO_DELAY, POSSIBLE_DELAY, DEFINITE_DELAY }; /* ========= * A process is a behavioral-model description. A process is a @@ -1689,6 +1690,9 @@ class NetProc : public virtual LineInfo { virtual void dump(ostream&, unsigned ind) const; + // Recursively checks to see if there is delay in this element. + virtual DelayType delay_type() const; + private: friend class NetBlock; NetProc*next_; @@ -1896,6 +1900,7 @@ class NetBlock : public NetProc { virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; + virtual DelayType delay_type() const; private: const Type type_; @@ -1939,6 +1944,7 @@ class NetCase : public NetProc { virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual DelayType delay_type() const; private: @@ -2012,6 +2018,7 @@ class NetCondit : public NetProc { virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; + virtual DelayType delay_type() const; private: NetExpr* expr_; @@ -2229,6 +2236,7 @@ class NetEvWait : public NetProc { const svector&events); virtual void dump(ostream&, unsigned ind) const; + virtual DelayType delay_type() const; private: NetProc*statement_; @@ -2296,6 +2304,7 @@ class NetForever : public NetProc { virtual NexusSet* nex_input(bool rem_out = true); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual DelayType delay_type() const; private: NetProc*statement_; @@ -2368,6 +2377,7 @@ class NetPDelay : public NetProc { virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual DelayType delay_type() const; bool emit_proc_recurse(struct target_t*) const; @@ -2392,6 +2402,7 @@ class NetRepeat : public NetProc { virtual NexusSet* nex_input(bool rem_out = true); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual DelayType delay_type() const; private: NetExpr*expr_; @@ -2473,6 +2484,7 @@ class NetTaskDef { NetNet*port(unsigned idx); void dump(ostream&, unsigned) const; + DelayType delay_type() const; private: NetScope*scope_; @@ -2541,6 +2553,7 @@ class NetUTask : public NetProc { virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual DelayType delay_type() const; private: NetScope*task_; @@ -2565,6 +2578,7 @@ class NetWhile : public NetProc { virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual DelayType delay_type() const; private: NetExpr* cond_;