diff --git a/Module.cc b/Module.cc index b69a3a950..80e1200e2 100644 --- a/Module.cc +++ b/Module.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2016 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 @@ -35,9 +35,6 @@ Module::Module(LexicalScope*parent, perm_string n) program_block = false; uc_drive = UCD_NONE; timescale_warn_done = false; - time_unit = 0; - time_precision = 0; - time_from_timescale = false; } Module::~Module() diff --git a/Module.h b/Module.h index f142a761d..66c60af4f 100644 --- a/Module.h +++ b/Module.h @@ -1,7 +1,7 @@ #ifndef IVL_Module_H #define IVL_Module_H /* - * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2016 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 @@ -121,10 +121,6 @@ class Module : public PScopeExtra, public LineInfo { map attributes; - /* These are the timescale for this module. The default is - set by the `timescale directive. */ - int time_unit, time_precision; - bool time_from_timescale; bool timescale_warn_done; /* The module has a list of generate schemes that appear in diff --git a/PExpr.cc b/PExpr.cc index 7e0e3693f..bce998419 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2012 Stephen Williams + * Copyright (c) 1998-2016 Stephen Williams * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -137,7 +137,7 @@ bool PEBinary::has_aa_term(Design*des, NetScope*scope) const return left_->has_aa_term(des, scope) || right_->has_aa_term(des, scope); } -PECastSize::PECastSize(unsigned si, PExpr*b) +PECastSize::PECastSize(PExpr*si, PExpr*b) : size_(si), base_(b) { } diff --git a/PExpr.h b/PExpr.h index 55f0fb237..adf17a137 100644 --- a/PExpr.h +++ b/PExpr.h @@ -1,7 +1,7 @@ #ifndef IVL_PExpr_H #define IVL_PExpr_H /* - * Copyright (c) 1998-2014 Stephen Williams + * Copyright (c) 1998-2016 Stephen Williams * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -971,7 +971,7 @@ class PECallFunction : public PExpr { class PECastSize : public PExpr { public: - explicit PECastSize(unsigned expr_wid, PExpr*base); + explicit PECastSize(PExpr*size, PExpr*base); ~PECastSize(); void dump(ostream &out) const; @@ -984,7 +984,7 @@ class PECastSize : public PExpr { width_mode_t&mode); private: - unsigned size_; + PExpr* size_; PExpr* base_; }; diff --git a/PScope.cc b/PScope.cc index 377142bf9..0b6a132d3 100644 --- a/PScope.cc +++ b/PScope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008,2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2008,2016 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 @@ -19,21 +19,9 @@ # include "PScope.h" -PScope::PScope(perm_string n, LexicalScope*parent) -: LexicalScope(parent), name_(n) +bool LexicalScope::var_init_needs_explicit_lifetime() const { -} - -PScope::PScope(perm_string n) -: LexicalScope(0), name_(n) -{ -} - -PScope::~PScope() -{ - for(map::iterator it = typedefs.begin(); - it != typedefs.end(); ++it) - delete it->second; + return false; } PWire* LexicalScope::wires_find(perm_string name) @@ -45,17 +33,26 @@ PWire* LexicalScope::wires_find(perm_string name) return (*cur).second; } -PScopeExtra::PScopeExtra(perm_string n, LexicalScope*parent) -: PScope(n, parent) +PScope::PScope(perm_string n, LexicalScope*parent) +: LexicalScope(parent), name_(n) { + time_unit = 0; + time_precision = 0; + time_from_timescale = false; } -PScopeExtra::PScopeExtra(perm_string n) -: PScope(n) +PScope::~PScope() +{ + for(map::iterator it = typedefs.begin(); + it != typedefs.end(); ++it) + delete it->second; +} + +PScopeExtra::PScopeExtra(perm_string n, LexicalScope*parent) +: PScope(n, parent) { } PScopeExtra::~PScopeExtra() { } - diff --git a/PScope.h b/PScope.h index 30e1fea7e..7e4fd6fa3 100644 --- a/PScope.h +++ b/PScope.h @@ -1,7 +1,7 @@ #ifndef IVL_PScope_H #define IVL_PScope_H /* - * Copyright (c) 2008-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2008-2016 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 @@ -36,6 +36,7 @@ class PProcess; class PClass; class PTask; class PWire; +class Statement; class Design; class NetScope; @@ -52,10 +53,14 @@ class NetScope; class LexicalScope { public: - explicit LexicalScope(LexicalScope*parent) : parent_(parent) { } + enum lifetime_t { INHERITED, STATIC, AUTOMATIC }; + + explicit LexicalScope(LexicalScope*parent) : default_lifetime(INHERITED), parent_(parent) { } // A virtual destructor is so that dynamic_cast can work. virtual ~LexicalScope() { } + lifetime_t default_lifetime; + struct range_t { // True if this is an exclude bool exclude_flag; @@ -108,6 +113,9 @@ class LexicalScope { // creating implicit nets. map genvars; + // Variable initializations in this scope + vector var_inits; + // Behaviors (processes) in this scope list behaviors; list analog_behaviors; @@ -117,6 +125,8 @@ class LexicalScope { LexicalScope* parent_scope() const { return parent_; } + virtual bool var_init_needs_explicit_lifetime() const; + protected: void dump_typedefs_(ostream&out, unsigned indent) const; @@ -130,6 +140,10 @@ class LexicalScope { void dump_wires_(ostream&out, unsigned indent) const; + void dump_var_inits_(ostream&out, unsigned indent) const; + + bool elaborate_var_inits_(Design*des, NetScope*scope) const; + private: LexicalScope*parent_; }; @@ -145,12 +159,17 @@ class PScope : public LexicalScope { // modules do not nest in Verilog, the parent must be nil for // modules. Scopes for tasks and functions point to their // containing module. - PScope(perm_string name, LexicalScope*parent); - PScope(perm_string name); + explicit PScope(perm_string name, LexicalScope*parent =0); virtual ~PScope(); perm_string pscope_name() const { return name_; } + /* These are the timescale for this scope. The default is + set by the `timescale directive or, in SystemVerilog, + by timeunit and timeprecision statements. */ + int time_unit, time_precision; + bool time_from_timescale; + protected: bool elaborate_sig_wires_(Design*des, NetScope*scope) const; @@ -168,14 +187,13 @@ class PScope : public LexicalScope { class PScopeExtra : public PScope { public: - PScopeExtra(perm_string, LexicalScope*parent); - PScopeExtra(perm_string); + explicit PScopeExtra(perm_string, LexicalScope*parent =0); ~PScopeExtra(); /* Task definitions within this module */ std::map tasks; std::map funcs; - /* class definitions within this module. */ + /* Class definitions within this module. */ std::map classes; /* This is the lexical order of the classes, and is used by elaboration to choose an elaboration order. */ diff --git a/PTask.cc b/PTask.cc index 034f38da6..93f8ba10f 100644 --- a/PTask.cc +++ b/PTask.cc @@ -30,6 +30,11 @@ PTaskFunc::~PTaskFunc() { } +bool PTaskFunc::var_init_needs_explicit_lifetime() const +{ + return default_lifetime == STATIC; +} + void PTaskFunc::set_ports(vector*p) { assert(ports_ == 0); diff --git a/PTask.h b/PTask.h index 1b636d05a..4c1899d04 100644 --- a/PTask.h +++ b/PTask.h @@ -41,6 +41,8 @@ class PTaskFunc : public PScope, public LineInfo { PTaskFunc(perm_string name, LexicalScope*parent); ~PTaskFunc(); + bool var_init_needs_explicit_lifetime() const; + void set_ports(std::vector*p); void set_this(class_type_t*use_type, PWire*this_wire); diff --git a/Statement.cc b/Statement.cc index e0ed46d24..942558107 100644 --- a/Statement.cc +++ b/Statement.cc @@ -115,6 +115,11 @@ PBlock::~PBlock() delete list_[idx]; } +bool PBlock::var_init_needs_explicit_lifetime() const +{ + return default_lifetime == STATIC; +} + PChainConstructor* PBlock::extract_chain_constructor() { if (list_.empty()) diff --git a/Statement.h b/Statement.h index b650a03f9..18e4c0f9a 100644 --- a/Statement.h +++ b/Statement.h @@ -108,7 +108,8 @@ class PAssign_ : public Statement { NetAssign_* elaborate_lval(Design*, NetScope*scope) const; NetExpr* elaborate_rval_(Design*, NetScope*, ivl_type_t lv_net_type, ivl_variable_type_t lv_type, - unsigned lv_width) const; + unsigned lv_width, + bool force_unsigned =false) const; NetExpr* elaborate_rval_(Design*, NetScope*, ivl_type_t ntype) const; NetExpr* elaborate_rval_obj_(Design*, NetScope*, @@ -182,6 +183,8 @@ class PBlock : public PScope, public Statement { BL_TYPE bl_type() const { return bl_type_; } + bool var_init_needs_explicit_lifetime() const; + // This is only used if this block is the statement list for a // constructor. We look for a PChainConstructor as the first // statement, and if it is there, extract it. diff --git a/compiler.h b/compiler.h index 7c545bed8..bd6ca25f5 100644 --- a/compiler.h +++ b/compiler.h @@ -1,7 +1,7 @@ #ifndef IVL_compiler_H #define IVL_compiler_H /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 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 @@ -142,7 +142,8 @@ extern int build_library_index(const char*path, bool key_case_sensitive); /* This is the generation of Verilog that the compiler is asked to support. Then there are also more detailed controls for more - specific language features. */ + specific language features. Note that the compiler often assumes + this is an ordered list. */ enum generation_t { GN_VER1995 = 1, GN_VER2001_NOCONFIG = 2, @@ -186,25 +187,24 @@ extern bool gn_strict_ca_eval_flag; standard expression width rules. */ extern bool gn_strict_expr_width_flag; -/* If variables can be converted to uwires by a continuous assignment - (assuming no procedural assign, then return true. This will be true - for SystemVerilog */ -static inline bool gn_var_can_be_uwire(void) +/* If this flag is true, then don't add a for-loop control variable + to an implicit event_expression list if it is only used inside the + loop. */ +extern bool gn_shared_loop_index_flag; + +static inline bool gn_system_verilog(void) { - if (generation_flag == GN_VER2005_SV || - generation_flag == GN_VER2009 || - generation_flag == GN_VER2012) + if (generation_flag >= GN_VER2005_SV) return true; return false; } -static inline bool gn_system_verilog(void) +/* If variables can be converted to uwires by a continuous assignment + (assuming no procedural assign), then return true. This will be true + for SystemVerilog */ +static inline bool gn_var_can_be_uwire(void) { - if (generation_flag == GN_VER2005_SV || - generation_flag == GN_VER2009 || - generation_flag == GN_VER2012) - return true; - return false; + return gn_system_verilog(); } static inline bool gn_modules_nest(void) diff --git a/driver/cflexor.lex b/driver/cflexor.lex index b0ba1a40b..3f3e46e80 100644 --- a/driver/cflexor.lex +++ b/driver/cflexor.lex @@ -4,7 +4,7 @@ %{ /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -124,7 +124,8 @@ int cmdfile_stack_ptr = 0; "-c" { return TOK_Dc; } "-f" { return TOK_Dc; } - /* Notice the -v flag. */ + /* Notice the -l or -v flag. */ +"-l" { return TOK_Dv; } "-v" { return TOK_Dv; } /* Notice the -y flag. */ diff --git a/driver/iverilog.man.in b/driver/iverilog.man.in index bf7d986be..fbbaa14a5 100644 --- a/driver/iverilog.man.in +++ b/driver/iverilog.man.in @@ -1,14 +1,15 @@ -.TH iverilog 1 "Aug 7th, 2015" "" "Version %M.%n%E" +.TH iverilog 1 "Oct 2nd, 2016" "" "Version %M.%n%E" .SH NAME iverilog - Icarus Verilog compiler .SH SYNOPSIS .B iverilog [\-ESVv] [\-Bpath] [\-ccmdfile|\-fcmdfile] [\-Dmacro[=defn]] -[\-Pparameter=value] [\-pflag=value] -[\-dname] [\-g1995|\-g2001|\-g2005|\-g2005-sv|\-g2009|\-g2012|\-g] +[\-Pparameter=value] [\-pflag=value] [\-dname] +[\-g1995\:|\-g2001\:|\-g2005\:|\-g2005-sv\:|\-g2009\:|\-g2012\:|\-g] [\-Iincludedir] [\-mmodule] [\-M[mode=]file] [\-Nfile] [\-ooutputfilename] -[\-stopmodule] [\-ttype] [\-Tmin/typ/max] [\-Wclass] [\-ypath] sourcefile +[\-stopmodule] [\-ttype] [\-Tmin/typ/max] [\-Wclass] [\-ypath] [\-lfile] +sourcefile .SH DESCRIPTION .PP @@ -133,12 +134,29 @@ parameter assignment is evaluated as a lossless expression, as is any expression containing an unsized constant number, and unsized constant numbers are not truncated to integer width. .TP 8 +.B -gshared-loop-index\fI|\fP-gno-shared-loop-index +Enable or disable (default) the exclusion of for-loop control variables +from implicit event_expression lists. When enabled, if a for-loop control +variable (loop index) is only used inside the for-loop statement, the +compiler will not include it in an implicit event_expression list it +calculates for that statement or any enclosing statement. This allows +the same control variable to be used in multiple processes without risk +of entering an infinite loop caused by each process triggering all other +processes that use the same varaible. For strict compliance with the +standards, this behaviour should be disabled. +.TP 8 .B -I\fIincludedir\fP Append directory \fIincludedir\fP to list of directories searched for Verilog include files. The \fB\-I\fP switch may be used many times to specify several directories to search, the directories are searched in the order they appear on the command line. .TP 8 +.B -l\fIfile\fP +Add the specified file to the list of source files to be compiled, +but mark it as a library file. All modules contained within that +file will be treated as library modules, and only elaborated if +they are instantiated by other modules in the design. +.TP 8 .B -M\fIpath\fP This is equivalent to \fB\-Mall=path\fP. Preserved for backwards compatibility. @@ -402,6 +420,15 @@ A \fB\-c\fP or \fB\-f\fP token prefixes a command file, exactly like it does on the command line. The cmdfile may be on the same line or the next non-comment line. +.TP 8 +.B -l\ \fIfile\fP -v\ \fIfile\fP +A \fB\-l\fP token prefixes a library file in the command file, +exactly like it does on the command line. The parameter to the \fB\-l\fP +flag may be on the same line or the next non-comment line. \fB\-v\fP is +an alias for \fB\-l\fP, provided for compatibility with other simulators. + +Variables in the \fIfile\fP are substituted. + .TP 8 .B -y\ \fIlibdir\fP A \fB\-y\fP token prefixes a library directory in the command file, @@ -532,7 +559,7 @@ Tips on using, debugging, and developing the compiler can be found at .SH COPYRIGHT .nf -Copyright \(co 2002\-2015 Stephen Williams +Copyright \(co 2002\-2016 Stephen Williams This document can be freely redistributed according to the terms of the GNU General Public License version 2.0 diff --git a/driver/main.c b/driver/main.c index 7144aad5a..93de379c1 100644 --- a/driver/main.c +++ b/driver/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2016 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 @@ -44,7 +44,7 @@ const char HELP[] = " [-M [mode=]depfile] [-m module]\n" " [-N file] [-o filename] [-p flag=value]\n" " [-s topmodule] [-t target] [-T min|typ|max]\n" -" [-W class] [-y dir] [-Y suf] source_file(s)\n" +" [-W class] [-y dir] [-Y suf] [-l file] source_file(s)\n" "\n" "See the man page for details."; @@ -128,6 +128,7 @@ const char*gen_icarus = "icarus-misc"; const char*gen_io_range_error = "io-range-error"; const char*gen_strict_ca_eval = "no-strict-ca-eval"; const char*gen_strict_expr_width = "no-strict-expr-width"; +const char*gen_shared_loop_index = "no-shared-loop-index"; const char*gen_verilog_ams = "no-verilog-ams"; /* Boolean: true means use a default include dir, false means don't */ @@ -730,6 +731,12 @@ static int process_generation(const char*name) else if (strcmp(name,"no-strict-expr-width") == 0) gen_strict_expr_width = "no-strict-expr-width"; + else if (strcmp(name,"shared-loop-index") == 0) + gen_shared_loop_index = "shared-loop-index"; + + else if (strcmp(name,"no-shared-loop-index") == 0) + gen_shared_loop_index = "no-shared-loop-index"; + else if (strcmp(name,"verilog-ams") == 0) gen_verilog_ams = "verilog-ams"; @@ -755,7 +762,8 @@ static int process_generation(const char*name) " icarus-misc | no-icarus-misc\n" " io-range-error | no-io-range-error\n" " strict-ca-eval | no-strict-ca-eval\n" - " strict-expr-width | no-strict-expr-width\n"); + " strict-expr-width | no-strict-expr-width\n" + " shared-loop-index | no-shared-loop-index\n"); return 1; } @@ -920,7 +928,7 @@ int main(int argc, char **argv) } } - while ((opt = getopt(argc, argv, "B:c:D:d:Ef:g:hI:M:m:N:o:P:p:Ss:T:t:vVW:y:Y:")) != EOF) { + while ((opt = getopt(argc, argv, "B:c:D:d:Ef:g:hl:I:M:m:N:o:P:p:Ss:T:t:vVW:y:Y:")) != EOF) { switch (opt) { case 'B': @@ -975,6 +983,10 @@ int main(int argc, char **argv) process_include_dir(optarg); break; + case 'l': + process_file_name(optarg, 1); + break; + case 'M': if (process_depfile(optarg) != 0) return -1; @@ -1108,6 +1120,7 @@ int main(int argc, char **argv) fprintf(iconfig_file, "generation:%s\n", gen_io_range_error); fprintf(iconfig_file, "generation:%s\n", gen_strict_ca_eval); fprintf(iconfig_file, "generation:%s\n", gen_strict_expr_width); + fprintf(iconfig_file, "generation:%s\n", gen_shared_loop_index); fprintf(iconfig_file, "generation:%s\n", gen_verilog_ams); fprintf(iconfig_file, "generation:%s\n", gen_icarus); fprintf(iconfig_file, "warnings:%s\n", warning_flags); diff --git a/elab_expr.cc b/elab_expr.cc index fbaa0246b..c1923b83c 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -90,7 +90,7 @@ static NetBranch* find_existing_implicit_branch(NetNet*sig, NetNet*gnd) NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_type_t lv_net_type, ivl_variable_type_t lv_type, unsigned lv_width, - PExpr*expr, bool need_const) + PExpr*expr, bool need_const, bool force_unsigned) { if (debug_elaborate) { cerr << expr->get_fileline() << ": elaborate_rval_expr: " @@ -135,7 +135,7 @@ NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_type_t lv_net_type, } return elab_and_eval(des, scope, expr, context_wid, need_const, - false, lv_type); + false, lv_type, force_unsigned); } /* @@ -675,10 +675,7 @@ NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, NetExpr*tmp = new NetEBComp(op_, lp, rp); tmp->set_line(*this); - tmp = pad_to_width(tmp, expr_wid, *this); - tmp->cast_signed(signed_flag_); - - return tmp; + return pad_to_width(tmp, expr_wid, signed_flag_, *this); } unsigned PEBLogic::test_width(Design*, NetScope*, width_mode_t&) @@ -716,10 +713,7 @@ NetExpr*PEBLogic::elaborate_expr(Design*des, NetScope*scope, NetExpr*tmp = new NetEBLogic(op_, lp, rp); tmp->set_line(*this); - tmp = pad_to_width(tmp, expr_wid, *this); - tmp->cast_signed(signed_flag_); - - return tmp; + return pad_to_width(tmp, expr_wid, signed_flag_, *this); } unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode) @@ -1035,8 +1029,7 @@ NetExpr*PEBShift::elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, tmp->set_line(*this); tmp = new NetESelect(lp, tmp, 1); tmp->set_line(*this); - tmp = pad_to_width(tmp, expr_wid, *this); - tmp->cast_signed(true); + tmp = pad_to_width(tmp, expr_wid, true, *this); delete rp; return tmp; @@ -1414,23 +1407,7 @@ NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, unsigned wid) const << " from expr_width()=" << expr->expr_width() << endl; } - /* If the expression is a const, then replace it with a new - const. This is a more efficient result. */ - if (NetEConst*tmp = dynamic_cast(expr)) { - tmp->cast_signed(signed_flag_); - if (wid != tmp->expr_width()) { - tmp = new NetEConst(verinum(tmp->value(), wid)); - tmp->set_line(*this); - delete expr; - } - return tmp; - } - - NetESelect*tmp = new NetESelect(expr, 0, wid); - tmp->cast_signed(signed_flag_); - tmp->set_line(*this); - - return tmp; + return cast_to_width(expr, wid, signed_flag_, *this); } /* @@ -1606,10 +1583,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, if (missing_parms || parm_errors) return 0; - NetExpr*tmp = pad_to_width(fun, expr_wid, *this); - tmp->cast_signed(signed_flag_); - - return tmp; + return pad_to_width(fun, expr_wid, signed_flag_, *this); } NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope, @@ -1666,10 +1640,7 @@ NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope, NetExpr*tmp = new NetEAccess(branch, nature); tmp->set_line(*this); - tmp = pad_to_width(tmp, expr_wid, *this); - tmp->cast_signed(signed_flag_); - - return tmp; + return pad_to_width(tmp, expr_wid, signed_flag_, *this); } /* @@ -2281,10 +2252,7 @@ NetExpr* PECallFunction::elaborate_base_(Design*des, NetScope*scope, NetScope*ds if(res->darray_type()) return func; - NetExpr*tmp = pad_to_width(func, expr_wid, *this); - tmp->cast_signed(signed_flag_); - - return tmp; + return pad_to_width(func, expr_wid, signed_flag_, *this); } cerr << get_fileline() << ": internal error: Unable to locate " @@ -2521,22 +2489,54 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, unsigned PECastSize::test_width(Design*des, NetScope*scope, width_mode_t&) { - expr_width_ = size_; + ivl_assert(*this, size_); + ivl_assert(*this, base_); + + NetExpr*size_ex = elab_and_eval(des, scope, size_, -1, true); + NetEConst*size_ce = dynamic_cast(size_ex); + expr_width_ = size_ce ? size_ce->value().as_ulong() : 0; + delete size_ex; + if (expr_width_ == 0) { + cerr << get_fileline() << ": error: Cast size expression " + "must be constant and greater than zero." << endl; + des->errors += 1; + return 0; + } width_mode_t tmp_mode = PExpr::SIZED; base_->test_width(des, scope, tmp_mode); - return size_; + if (!type_is_vectorable(base_->expr_type())) { + cerr << get_fileline() << ": error: Cast base expression " + "must be a vector type." << endl; + des->errors += 1; + return 0; + } + + expr_type_ = base_->expr_type(); + min_width_ = expr_width_; + signed_flag_ = base_->has_sign(); + + return expr_width_; } NetExpr* PECastSize::elaborate_expr(Design*des, NetScope*scope, - unsigned, unsigned) const + unsigned expr_wid, unsigned flags) const { - NetExpr*sub = base_->elaborate_expr(des, scope, base_->expr_width(), NO_FLAGS); - NetESelect*sel = new NetESelect(sub, 0, size_); - sel->set_line(*this); + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag - return sel; + ivl_assert(*this, size_); + ivl_assert(*this, base_); + + NetExpr*sub = base_->elaborate_expr(des, scope, base_->expr_width(), flags); + + // Perform the cast. The extension method (zero/sign), if needed, + // depends on the type of the base expression. + NetExpr*tmp = cast_to_width(sub, expr_width_, base_->has_sign(), *this); + + // Pad up to the expression width. The extension method (zero/sign) + // depends on the type of enclosing expression. + return pad_to_width(tmp, expr_wid, signed_flag_, *this); } unsigned PECastType::test_width(Design*des, NetScope*scope, width_mode_t&wid) @@ -2851,8 +2851,7 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, return 0; } - NetExpr*tmp = pad_to_width(concat, expr_wid, *this); - tmp->cast_signed(signed_flag_); + NetExpr*tmp = pad_to_width(concat, expr_wid, signed_flag_, *this); concat_depth -= 1; return tmp; @@ -3769,10 +3768,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, if (!tmp) return 0; - tmp = pad_to_width(tmp, expr_wid, *this); - tmp->cast_signed(signed_flag_); - - return tmp; + return pad_to_width(tmp, expr_wid, signed_flag_, *this); } // If the identifier names a signal (a register or wire) @@ -3808,10 +3804,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, << ", tmp=" << *tmp << endl; } - tmp = pad_to_width(tmp, expr_wid, *this); - tmp->cast_signed(signed_flag_); - - return tmp; + return pad_to_width(tmp, expr_wid, signed_flag_, *this); } // If the identifier is a named event @@ -3911,9 +3904,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, member_comp); if (!tmp) return 0; - tmp = pad_to_width(tmp, expr_wid, *this); - tmp->cast_signed(signed_flag_); - return tmp; + return pad_to_width(tmp, expr_wid, signed_flag_, *this); } if (net->class_type() != 0) { @@ -4492,7 +4483,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, << "Elaborate parameter <" << path_ << "> as enumeration constant." << *etmp << endl; tmp = etmp->dup_expr(); - tmp = pad_to_width(tmp, expr_wid, *this); + tmp = pad_to_width(tmp, expr_wid, signed_flag_, *this); } else { perm_string name = peek_tail_name(path_); @@ -6082,7 +6073,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, } tmp->set_line(*this); } - tmp = pad_to_width(tmp, expr_wid, *this); + tmp = pad_to_width(tmp, expr_wid, signed_flag_, *this); break; case '&': // Reduction AND @@ -6100,7 +6091,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, } tmp = new NetEUReduce(op_, ip); tmp->set_line(*this); - tmp = pad_to_width(tmp, expr_wid, *this); + tmp = pad_to_width(tmp, expr_wid, signed_flag_, *this); break; case '~': diff --git a/elab_lval.cc b/elab_lval.cc index ab277685f..eec2aabd1 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -138,6 +138,9 @@ NetAssign_* PEConcat::elaborate_lval(Design*des, continue; } + /* A concatenation is always unsigned. */ + tmp->set_signed(false); + /* Link the new l-value to the previous one. */ NetAssign_*last = tmp; while (last->more) @@ -287,13 +290,13 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, } // We are processing the tail of a string of names. For - // example, the verilog may be "a.b.c", so we are processing + // example, the Verilog may be "a.b.c", so we are processing // "c" at this point. (Note that if method_name is not nil, // then this is "a.b.c.method" and "a.b.c" is a struct or class.) const name_component_t&name_tail = path_.back(); // Use the last index to determine what kind of select - // (bit/part/etc) we are processing. For example, the verilog + // (bit/part/etc) we are processing. For example, the Verilog // may be "a.b.c[1][2][]". All but the last index must // be simple expressions, only the may be a part // select etc., so look at it to determine how we will be @@ -399,6 +402,7 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, /* No select expressions. */ NetAssign_*lv = new NetAssign_(reg); + lv->set_signed(reg->get_signed()); return lv; } @@ -687,20 +691,24 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, unsigned long lwid; bool rcl = reg->sb_to_slice(prefix_indices, lsb, loff, lwid); ivl_assert(*this, rcl); - cerr << get_fileline() << ": warning: L-value packed array " - << "select of " << reg->name(); - if (reg->unpacked_dimensions() > 0) cerr << "[]"; - cerr << " has an undefined index." << endl; - + if (warn_ob_select) { + cerr << get_fileline() + << ": warning: L-value packed array select of " + << reg->name(); + if (reg->unpacked_dimensions() > 0) cerr << "[]"; + cerr << " has an undefined index." << endl; + } lv->set_part(new NetEConst(verinum(verinum::Vx)), lwid); return true; // The index is undefined and this is a bit select. } else { - cerr << get_fileline() << ": warning: L-value bit select of " - << reg->name(); - if (reg->unpacked_dimensions() > 0) cerr << "[]"; - cerr << " has an undefined index." << endl; - + if (warn_ob_select) { + cerr << get_fileline() + << ": warning: L-value bit select of " + << reg->name(); + if (reg->unpacked_dimensions() > 0) cerr << "[]"; + cerr << " has an undefined index." << endl; + } lv->set_part(new NetEConst(verinum(verinum::Vx)), 1); return true; } @@ -797,12 +805,10 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, // Constant bit select that does something useful. long loff = reg->sb_to_idx(prefix_indices,lsb); - if (loff < 0 || loff >= (long)reg->vector_width()) { - cerr << get_fileline() << ": error: bit select " + if (warn_ob_select && (loff < 0 || loff >= (long)reg->vector_width())) { + cerr << get_fileline() << ": warning: bit select " << reg->name() << "[" <errors += 1; - return 0; } if (reg->type()==NetNet::UNRESOLVED_WIRE) { @@ -868,10 +874,13 @@ bool PEIdent::elaborate_lval_net_part_(Design*des, ivl_assert(*this, reg); if (! parts_defined_flag) { - cerr << get_fileline() << ": warning: L-value part select of " - << reg->name(); - if (reg->unpacked_dimensions() > 0) cerr << "[]"; - cerr << " has an undefined index." << endl; + if (warn_ob_select) { + cerr << get_fileline() + << ": warning: L-value part select of " + << reg->name(); + if (reg->unpacked_dimensions() > 0) cerr << "[]"; + cerr << " has an undefined index." << endl; + } // Use a width of two here so we can distinguish between an // undefined bit or part select. lv->set_part(new NetEConst(verinum(verinum::Vx)), 2); @@ -934,11 +943,11 @@ bool PEIdent::elaborate_lval_net_part_(Design*des, } /* If the part select extends beyond the extremes of the - variable, then report an error. Note that loff is + variable, then output a warning. Note that loff is converted to normalized form so is relative the variable pins. */ - if (loff < 0 || moff >= (long)reg->vector_width()) { + if (warn_ob_select && (loff < 0 || moff >= (long)reg->vector_width())) { cerr << get_fileline() << ": warning: Part select " << reg->name() << "[" << msb<<":"<sb_to_idx(prefix_indices, lsb); - long midx_tmp = sig->sb_to_idx(prefix_indices, msb); - /* Detect reversed indices of a part select. */ - if (lidx_tmp > midx_tmp) { - cerr << get_fileline() << ": error: Part select " - << sig->name() << "[" << msb << ":" - << lsb << "] indices reversed." << endl; - cerr << get_fileline() << ": : Did you mean " - << sig->name() << "[" << lsb << ":" - << msb << "]?" << endl; - long tmp = midx_tmp; - midx_tmp = lidx_tmp; - lidx_tmp = tmp; - des->errors += 1; - } + if (prefix_indices.size()+1 < sig->packed_dims().size()) { + // Here we have a slice that doesn't have enough indices + // to get to a single slice. For example: + // wire [9:0][5:1] foo + // ... foo[4:3] ... + // Make this work by finding the indexed slices and + // creating a generated slice that spans the whole + // range. + long loff, moff; + unsigned long lwid, mwid; + bool lrc; + lrc = sig->sb_to_slice(prefix_indices, lsb, loff, lwid); + ivl_assert(*this, lrc); + lrc = sig->sb_to_slice(prefix_indices, msb, moff, mwid); + ivl_assert(*this, lrc); + ivl_assert(*this, lwid == mwid); - /* Warn about a part select that is out of range. */ - if (midx_tmp >= (long)sig->vector_width() || lidx_tmp < 0) { - cerr << get_fileline() << ": warning: Part select " - << sig->name(); - if (sig->unpacked_dimensions() > 0) { - cerr << "[]"; + if (moff > loff) { + lidx = loff; + midx = moff + mwid - 1; + } else { + lidx = moff; + midx = loff + lwid - 1; } - cerr << "[" << msb << ":" << lsb - << "] is out of range." << endl; - } - /* This is completely out side the signal so just skip it. */ - if (lidx_tmp >= (long)sig->vector_width() || midx_tmp < 0) { - return false; - } + } else { + long lidx_tmp = sig->sb_to_idx(prefix_indices, lsb); + long midx_tmp = sig->sb_to_idx(prefix_indices, msb); - midx = midx_tmp; - lidx = lidx_tmp; + /* Detect reversed indices of a part select. */ + if (lidx_tmp > midx_tmp) { + cerr << get_fileline() << ": error: Part select " + << sig->name() << "[" << msb << ":" + << lsb << "] indices reversed." << endl; + cerr << get_fileline() << ": : Did you mean " + << sig->name() << "[" << lsb << ":" + << msb << "]?" << endl; + long tmp = midx_tmp; + midx_tmp = lidx_tmp; + lidx_tmp = tmp; + des->errors += 1; + } + + /* Warn about a part select that is out of range. */ + if (midx_tmp >= (long)sig->vector_width() || lidx_tmp < 0) { + cerr << get_fileline() << ": warning: Part select " + << sig->name(); + if (sig->unpacked_dimensions() > 0) { + cerr << "[]"; + } + cerr << "[" << msb << ":" << lsb + << "] is out of range." << endl; + } + /* This is completely out side the signal so just skip it. */ + if (lidx_tmp >= (long)sig->vector_width() || midx_tmp < 0) { + return false; + } + + midx = midx_tmp; + lidx = lidx_tmp; + } break; } diff --git a/elab_scope.cc b/elab_scope.cc index 6f37e63d7..73834249c 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -54,6 +54,15 @@ # include # include "ivl_assert.h" + +void set_scope_timescale(Design*des, NetScope*scope, PScope*pscope) +{ + scope->time_unit(pscope->time_unit); + scope->time_precision(pscope->time_precision); + scope->time_from_timescale(pscope->time_from_timescale); + des->set_precision(pscope->time_precision); +} + typedef map::const_iterator mparm_it_t; static void collect_parm_item_(Design*des, NetScope*scope, perm_string name, @@ -523,6 +532,7 @@ static void elaborate_scope_class(Design*des, NetScope*scope, PClass*pclass) class_scope->set_class_def(use_class); use_class->set_class_scope(class_scope); use_class->set_definition_scope(scope); + set_scope_timescale(des, class_scope, pclass); // Collect the properties, elaborate them, and add them to the // elaborated class definition. @@ -654,8 +664,10 @@ static void elaborate_scope_task(Design*des, NetScope*scope, PTask*task) task_scope->is_auto(task->is_auto()); task_scope->set_line(task); - if (scope==0) + if (scope==0) { + set_scope_timescale(des, task_scope, task); des->add_root_task(task_scope, task); + } if (debug_scopes) { cerr << task->get_fileline() << ": elaborate_scope_task: " @@ -719,8 +731,10 @@ static void elaborate_scope_func(Design*des, NetScope*scope, PFunction*task) task_scope->is_auto(task->is_auto()); task_scope->set_line(task); - if (scope==0) + if (scope==0) { + set_scope_timescale(des, task_scope, task); des->add_root_task(task_scope, task); + } if (debug_scopes) { cerr << task->get_fileline() << ": elaborate_scope_func: " @@ -1750,11 +1764,7 @@ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*s instances[idx] = my_scope; - // Set time units and precision. - my_scope->time_unit(mod->time_unit); - my_scope->time_precision(mod->time_precision); - my_scope->time_from_timescale(mod->time_from_timescale); - des->set_precision(mod->time_precision); + set_scope_timescale(des, my_scope, mod); // Look for module parameter replacements. The "replace" map // maps parameter name to replacement expression that is diff --git a/elab_sig.cc b/elab_sig.cc index 3ace3cb72..e92ad6667 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2012 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -1185,6 +1185,14 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const NetLogic*pull = 0; if (wtype == NetNet::SUPPLY0 || wtype == NetNet::SUPPLY1) { + if (debug_elaborate) { + cerr << get_fileline() << ": debug: " + << "Generate a SUPPLY pull for the "; + if (wtype == NetNet::SUPPLY0) cerr << "supply0"; + else cerr << "supply1"; + cerr << " net." << endl; + } + NetLogic::TYPE pull_type = (wtype==NetNet::SUPPLY1) ? NetLogic::PULLUP : NetLogic::PULLDOWN; @@ -1195,14 +1203,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const pull->pin(0).drive1(IVL_DR_SUPPLY); des->add_node(pull); wtype = NetNet::WIRE; - - if (debug_elaborate) { - cerr << get_fileline() << ": debug: " - << "Generate a SUPPLY pull for the "; - if (wtype == NetNet::SUPPLY0) cerr << "supply0"; - else cerr << "supply1"; - cerr << " net." << endl; - } } diff --git a/elab_type.cc b/elab_type.cc index eb2803cbf..4c1814909 100644 --- a/elab_type.cc +++ b/elab_type.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2016 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 @@ -17,11 +17,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +# include "PExpr.h" # include "pform_types.h" # include "netlist.h" # include "netclass.h" # include "netdarray.h" # include "netenum.h" +# include "netqueue.h" # include "netparray.h" # include "netscalar.h" # include "netstruct.h" @@ -245,6 +247,16 @@ ivl_type_s* uarray_type_t::elaborate_type_raw(Design*des, NetScope*scope) const return res; } + // Special case: if the dimension is null:nil. this is a queue. + if (cur->second==0 && dynamic_cast(cur->first)) { + cerr << get_fileline() << ": sorry: " + << "SV queues inside classes are not yet supported." << endl; + des->errors += 1; + + ivl_type_s*res = new netqueue_t(btype); + return res; + } + vector dimensions; bool bad_range = evaluate_ranges(des, scope, dimensions, *dims); diff --git a/elaborate.cc b/elaborate.cc index 376c099d1..f4f99f85f 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -36,6 +36,7 @@ # include "PEvent.h" # include "PGenerate.h" # include "PPackage.h" +# include "PScope.h" # include "PSpec.h" # include "netlist.h" # include "netenum.h" @@ -50,6 +51,9 @@ # include "ivl_assert.h" +// Implemented in elab_scope.cc +extern void set_scope_timescale(Design*des, NetScope*scope, PScope*pscope); + void PGate::elaborate(Design*, NetScope*) const { cerr << "internal error: what kind of gate? " << @@ -787,7 +791,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const if (check_delay_count(des)) return; NetExpr* rise_time, *fall_time, *decay_time; - eval_delays(des, scope, rise_time, fall_time, decay_time); + eval_delays(des, scope, rise_time, fall_time, decay_time, true); struct attrib_list_t*attrib_list; unsigned attrib_list_n = 0; @@ -1421,6 +1425,16 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const << prts[0]->name() << " is coerced to inout." << endl; } + if (!prts.empty() && (prts[0]->port_type() == NetNet::POUTPUT) + && (prts[0]->type() != NetNet::REG) + && prts[0]->pin(0).nexus()->has_floating_input() + && pins[idx]->is_collapsible_net(des, scope)) { + prts[0]->port_type(NetNet::PINOUT); + + cerr << pins[idx]->get_fileline() << ": warning: output port " + << prts[0]->name() << " is coerced to inout." << endl; + } + // Elaborate the expression that connects to the // module[s] port. sig is the thing outside the module // that connects to the port. @@ -2000,7 +2014,7 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const PDelays tmp_del; tmp_del.set_delays(overrides_, false); tmp_del.eval_delays(des, scope, rise_expr, fall_expr, - decay_expr); + decay_expr, true); } } @@ -2325,7 +2339,8 @@ NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, ivl_type_t lv_net_type, ivl_variable_type_t lv_type, - unsigned lv_width) const + unsigned lv_width, + bool force_unsigned) const { ivl_assert(*this, rval_); @@ -2334,7 +2349,7 @@ NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, // should look into fixing calls to this method to pass a // net_type instead of the separate lv_width/lv_type values. NetExpr*rv = elaborate_rval_expr(des, scope, lv_net_type, lv_type, lv_width, - rval(), is_constant_); + rval(), is_constant_, force_unsigned); if (!is_constant_ || !rv) return rv; @@ -2452,10 +2467,35 @@ NetProc* PAssign::elaborate_compressed_(Design*des, NetScope*scope) const NetAssign_*lv = elaborate_lval(des, scope); if (lv == 0) return 0; - NetExpr*rv = elaborate_rval_(des, scope, 0, lv->expr_type(), count_lval_width(lv)); + // Compressed assignments should behave identically to the + // equivalent uncompressed assignments. This means we need + // to take the type of the LHS into account when determining + // the type of the RHS expression. + bool force_unsigned; + switch (op_) { + case 'l': + case 'r': + case 'R': + // The right-hand operand of shift operations is + // self-determined. + force_unsigned = false; + break; + default: + force_unsigned = !lv->get_signed(); + break; + } + NetExpr*rv = elaborate_rval_(des, scope, 0, lv->expr_type(), + count_lval_width(lv), force_unsigned); if (rv == 0) return 0; - NetAssign*cur = new NetAssign(lv, op_, rv); + // The ivl_target API doesn't support signalling the type + // of a lval, so convert arithmetic shifts into logical + // shifts now if the lval is unsigned. + char op = op_; + if ((op == 'R') && !lv->get_signed()) + op = 'r'; + + NetAssign*cur = new NetAssign(lv, op, rv); cur->set_line(*this); return cur; @@ -2868,14 +2908,27 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const des->errors += 1; return 0; } - assert(nscope); - - elaborate_behaviors_(des, nscope); } NetBlock*cur = new NetBlock(type, nscope); + if (nscope) { + // Handle any variable initialization statements in this scope. + // For automatic scopes these statements need to be executed + // each time the block is entered, so add them to the main + // block. For static scopes, put them in a separate process + // that will be executed at the start of simulation. + if (nscope->is_auto()) { + for (unsigned idx = 0; idx < var_inits.size(); idx += 1) { + NetProc*tmp = var_inits[idx]->elaborate(des, nscope); + if (tmp) cur->append(tmp); + } + } else { + elaborate_var_inits_(des, nscope); + } + } + if (nscope == 0) nscope = scope; @@ -3778,7 +3831,9 @@ NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope, expression that can be a target to a procedural assignment, including a memory word. */ - for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { + for (unsigned idx = use_this?1:0 ; idx < parm_count ; idx += 1) { + + size_t parms_idx = use_this? idx-1 : idx; NetNet*port = def->port(idx); @@ -3794,12 +3849,12 @@ NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope, message. Note that the elaborate_lval method already printed a detailed message for the latter case. */ NetAssign_*lv = 0; - if (idx < parms_.size() && parms_[idx]) { - lv = parms_[idx]->elaborate_lval(des, scope, false, false); + if (parms_idx < parms_.size() && parms_[parms_idx]) { + lv = parms_[parms_idx]->elaborate_lval(des, scope, false, false); if (lv == 0) { - cerr << parms_[idx]->get_fileline() << ": error: " + cerr << parms_[parms_idx]->get_fileline() << ": error: " << "I give up on task port " << (idx+1) - << " expression: " << *parms_[idx] << endl; + << " expression: " << *parms_[parms_idx] << endl; } } else if (port->port_type() == NetNet::POUTPUT) { // Output ports were skipped earlier, so @@ -4997,7 +5052,6 @@ void PFunction::elaborate(Design*des, NetScope*scope) const des->errors += 1; return; } - assert(def); ivl_assert(*this, statement_); @@ -5010,6 +5064,31 @@ void PFunction::elaborate(Design*des, NetScope*scope) const return; } + // Handle any variable initialization statements in this scope. + // For automatic functions, these statements need to be executed + // each time the function is called, so insert them at the start + // of the elaborated definition. For static functions, put them + // in a separate process that will be executed before the start + // of simulation. + if (is_auto_) { + // Get the NetBlock of the statement. If it is not a + // NetBlock then create one to wrap the initialization + // statements and the original statement. + NetBlock*blk = dynamic_cast (st); + if ((blk == 0) && (var_inits.size() > 0)) { + blk = new NetBlock(NetBlock::SEQU, scope); + blk->set_line(*this); + blk->append(st); + st = blk; + } + for (unsigned idx = var_inits.size(); idx > 0; idx -= 1) { + NetProc*tmp = var_inits[idx-1]->elaborate(des, scope); + if (tmp) blk->prepend(tmp); + } + } else { + elaborate_var_inits_(des, scope); + } + def->set_proc(st); } @@ -5172,11 +5251,6 @@ NetProc* PReturn::elaborate(Design*des, NetScope*scope) const void PTask::elaborate(Design*des, NetScope*task) const { - // Elaborate any processes that are part of this scope that - // aren't the definition itself. This can happen, for example, - // with variable initialization statements in this scope. - elaborate_behaviors_(des, task); - NetTaskDef*def = task->task_def(); assert(def); @@ -5195,6 +5269,31 @@ void PTask::elaborate(Design*des, NetScope*task) const } } + // Handle any variable initialization statements in this scope. + // For automatic tasks , these statements need to be executed + // each time the task is called, so insert them at the start + // of the elaborated definition. For static tasks, put them + // in a separate process that will be executed before the start + // of simulation. + if (is_auto_) { + // Get the NetBlock of the statement. If it is not a + // NetBlock then create one to wrap the initialization + // statements and the original statement. + NetBlock*blk = dynamic_cast (st); + if ((blk == 0) && (var_inits.size() > 0)) { + blk = new NetBlock(NetBlock::SEQU, task); + blk->set_line(*this); + blk->append(st); + st = blk; + } + for (unsigned idx = var_inits.size(); idx > 0; idx -= 1) { + NetProc*tmp = var_inits[idx-1]->elaborate(des, task); + if (tmp) blk->prepend(tmp); + } + } else { + elaborate_var_inits_(des, task); + } + def->set_proc(st); } @@ -5651,6 +5750,10 @@ bool Module::elaborate(Design*des, NetScope*scope) const (*gt)->elaborate(des, scope); } + // Elaborate the variable initialization statements, making a + // single initial process out of them. + result_flag &= elaborate_var_inits_(des, scope); + // Elaborate the behaviors, making processes out of them. This // involves scanning the PProcess* list, creating a NetProcTop // for each process. @@ -5841,6 +5944,8 @@ bool PGenerate::elaborate_(Design*des, NetScope*scope) const for (gates_it_t cur = gates.begin() ; cur != gates.end() ; ++ cur ) (*cur)->elaborate(des, scope); + elaborate_var_inits_(des, scope); + typedef list::const_iterator proc_it_t; for (proc_it_t cur = behaviors.begin(); cur != behaviors.end(); ++ cur ) (*cur)->elaborate(des, scope); @@ -5876,6 +5981,44 @@ bool PScope::elaborate_behaviors_(Design*des, NetScope*scope) const return result_flag; } +bool LexicalScope::elaborate_var_inits_(Design*des, NetScope*scope) const +{ + if (var_inits.size() == 0) + return true; + + NetProc*proc = 0; + if (var_inits.size() == 1) { + proc = var_inits[0]->elaborate(des, scope); + } else { + NetBlock*blk = new NetBlock(NetBlock::SEQU, 0); + bool flag = true; + for (unsigned idx = 0; idx < var_inits.size(); idx += 1) { + NetProc*tmp = var_inits[idx]->elaborate(des, scope); + if (tmp) + blk->append(tmp); + else + flag = false; + } + if (flag) proc = blk; + } + if (proc == 0) + return false; + + NetProcTop*top = new NetProcTop(scope, IVL_PR_INITIAL, proc); + if (const LineInfo*li = dynamic_cast(this)) { + top->set_line(*li); + } + if (gn_system_verilog()) { + top->attribute(perm_string::literal("_ivl_schedule_init"), + verinum(1)); + } + des->add_process(top); + + scope->set_var_init(proc); + + return true; +} + class elaborate_package_t : public elaborator_work_item_t { public: elaborate_package_t(Design*d, NetScope*scope, PPackage*p) @@ -6109,6 +6252,7 @@ Design* elaborate(listroots) ivl_assert(*pac->second, pac->first == pac->second->pscope_name()); NetScope*scope = des->make_package_scope(pac->first); scope->set_line(pac->second); + set_scope_timescale(des, scope, pac->second); elaborator_work_item_t*es = new elaborate_package_t(des, scope, pac->second); des->elaboration_work_list.push_back(es); @@ -6145,11 +6289,7 @@ Design* elaborate(listroots) // Collect some basic properties of this scope from the // Module definition. scope->set_line(rmod); - scope->time_unit(rmod->time_unit); - scope->time_precision(rmod->time_precision); - scope->time_from_timescale(rmod->time_from_timescale); - des->set_precision(rmod->time_precision); - + set_scope_timescale(des, scope, rmod); // Save this scope, along with its definition, in the // "root_elems" list for later passes. diff --git a/emit.cc b/emit.cc index bd46fc5c4..7f86b6f9b 100644 --- a/emit.cc +++ b/emit.cc @@ -507,7 +507,6 @@ int Design::emit(struct target_t*tgt) const for (map::const_iterator scope = root_tasks_.begin() ; scope != root_tasks_.end() ; ++ scope) { scope->first->emit_scope(tgt); - scope->first->emit_defs(tgt); } // enumerate package scopes @@ -521,7 +520,6 @@ int Design::emit(struct target_t*tgt) const const NetScope*use_scope = cur->second->class_scope(); cur->second->emit_scope(tgt); tgt->class_type(use_scope, cur->second); - cur->second->emit_defs(tgt); } // enumerate root scopes @@ -548,6 +546,12 @@ int Design::emit(struct target_t*tgt) const // emit task and function definitions bool tasks_rc = true; + for (map::const_iterator scope = root_tasks_.begin() + ; scope != root_tasks_.end() ; ++ scope) + tasks_rc &= scope->first->emit_defs(tgt); + for (map::const_iterator cur = classes_.begin() + ; cur != classes_.end() ; ++cur) + tasks_rc &= cur->second->emit_defs(tgt); for (map::const_iterator scope = packages_.begin() ; scope != packages_.end() ; ++ scope ) tasks_rc &= scope->second->emit_defs(tgt); diff --git a/ivlpp/lexor.lex b/ivlpp/lexor.lex index b22fac4cb..c81af7397 100644 --- a/ivlpp/lexor.lex +++ b/ivlpp/lexor.lex @@ -1,7 +1,7 @@ %option prefix="yy" %{ /* - * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 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 @@ -148,18 +148,38 @@ static void ifdef_leave(void) free(cur); } -#define YY_INPUT(buf,result,max_size) do { \ - if (istack->file) { \ - size_t rc = fread(buf, 1, max_size, istack->file); \ - result = (rc == 0) ? YY_NULL : rc; \ - } else { \ - if (*istack->str == 0) \ - result = YY_NULL; \ - else { \ - buf[0] = *istack->str++; \ - result = 1; \ - } \ - } \ +#define YY_INPUT(buf,result,max_size) do { \ + if (istack->file) { \ + size_t rc = fread(buf, 1, max_size, istack->file); \ + result = (rc == 0) ? YY_NULL : rc; \ + } else { \ + /* We are expanding a macro. Handle the SV macro escape \ + sequences. There doesn't seem to be any good reason \ + not to allow them in traditional Verilog as well. */ \ + while ((istack->str[0] == '`') && \ + (istack->str[1] == '`')) { \ + istack->str += 2; \ + } \ + if (*istack->str == 0) { \ + result = YY_NULL; \ + } else if ((istack->str[0] == '`') && \ + (istack->str[1] == '"')) { \ + istack->str += 2; \ + buf[0] = '"'; \ + result = 1; \ + } else if ((istack->str[0] == '`') && \ + (istack->str[1] == '\\')&& \ + (istack->str[2] == '`') && \ + (istack->str[3] == '"')) { \ + istack->str += 4; \ + buf[0] = '\\'; \ + buf[1] = '"'; \ + result = 2; \ + } else { \ + buf[0] = *istack->str++; \ + result = 1; \ + } \ + } \ } while (0) static int comment_enter = 0; @@ -242,12 +262,6 @@ keywords (include|define|undef|ifdef|ifndef|else|elseif|endif) if (macro_needs_args(yytext+1)) yy_push_state(MA_START); else do_expand(0); } - /* `" overrides the usual lexical meaning of " and `\`" indicates - that the expansion should include the escape sequence \". - */ -`\" { fputc('"', yyout); } -`\\`\" { fprintf(yyout, "\\\""); } - /* Strings do not contain preprocessor directives, but can expand * macros. If that happens, they get expanded in the context of the * string. @@ -514,29 +528,19 @@ keywords (include|define|undef|ifdef|ifndef|else|elseif|endif) do_expand(0); } - /* Stringified version of macro expansion. If the sequence `` is - * encountered inside a macro definition, we use the SystemVerilog - * handling of ignoring it so that identifiers can be constructed - * from arguments. If istack->file is NULL, we are reading text - * produced from a macro, so use SystemVerilog's handling; - * otherwise, use the special Icarus handling. - */ + /* Stringified version of macro expansion. This is an Icarus extension. + When expanding macro text, the SV usage of `` takes precedence. */ ``[a-zA-Z_][a-zA-Z0-9_$]* { - if (istack->file == NULL) - fprintf(yyout, "%s", yytext+2); - else { - assert(do_expand_stringify_flag == 0); - do_expand_stringify_flag = 1; - fputc('"', yyout); - if (macro_needs_args(yytext+2)) - yy_push_state(MA_START); - else - do_expand(0); - } + assert(istack->file); + assert(do_expand_stringify_flag == 0); + do_expand_stringify_flag = 1; + fputc('"', yyout); + if (macro_needs_args(yytext+2)) + yy_push_state(MA_START); + else + do_expand(0); } -`` { if (istack->file != NULL) ECHO; } - \( { BEGIN(MA_ADD); macro_start_args(); } {W} {} diff --git a/lexor.lex b/lexor.lex index a153000ad..15591d402 100644 --- a/lexor.lex +++ b/lexor.lex @@ -453,7 +453,7 @@ TU [munpf] return BASED_NUMBER; } \'[01xzXZ] { - if (generation_flag < GN_VER2005_SV) { + if (!gn_system_verilog()) { cerr << yylloc.text << ":" << yylloc.first_line << ": warning: " << "Using SystemVerilog 'N bit vector. Use at least " << "-g2005-sv to remove this warning." << endl; @@ -479,7 +479,7 @@ TU [munpf] /* This rule handles scaled time values for SystemVerilog. */ [0-9][0-9_]*(\.[0-9][0-9_]*)?{TU}?s { - if(generation_flag & (GN_VER2005_SV | GN_VER2009 | GN_VER2012)) { + if (gn_system_verilog()) { yylval.text = strdupnew(yytext); return TIME_LITERAL; } else REJECT; } @@ -857,7 +857,7 @@ verinum*make_unsized_binary(const char*txt) ptr += 1; } - assert((tolower(*ptr) == 'b') || (generation_flag >= GN_VER2005_SV)); + assert((tolower(*ptr) == 'b') || gn_system_verilog()); if (tolower(*ptr) == 'b') { ptr += 1; } else { diff --git a/main.cc b/main.cc index f0a8dbf17..9e54f5c50 100644 --- a/main.cc +++ b/main.cc @@ -108,6 +108,7 @@ bool gn_assertions_flag = true; bool gn_io_range_error_flag = true; bool gn_strict_ca_eval_flag = false; bool gn_strict_expr_width_flag = false; +bool gn_shared_loop_index_flag = false; bool gn_verilog_ams_flag = false; /* @@ -344,6 +345,12 @@ static void process_generation_flag(const char*gen) } else if (strcmp(gen,"no-strict-expr-width") == 0) { gn_strict_expr_width_flag = false; + } else if (strcmp(gen,"shared-loop-index") == 0) { + gn_shared_loop_index_flag = true; + + } else if (strcmp(gen,"no-shared-loop-index") == 0) { + gn_shared_loop_index_flag = false; + } else { } } @@ -1111,7 +1118,7 @@ int main(int argc, char*argv[]) /* Decide if we are going to allow system functions to be called * as tasks. */ - if (generation_flag >= GN_VER2005_SV) { + if (gn_system_verilog()) { def_sfunc_as_task = IVL_SFUNC_AS_TASK_WARNING; } diff --git a/net_assign.cc b/net_assign.cc index 1467ee961..fa5c1cce7 100644 --- a/net_assign.cc +++ b/net_assign.cc @@ -43,6 +43,8 @@ NetAssign_::NetAssign_(NetAssign_*n) : nest_(n), sig_(0), word_(0), base_(0), sel_type_(IVL_SEL_OTHER) { more = 0; + signed_ = false; + turn_sig_to_wire_on_release_ = false; } NetAssign_::NetAssign_(NetNet*s) @@ -51,6 +53,7 @@ NetAssign_::NetAssign_(NetNet*s) lwid_ = sig_->vector_width(); sig_->incr_lref(); more = 0; + signed_ = false; turn_sig_to_wire_on_release_ = false; } diff --git a/net_func_eval.cc b/net_func_eval.cc index ecbe55780..00790d692 100644 --- a/net_func_eval.cc +++ b/net_func_eval.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2016 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 @@ -88,6 +88,10 @@ NetExpr* NetFuncDef::evaluate_function(const LineInfo&loc, const std::vectorevaluate_function_find_locals(loc, context_map); + // Execute any variable initialization statements. + if (const NetProc*init_proc = scope()->var_init()) + init_proc->evaluate_function(loc, context_map); + if (debug_eval_tree && proc_==0) { cerr << loc.get_fileline() << ": NetFuncDef::evaluate_function: " << "Function " << scope_path(scope()) @@ -209,6 +213,98 @@ bool NetProc::evaluate_function(const LineInfo&, return false; } +void NetAssign::eval_func_lval_op_real_(const LineInfo&loc, + verireal&lv, verireal&rv) const +{ + switch (op_) { + case '+': + lv = lv + rv; + break; + case '-': + lv = lv - rv; + break; + case '*': + lv = lv * rv; + break; + case '/': + lv = lv / rv; + break; + case '%': + lv = lv % rv; + break; + default: + cerr << "Illegal assignment operator: " + << human_readable_op(op_) << endl; + ivl_assert(loc, 0); + } +} + +void NetAssign::eval_func_lval_op_(const LineInfo&loc, + verinum&lv, verinum&rv) const +{ + unsigned lv_width = lv.len(); + bool lv_sign = lv.has_sign(); + switch (op_) { + case 'l': + case 'R': + // The left operand is self-determined. + break; + case 'r': + // The left operand is self-determined, but we need to + // cast it to unsigned to get a logical shift. + lv.has_sign(false); + break; + default: + // The left operand must be cast to the expression type/size + lv.has_sign(rv.has_sign()); + lv = cast_to_width(lv, rv.len()); + } + switch (op_) { + case '+': + lv = lv + rv; + break; + case '-': + lv = lv - rv; + break; + case '*': + lv = lv * rv; + break; + case '/': + lv = lv / rv; + break; + case '%': + lv = lv % rv; + break; + case '&': + for (unsigned idx = 0 ; idx < lv.len() ; idx += 1) + lv.set(idx, lv[idx] & rv[idx]); + break; + case '|': + for (unsigned idx = 0 ; idx < lv.len() ; idx += 1) + lv.set(idx, lv[idx] | rv[idx]); + break; + case '^': + for (unsigned idx = 0 ; idx < lv.len() ; idx += 1) + lv.set(idx, lv[idx] ^ rv[idx]); + break; + case 'l': + lv = lv << rv.as_unsigned(); + break; + case 'r': + lv = lv >> rv.as_unsigned(); + break; + case 'R': + lv = lv >> rv.as_unsigned(); + break; + default: + cerr << "Illegal assignment operator: " + << human_readable_op(op_) << endl; + ivl_assert(loc, 0); + } + lv = cast_to_width(lv, lv_width); + lv.has_sign(lv_sign); +} + bool NetAssign::eval_func_lval_(const LineInfo&loc, map&context_map, const NetAssign_*lval, NetExpr*rval_result) const @@ -269,18 +365,55 @@ bool NetAssign::eval_func_lval_(const LineInfo&loc, ivl_assert(loc, base + lval->lwidth() <= old_lval->expr_width()); NetEConst*lval_const = dynamic_cast(old_lval); + ivl_assert(loc, lval_const); verinum lval_v = lval_const->value(); NetEConst*rval_const = dynamic_cast(rval_result); - verinum rval_v = cast_to_width(rval_const->value(), lval->lwidth()); + ivl_assert(loc, rval_const); + verinum rval_v = rval_const->value(); - for (unsigned idx = 0 ; idx < rval_v.len() ; idx += 1) - lval_v.set(idx+base, rval_v[idx]); + verinum lpart(verinum::Vx, lval->lwidth()); + if (op_) { + for (unsigned idx = 0 ; idx < lpart.len() ; idx += 1) + lpart.set(idx, lval_v[base+idx]); + + eval_func_lval_op_(loc, lpart, rval_v); + } else { + lpart = cast_to_width(rval_v, lval->lwidth()); + } + for (unsigned idx = 0 ; idx < lpart.len() ; idx += 1) + lval_v.set(idx+base, lpart[idx]); delete base_result; delete rval_result; rval_result = new NetEConst(lval_v); } else { - rval_result = fix_assign_value(lval->sig(), rval_result); + if (op_ == 0) { + rval_result = fix_assign_value(lval->sig(), rval_result); + } else if (dynamic_cast(rval_result)) { + NetECReal*lval_const = dynamic_cast(old_lval); + ivl_assert(loc, lval_const); + verireal lval_r = lval_const->value(); + NetECReal*rval_const = dynamic_cast(rval_result); + ivl_assert(loc, rval_const); + verireal rval_r = rval_const->value(); + + eval_func_lval_op_real_(loc, lval_r, rval_r); + + delete rval_result; + rval_result = new NetECReal(lval_r); + } else { + NetEConst*lval_const = dynamic_cast(old_lval); + ivl_assert(loc, lval_const); + verinum lval_v = lval_const->value(); + NetEConst*rval_const = dynamic_cast(rval_result); + ivl_assert(loc, rval_const); + verinum rval_v = rval_const->value(); + + eval_func_lval_op_(loc, lval_v, rval_v); + + delete rval_result; + rval_result = new NetEConst(lval_v); + } } if (old_lval) @@ -318,6 +451,13 @@ bool NetAssign::evaluate_function(const LineInfo&loc, NetEConst*rval_const = dynamic_cast(rval_result); ivl_assert(*this, rval_const); + if (op_) { + cerr << get_fileline() << ": sorry: Assignment operators " + "inside a constant function are not currently " + "supported if the LHS is a concatenation." << endl; + return false; + } + verinum rval_full = rval_const->value(); delete rval_result; @@ -369,6 +509,10 @@ bool NetBlock::evaluate_function(const LineInfo&loc, // Now collect the new locals. subscope_->evaluate_function_find_locals(loc, local_context_map); use_local_context_map = true; + + // Execute any variable initialization statements. + if (const NetProc*init_proc = subscope_->var_init()) + init_proc->evaluate_function(loc, local_context_map); } // Now use the local context map if there is any local diff --git a/net_link.cc b/net_link.cc index 15c67e046..fcd202f10 100644 --- a/net_link.cc +++ b/net_link.cc @@ -311,6 +311,20 @@ void Nexus::count_io(unsigned&inp, unsigned&out) const } } +bool Nexus::has_floating_input() const +{ + bool found_input = false; + for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { + if (cur->get_dir() == Link::OUTPUT) + return false; + + if (cur->get_dir() == Link::INPUT) + found_input = true; + } + + return found_input; +} + bool Nexus::drivers_present() const { for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { diff --git a/net_nex_input.cc b/net_nex_input.cc index aff41f281..4cb91fc24 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2016 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 @@ -427,6 +427,15 @@ NexusSet* NetForLoop::nex_input(bool rem_out) result->add(*tmp); delete tmp; + if (gn_shared_loop_index_flag) { + tmp = new NexusSet(); + for (unsigned idx = 0 ; idx < index_->pin_count() ; idx += 1) + tmp->add(index_->pin(idx).nexus(), 0, index_->vector_width()); + + result->rem(*tmp); + delete tmp; + } + return result; } diff --git a/net_proc.cc b/net_proc.cc index 2aedec7c2..8b6902b68 100644 --- a/net_proc.cc +++ b/net_proc.cc @@ -56,6 +56,17 @@ void NetBlock::append(NetProc*cur) } } +void NetBlock::prepend(NetProc*cur) +{ + if (last_ == 0) { + last_ = cur; + cur->next_ = cur; + } else { + cur->next_ = last_->next_; + last_->next_ = cur; + } +} + const NetProc* NetBlock::proc_first() const { if (last_ == 0) diff --git a/net_scope.cc b/net_scope.cc index 688266aff..438ccd729 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -139,6 +139,7 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, bool nest, time_from_timescale_ = false; } + var_init_ = 0; switch (t) { case NetScope::TASK: task_ = 0; diff --git a/netlist.cc b/netlist.cc index 838e82ceb..bb162a021 100644 --- a/netlist.cc +++ b/netlist.cc @@ -557,7 +557,7 @@ void NetNet::calculate_slice_widths_from_packed_dims_(void) ivl_assert(*this, ! slice_wids_.empty()); slice_wids_[0] = netrange_width(slice_dims_); vector::const_iterator cur = slice_dims_.begin(); - for (size_t idx = 1 ; idx < slice_wids_.size() ; idx += 1) { + for (size_t idx = 1 ; idx < slice_wids_.size() ; idx += 1, cur++) { slice_wids_[idx] = slice_wids_[idx-1] / cur->width(); } } diff --git a/netlist.h b/netlist.h index a3e601165..054b87b66 100644 --- a/netlist.h +++ b/netlist.h @@ -388,6 +388,10 @@ class Nexus { is a variable, but also if this is a net with a force. */ bool assign_lval() const; + /* This method returns true if there are any inputs + attached to this nexus but no drivers. */ + bool has_floating_input() const; + /* This method returns true if there are any drivers (including variables) attached to this nexus. */ bool drivers_present() const; @@ -713,7 +717,7 @@ class NetNet : public NetObj, public PortType { /* This method returns a reference to the packed dimensions for the vector. These are arranged as a list where the first range in the list (front) is the left-most range in - the verilog declaration. These packed dims are compressed + the Verilog declaration. These packed dims are compressed to represent the dimensions of all the subtypes. */ const std::vector& packed_dims() const { return slice_dims_; } @@ -1020,6 +1024,13 @@ class NetScope : public Definitions, public Attrib { TYPE type() const; void print_type(ostream&) const; + // This provides a link to the variable initialisation process + // for use when evaluating a constant function. Note this is + // only used for static functions - the variable initialization + // for automatic functions is included in the function definition. + void set_var_init(const NetProc*proc) { var_init_ = proc; } + const NetProc* var_init() const { return var_init_; } + void set_task_def(NetTaskDef*); void set_func_def(NetFuncDef*); void set_class_def(netclass_t*); @@ -1250,6 +1261,8 @@ class NetScope : public Definitions, public Attrib { vector ports_; + const NetProc*var_init_; + union { NetTaskDef*task_; NetFuncDef*func_; @@ -2732,6 +2745,12 @@ class NetAssign_ { void set_property(const perm_string&name); inline perm_string get_property(void) const { return member_; } + // Determine if the assigned object is signed or unsigned. + // This is used when determining the expression type for + // a compressed assignment statement. + bool get_signed() const { return signed_; } + void set_signed(bool flag) { signed_ = flag; } + // Get the width of the r-value that this node expects. This // method accounts for the presence of the mux, so it is not // necessarily the same as the pin_count(). @@ -2782,6 +2801,7 @@ class NetAssign_ { // member/property if signal is a class. perm_string member_; + bool signed_; bool turn_sig_to_wire_on_release_; // indexed part select base NetExpr*base_; @@ -2849,6 +2869,8 @@ class NetAssign : public NetAssignBase { map&ctx) const; private: + void eval_func_lval_op_real_(const LineInfo&loc, verireal&lv, verireal&rv) const; + void eval_func_lval_op_(const LineInfo&loc, verinum&lv, verinum&rv) const; bool eval_func_lval_(const LineInfo&loc, map&ctx, const NetAssign_*lval, NetExpr*rval_result) const; @@ -2895,6 +2917,7 @@ class NetBlock : public NetProc { NetScope* subscope() const { return subscope_; } void append(NetProc*); + void prepend(NetProc*); const NetProc*proc_first() const; const NetProc*proc_next(const NetProc*cur) const; diff --git a/netmisc.cc b/netmisc.cc index 50a48a2c3..b0bf41b98 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -203,7 +203,7 @@ static NetExpr* make_add_expr(NetExpr*expr, long val) } verinum val_v (val, expr->expr_width()); - val_v.has_sign(true); + val_v.has_sign(expr->has_sign()); NetEConst*val_c = new NetEConst(val_v); val_c->set_line(*expr); @@ -236,7 +236,7 @@ static NetExpr* make_add_expr(const LineInfo*loc, NetExpr*expr1, NetExpr*expr2) static NetExpr* make_sub_expr(long val, NetExpr*expr) { verinum val_v (val, expr->expr_width()); - val_v.has_sign(true); + val_v.has_sign(expr->has_sign()); NetEConst*val_c = new NetEConst(val_v); val_c->set_line(*expr); @@ -249,7 +249,26 @@ static NetExpr* make_sub_expr(long val, NetExpr*expr) } /* - * Multiple an existing expression by a signed positive number. + * Subtract a signed constant from an existing expression. + */ +static NetExpr* make_sub_expr(NetExpr*expr, long val) +{ + verinum val_v (val, expr->expr_width()); + val_v.has_sign(expr->has_sign()); + + NetEConst*val_c = new NetEConst(val_v); + val_c->set_line(*expr); + + NetEBAdd*res = new NetEBAdd('-', expr, val_c, expr->expr_width(), + expr->has_sign()); + res->set_line(*expr); + + return res; +} + + +/* + * Multiply an existing expression by a signed positive number. * This does a lossless multiply, so the arguments will need to be * sized to match the output size. */ @@ -258,7 +277,7 @@ static NetExpr* make_mult_expr(NetExpr*expr, unsigned long val) const unsigned val_wid = ceil(log2((double)val)) ; unsigned use_wid = expr->expr_width() + val_wid; verinum val_v (val, use_wid); - val_v.has_sign(true); + val_v.has_sign(expr->has_sign()); NetEConst*val_c = new NetEConst(val_v); val_c->set_line(*expr); @@ -434,17 +453,41 @@ NetExpr *normalize_variable_slice_base(const list&indices, NetExpr*base, -- pcur; } - long sb; - if (pcur->get_msb() >= pcur->get_lsb()) - sb = pcur->get_lsb(); - else - sb = pcur->get_msb(); - + long sb = min(pcur->get_lsb(), pcur->get_msb()); long loff; reg->sb_to_slice(indices, sb, loff, lwid); - base = make_mult_expr(base, lwid); - base = make_add_expr(base, loff); + unsigned min_wid = base->expr_width(); + if ((sb < 0) && !base->has_sign()) min_wid += 1; + if (min_wid < num_bits(pcur->get_lsb())) min_wid = pcur->get_lsb(); + if (min_wid < num_bits(pcur->get_msb())) min_wid = pcur->get_msb(); + base = pad_to_width(base, min_wid, *base); + if ((sb < 0) && !base->has_sign()) { + NetESelect *tmp = new NetESelect(base, 0 , min_wid); + tmp->set_line(*base); + tmp->cast_signed(true); + base = tmp; + } + + if (pcur->get_msb() >= pcur->get_lsb()) { + if (pcur->get_lsb() != 0) + base = make_sub_expr(base, pcur->get_lsb()); + base = make_mult_expr(base, lwid); + min_wid = base->expr_width(); + if (min_wid < num_bits(loff)) min_wid = num_bits(loff); + if (loff != 0) min_wid += 1; + base = pad_to_width(base, min_wid, *base); + base = make_add_expr(base, loff); + } else { + if (pcur->get_msb() != 0) + base = make_sub_expr(base, pcur->get_msb()); + base = make_mult_expr(base, lwid); + min_wid = base->expr_width(); + if (min_wid < num_bits(loff)) min_wid = num_bits(loff); + if (loff != 0) min_wid += 1; + base = pad_to_width(base, min_wid, *base); + base = make_sub_expr(loff, base); + } return base; } @@ -802,9 +845,10 @@ NetExpr* condition_reduce(NetExpr*expr) } static NetExpr* do_elab_and_eval(Design*des, NetScope*scope, PExpr*pe, - int context_width, bool need_const, bool annotatable, - bool force_expand, - ivl_variable_type_t cast_type) + int context_width, bool need_const, + bool annotatable, bool force_expand, + ivl_variable_type_t cast_type, + bool force_unsigned) { PExpr::width_mode_t mode = PExpr::SIZED; if ((context_width == -2) && !gn_strict_expr_width_flag) @@ -824,6 +868,11 @@ static NetExpr* do_elab_and_eval(Design*des, NetScope*scope, PExpr*pe, if ((pe->expr_type() != IVL_VT_REAL) && (expr_width < pos_context_width)) expr_width = pos_context_width; + // If this is the RHS of a compressed assignment, the LHS also + // affects the expression type (signed/unsigned). + if (force_unsigned) + pe->cast_signed(false); + if (debug_elaborate) { cerr << pe->get_fileline() << ": elab_and_eval: test_width of " << *pe << endl; @@ -910,15 +959,16 @@ static NetExpr* do_elab_and_eval(Design*des, NetScope*scope, PExpr*pe, NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width, bool need_const, bool annotatable, - ivl_variable_type_t cast_type) + ivl_variable_type_t cast_type, bool force_unsigned) { return do_elab_and_eval(des, scope, pe, context_width, - need_const, annotatable, false, cast_type); + need_const, annotatable, false, + cast_type, force_unsigned); } /* * This variant of elab_and_eval does the expression losslessly, no - * matter what the generation of verilog. This is in support of + * matter what the generation of Verilog. This is in support of * certain special contexts, notably index expressions. */ NetExpr* elab_and_eval_lossless(Design*des, NetScope*scope, PExpr*pe, @@ -926,7 +976,8 @@ NetExpr* elab_and_eval_lossless(Design*des, NetScope*scope, PExpr*pe, ivl_variable_type_t cast_type) { return do_elab_and_eval(des, scope, pe, context_width, - need_const, annotatable, true, cast_type); + need_const, annotatable, true, + cast_type, false); } NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, @@ -1404,7 +1455,7 @@ bool evaluate_index_prefix(Design*des, NetScope*scope, return false; } - prefix_indices .push_back(tmp); + prefix_indices.push_back(tmp); delete texpr; } diff --git a/netmisc.h b/netmisc.h index 2b67403f2..e0569bce0 100644 --- a/netmisc.h +++ b/netmisc.h @@ -1,7 +1,7 @@ #ifndef IVL_netmisc_H #define IVL_netmisc_H /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 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 @@ -60,12 +60,32 @@ inline NetScope* symbol_search(const LineInfo*li, } /* - * This function transforms an expression by padding the high bits - * with V0 until the expression has the desired width. This may mean - * not transforming the expression at all, if it is already wide - * enough. + * This function transforms an expression by either zero or sign extending + * the high bits until the expression has the desired width. This may mean + * not transforming the expression at all, if it is already wide enough. + * The extension method and the returned expression type is determined by + * signed_flag. */ -extern NetExpr*pad_to_width(NetExpr*expr, unsigned wid, const LineInfo&info); +extern NetExpr*pad_to_width(NetExpr*expr, unsigned wid, bool signed_flag, + const LineInfo&info); +/* + * This version determines the extension method from the base expression type. + */ +inline NetExpr*pad_to_width(NetExpr*expr, unsigned wid, const LineInfo&info) +{ + return pad_to_width(expr, wid, expr->has_sign(), info); +} + +/* + * This function transforms an expression by either zero or sign extending + * or discarding the high bits until the expression has the desired width. + * This may mean not transforming the expression at all, if it is already + * the correct width. The extension method (if needed) and the returned + * expression type is determined by signed_flag. + */ +extern NetExpr*cast_to_width(NetExpr*expr, unsigned wid, bool signed_flag, + const LineInfo&info); + extern NetNet*pad_to_width(Design*des, NetNet*n, unsigned w, const LineInfo&info); @@ -258,7 +278,8 @@ extern NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width, bool need_const =false, bool annotatable =false, - ivl_variable_type_t cast_type =IVL_VT_NO_TYPE); + ivl_variable_type_t cast_type =IVL_VT_NO_TYPE, + bool force_unsigned =false); extern NetExpr* elab_and_eval_lossless(Design*des, NetScope*scope, PExpr*pe, int context_width, @@ -297,7 +318,8 @@ extern NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_type_t lv_net_type, ivl_variable_type_t lv_type, unsigned lv_width, PExpr*expr, - bool need_const =false); + bool need_const =false, + bool force_unsigned =false); extern bool evaluate_ranges(Design*des, NetScope*scope, std::vector&llist, diff --git a/nettypes.cc b/nettypes.cc index 2f2e3988f..807956b4b 100644 --- a/nettypes.cc +++ b/nettypes.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2016 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 @@ -147,13 +147,12 @@ bool prefix_to_slice(const std::vector&dims, do { -- icur; acc_wid *= pcur->width(); + -- pcur; if (pcur->get_msb() >= pcur->get_lsb()) acc_off += (*icur - pcur->get_lsb()) * acc_wid; else acc_off += (pcur->get_lsb() - *icur) * acc_wid; - -- pcur; - } while (icur != prefix.begin()); // Got our final offset. diff --git a/pad_to_width.cc b/pad_to_width.cc index 32ba6c61d..32db9e6c4 100644 --- a/pad_to_width.cc +++ b/pad_to_width.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 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 @@ -24,21 +24,20 @@ # include "netmisc.h" -/* - * This function transforms an expression by padding the high bits - * with V0 until the expression has the desired width. This may mean - * not transforming the expression at all, if it is already wide - * enough. - */ -NetExpr*pad_to_width(NetExpr*expr, unsigned wid, const LineInfo&info) +NetExpr*pad_to_width(NetExpr*expr, unsigned wid, bool signed_flag, + const LineInfo&info) { - if (wid <= expr->expr_width()) + if (wid <= expr->expr_width()) { + expr->cast_signed(signed_flag); return expr; + } /* If the expression is a const, then replace it with a wider const. This is a more efficient result. */ if (NetEConst*tmp = dynamic_cast(expr)) { - verinum oval = pad_to_width(tmp->value(), wid); + verinum oval = tmp->value(); + oval.has_sign(signed_flag); + oval = pad_to_width(oval, wid); tmp = new NetEConst(oval); tmp->set_line(info); delete expr; @@ -46,8 +45,30 @@ NetExpr*pad_to_width(NetExpr*expr, unsigned wid, const LineInfo&info) } NetESelect*tmp = new NetESelect(expr, 0, wid); + tmp->cast_signed(signed_flag); tmp->set_line(info); - tmp->cast_signed(expr->has_sign()); + return tmp; +} + +NetExpr*cast_to_width(NetExpr*expr, unsigned wid, bool signed_flag, + const LineInfo&info) +{ + /* If the expression is a const, then replace it with a new + const. This is a more efficient result. */ + if (NetEConst*tmp = dynamic_cast(expr)) { + tmp->cast_signed(signed_flag); + if (wid != tmp->expr_width()) { + tmp = new NetEConst(verinum(tmp->value(), wid)); + tmp->set_line(info); + delete expr; + } + return tmp; + } + + NetESelect*tmp = new NetESelect(expr, 0, wid); + tmp->cast_signed(signed_flag); + tmp->set_line(info); + return tmp; } diff --git a/parse.y b/parse.y index 806b58713..a4bd0623e 100644 --- a/parse.y +++ b/parse.y @@ -1,7 +1,7 @@ %{ /* - * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -65,6 +65,10 @@ static PTask* current_task = 0; static PFunction* current_function = 0; static stack current_block_stack; +/* The variable declaration rules need to know if a lifetime has been + specified. */ +static LexicalScope::lifetime_t var_lifetime; + static pform_name_t* pform_create_this(void) { name_component_t name (perm_string::literal("@")); @@ -131,17 +135,17 @@ static list*attributes_in_context = 0; static const struct str_pair_t pull_strength = { IVL_DR_PULL, IVL_DR_PULL }; static const struct str_pair_t str_strength = { IVL_DR_STRONG, IVL_DR_STRONG }; -static list >* make_port_list(char*id, PExpr*expr) +static list* make_port_list(char*id, list*udims, PExpr*expr) { - list >*tmp = new list >; - tmp->push_back(make_pair(lex_strings.make(id), expr)); + list*tmp = new list; + tmp->push_back(pform_port_t(lex_strings.make(id), udims, expr)); delete[]id; return tmp; } -static list >* make_port_list(list >*tmp, - char*id, PExpr*expr) +static list* make_port_list(list*tmp, + char*id, list*udims, PExpr*expr) { - tmp->push_back(make_pair(lex_strings.make(id), expr)); + tmp->push_back(pform_port_t(lex_strings.make(id), udims, expr)); delete[]id; return tmp; } @@ -370,7 +374,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector char*text; list*perm_strings; - list >*port_list; + list*port_list; vector* tf_ports; @@ -452,6 +456,8 @@ static void current_function_set_statement(const YYLTYPE&loc, vector PSpecPath* specpath; list *dimensions; + + LexicalScope::lifetime_t lifetime; }; %token IDENTIFIER SYSTEM_IDENTIFIER STRING TIME_LITERAL @@ -560,7 +566,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type number pos_neg_number %type signing unsigned_signed_opt signed_unsigned_opt %type import_export -%type K_automatic_opt K_packed_opt K_reg_opt K_static_opt K_virtual_opt +%type K_packed_opt K_reg_opt K_static_opt K_virtual_opt %type udp_reg_opt edge_operator %type drive_strength drive_strength_opt dr_strength0 dr_strength1 %type udp_input_sym udp_output_sym @@ -576,7 +582,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type register_variable net_variable event_variable endlabel_opt class_declaration_endlabel_opt %type register_variable_list net_variable_list event_variable_list %type list_of_identifiers loop_variables -%type list_of_port_identifiers +%type list_of_port_identifiers list_of_variable_port_identifiers %type net_decl_assign net_decl_assigns @@ -607,7 +613,8 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type gate_instance_list %type hierarchy_identifier implicit_class_handle -%type assignment_pattern expression expr_primary expr_mintypmax +%type assignment_pattern expression expr_mintypmax +%type expr_primary_or_typename expr_primary %type class_new dynamic_array_new %type inc_or_dec_expression inside_expression lpvalue %type branch_probe_expression streaming_concatenation @@ -635,7 +642,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type variable_dimension %type dimensions_opt dimensions -%type net_type var_type net_type_opt +%type net_type net_type_opt %type gatetype switchtype %type port_direction port_direction_opt %type bit_logic bit_logic_opt @@ -664,6 +671,8 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type atom2_type %type module_start module_end +%type lifetime lifetime_opt + %token K_TAND %right K_PLUS_EQ K_MINUS_EQ K_MUL_EQ K_DIV_EQ K_MOD_EQ K_AND_EQ K_OR_EQ %right K_XOR_EQ K_LS_EQ K_RS_EQ K_RSS_EQ @@ -723,17 +732,17 @@ block_identifier_opt /* */ ; class_declaration /* IEEE1800-2005: A.1.2 */ - : K_virtual_opt K_class class_identifier class_declaration_extends_opt ';' - { pform_start_class_declaration(@2, $3, $4.type, $4.exprs); } + : K_virtual_opt K_class lifetime_opt class_identifier class_declaration_extends_opt ';' + { pform_start_class_declaration(@2, $4, $5.type, $5.exprs, $3); } class_items_opt K_endclass { // Process a class. - pform_end_class_declaration(@8); + pform_end_class_declaration(@9); } class_declaration_endlabel_opt { // Wrap up the class. - if ($10 && $3 && $3->name != $10) { - yyerror(@10, "error: Class end label doesn't match class name."); - delete[]$10; + if ($11 && $4 && $4->name != $11) { + yyerror(@11, "error: Class end label doesn't match class name."); + delete[]$11; } } ; @@ -1193,7 +1202,7 @@ for_step /* IEEE1800-2005: A.6.8 */ definitions in the func_body to take on the scope of the function instead of the module. */ function_declaration /* IEEE1800-2005: A.2.6 */ - : K_function K_automatic_opt data_type_or_implicit_or_void IDENTIFIER ';' + : K_function lifetime_opt data_type_or_implicit_or_void IDENTIFIER ';' { assert(current_function == 0); current_function = pform_push_function_scope(@1, $4, $2); } @@ -1214,7 +1223,7 @@ function_declaration /* IEEE1800-2005: A.2.6 */ "function name"); } if (! gn_system_verilog()) { - yyerror(@11, "error: Function end label require " + yyerror(@11, "error: Function end labels require " "SystemVerilog."); } delete[]$11; @@ -1222,7 +1231,7 @@ function_declaration /* IEEE1800-2005: A.2.6 */ delete[]$4; } - | K_function K_automatic_opt data_type_or_implicit_or_void IDENTIFIER + | K_function lifetime_opt data_type_or_implicit_or_void IDENTIFIER { assert(current_function == 0); current_function = pform_push_function_scope(@1, $4, $2); } @@ -1258,7 +1267,7 @@ function_declaration /* IEEE1800-2005: A.2.6 */ /* Detect and recover from some errors. */ - | K_function K_automatic_opt data_type_or_implicit_or_void IDENTIFIER error K_endfunction + | K_function lifetime_opt data_type_or_implicit_or_void IDENTIFIER error K_endfunction { /* */ if (current_function) { pform_pop_scope(); @@ -1364,6 +1373,16 @@ jump_statement /* IEEE1800-2005: A.6.5 */ } ; +lifetime /* IEEE1800-2005: A.2.1.3 */ + : K_automatic { $$ = LexicalScope::AUTOMATIC; } + | K_static { $$ = LexicalScope::STATIC; } + ; + +lifetime_opt /* IEEE1800-2005: A.2.1.3 */ + : lifetime { $$ = $1; } + | { $$ = LexicalScope::INHERITED; } + ; + /* Loop statements are kinds of statements. */ loop_statement /* IEEE1800-2005: A.6.8 */ @@ -1703,20 +1722,20 @@ open_range_list /* IEEE1800-2005 A.2.11 */ ; package_declaration /* IEEE1800-2005 A.1.2 */ - : K_package IDENTIFIER ';' - { pform_start_package_declaration(@1, $2); + : K_package lifetime_opt IDENTIFIER ';' + { pform_start_package_declaration(@1, $3, $2); } package_item_list_opt K_endpackage endlabel_opt { pform_end_package_declaration(@1); // If an end label is present make sure it match the package name. - if ($7) { - if (strcmp($2,$7) != 0) { - yyerror(@7, "error: End label doesn't match package name"); + if ($8) { + if (strcmp($3,$8) != 0) { + yyerror(@8, "error: End label doesn't match package name"); } - delete[]$7; + delete[]$8; } - delete[]$2; + delete[]$3; } ; @@ -1895,7 +1914,7 @@ streaming_concatenation /* IEEE1800-2005: A.8.1 */ task_declaration /* IEEE1800-2005: A.2.7 */ - : K_task K_automatic_opt IDENTIFIER ';' + : K_task lifetime_opt IDENTIFIER ';' { assert(current_task == 0); current_task = pform_push_task_scope(@1, $3, $2); } @@ -1908,7 +1927,7 @@ task_declaration /* IEEE1800-2005: A.2.7 */ pform_pop_scope(); current_task = 0; if ($7 && $7->size() > 1 && !gn_system_verilog()) { - yyerror(@7, "error: Task body with multiple statements requres SystemVerilog."); + yyerror(@7, "error: Task body with multiple statements requires SystemVerilog."); } delete $7; } @@ -1931,7 +1950,7 @@ task_declaration /* IEEE1800-2005: A.2.7 */ delete[]$3; } - | K_task K_automatic_opt IDENTIFIER '(' + | K_task lifetime_opt IDENTIFIER '(' { assert(current_task == 0); current_task = pform_push_task_scope(@1, $3, $2); } @@ -1965,7 +1984,7 @@ task_declaration /* IEEE1800-2005: A.2.7 */ delete[]$3; } - | K_task K_automatic_opt IDENTIFIER '(' ')' ';' + | K_task lifetime_opt IDENTIFIER '(' ')' ';' { assert(current_task == 0); current_task = pform_push_task_scope(@1, $3, $2); } @@ -1982,7 +2001,7 @@ task_declaration /* IEEE1800-2005: A.2.7 */ pform_pop_scope(); current_task = 0; if ($9->size() > 1 && !gn_system_verilog()) { - yyerror(@9, "error: Task body with multiple statements requres SystemVerilog."); + yyerror(@9, "error: Task body with multiple statements requires SystemVerilog."); } delete $9; } @@ -2005,7 +2024,7 @@ task_declaration /* IEEE1800-2005: A.2.7 */ delete[]$3; } - | K_task K_automatic_opt IDENTIFIER error K_endtask + | K_task lifetime_opt IDENTIFIER error K_endtask { assert(current_task == 0); } @@ -2213,7 +2232,7 @@ variable_dimension /* IEEE1800-2005: A.2.5 */ } | '[' expression ']' { // SystemVerilog canonical range - if (generation_flag < GN_VER2005_SV) { + if (!gn_system_verilog()) { warn_count += 1; cerr << @2 << ": warning: Use of SystemVerilog [size] dimension. " << "Use at least -g2005-sv to remove this warning." << endl; @@ -2243,6 +2262,19 @@ variable_dimension /* IEEE1800-2005: A.2.5 */ } ; +variable_lifetime + : lifetime + { if (!gn_system_verilog()) { + yyerror(@1, "error: overriding the default variable lifetime " + "requires SystemVerilog."); + } else if ($1 != pform_peek_scope()->default_lifetime) { + yyerror(@1, "sorry: overriding the default variable lifetime " + "is not yet supported."); + } + var_lifetime = $1; + } + ; + /* Verilog-2001 supports attribute lists, which can be attached to a variety of different objects. The syntax inside the (* *) is a comma separated list of names or names with assigned values. */ @@ -2317,10 +2349,20 @@ block_item_decl { if ($1) pform_set_data_type(@1, $1, $2, NetNet::REG, attributes_in_context); } + | variable_lifetime data_type register_variable_list ';' + { if ($2) pform_set_data_type(@2, $2, $3, NetNet::REG, attributes_in_context); + var_lifetime = LexicalScope::INHERITED; + } + | K_reg data_type register_variable_list ';' { if ($2) pform_set_data_type(@2, $2, $3, NetNet::REG, attributes_in_context); } + | variable_lifetime K_reg data_type register_variable_list ';' + { if ($3) pform_set_data_type(@3, $3, $4, NetNet::REG, attributes_in_context); + var_lifetime = LexicalScope::INHERITED; + } + | K_event event_variable_list ';' { if ($2) pform_make_events($2, @1.text, @1.first_line); } @@ -3033,7 +3075,7 @@ branch_probe_expression ; expression - : expr_primary + : expr_primary_or_typename { $$ = $1; } | inc_or_dec_expression { $$ = $1; } @@ -3328,6 +3370,20 @@ expression_list_proper } ; +expr_primary_or_typename + : expr_primary + + /* There are a few special cases (notably $bits argument) where the + expression may be a type name. Let the elaborator sort this out. */ + | TYPE_IDENTIFIER + { PETypename*tmp = new PETypename($1.type); + FILE_NAME(tmp,@1); + $$ = tmp; + delete[]$1.text; + } + + ; + expr_primary : number { assert($1); @@ -3369,15 +3425,6 @@ expr_primary delete[]$1; } - /* There are a few special cases (notably $bits argument) where the - expression may be a type name. Let the elaborator sort this out. */ - | TYPE_IDENTIFIER - { PETypename*tmp = new PETypename($1.type); - FILE_NAME(tmp,@1); - $$ = tmp; - delete[]$1.text; - } - /* The hierarchy_identifier rule matches simple identifiers as well as indexed arrays and part selects */ @@ -3681,12 +3728,11 @@ expr_primary /* Cast expressions are primaries */ - | DEC_NUMBER '\'' '(' expression ')' + | expr_primary '\'' '(' expression ')' { PExpr*base = $4; if (gn_system_verilog()) { - PECastSize*tmp = new PECastSize($1->as_ulong(), base); + PECastSize*tmp = new PECastSize($1, base); FILE_NAME(tmp, @1); - delete $1; $$ = tmp; } else { yyerror(@1, "error: Size cast requires SystemVerilog."); @@ -4002,14 +4048,21 @@ list_of_identifiers ; list_of_port_identifiers - : IDENTIFIER - { $$ = make_port_list($1, 0); } - | IDENTIFIER '=' expression - { $$ = make_port_list($1, $3); } - | list_of_port_identifiers ',' IDENTIFIER - { $$ = make_port_list($1, $3, 0); } - | list_of_port_identifiers ',' IDENTIFIER '=' expression - { $$ = make_port_list($1, $3, $5); } + : IDENTIFIER dimensions_opt + { $$ = make_port_list($1, $2, 0); } + | list_of_port_identifiers ',' IDENTIFIER dimensions_opt + { $$ = make_port_list($1, $3, $4, 0); } + ; + +list_of_variable_port_identifiers + : IDENTIFIER dimensions_opt + { $$ = make_port_list($1, $2, 0); } + | IDENTIFIER dimensions_opt '=' expression + { $$ = make_port_list($1, $2, $4); } + | list_of_variable_port_identifiers ',' IDENTIFIER dimensions_opt + { $$ = make_port_list($1, $3, $4, 0); } + | list_of_variable_port_identifiers ',' IDENTIFIER dimensions_opt '=' expression + { $$ = make_port_list($1, $3, $4, $6); } ; @@ -4218,7 +4271,7 @@ port_declaration port_declaration_context.port_net_type = use_type; port_declaration_context.data_type = $4; - pform_make_reginit(@5, name, $7); + pform_make_var_init(@5, name, $7); delete[]$5; $$ = ptmp; @@ -4372,13 +4425,13 @@ local_timeunit_prec_decl2 items, and finally an end marker. */ module - : attribute_list_opt module_start IDENTIFIER - { pform_startmodule(@2, $3, $2==K_program, $2==K_interface, $1); } + : attribute_list_opt module_start lifetime_opt IDENTIFIER + { pform_startmodule(@2, $4, $2==K_program, $2==K_interface, $3, $1); } module_package_import_list_opt module_parameter_port_list_opt module_port_list_opt module_attribute_foreign ';' - { pform_module_set_ports($7); } + { pform_module_set_ports($8); } local_timeunit_prec_decl_opt { have_timeunit_decl = true; // Every thing past here is have_timeprec_decl = true; // a check! @@ -4404,22 +4457,22 @@ module } // Check that program/endprogram and module/endmodule // keywords match. - if ($2 != $14) { + if ($2 != $15) { switch ($2) { case K_module: - yyerror(@14, "error: module not closed by endmodule."); + yyerror(@15, "error: module not closed by endmodule."); break; case K_program: - yyerror(@14, "error: program not closed by endprogram."); + yyerror(@15, "error: program not closed by endprogram."); break; case K_interface: - yyerror(@14, "error: interface not closed by endinterface."); + yyerror(@15, "error: interface not closed by endinterface."); break; default: break; } } - pform_endmodule($3, in_celldefine, ucd); + pform_endmodule($4, in_celldefine, ucd); have_timeunit_decl = false; // We will allow decls again. have_timeprec_decl = false; } @@ -4429,19 +4482,19 @@ module // endlabel_opt but still have the pform_endmodule() called // early enough that the lexor can know we are outside the // module. - if ($16) { - if (strcmp($3,$16) != 0) { + if ($17) { + if (strcmp($4,$17) != 0) { switch ($2) { case K_module: - yyerror(@16, "error: End label doesn't match " + yyerror(@17, "error: End label doesn't match " "module name."); break; case K_program: - yyerror(@16, "error: End label doesn't match " + yyerror(@17, "error: End label doesn't match " "program name."); break; case K_interface: - yyerror(@16, "error: End label doesn't match " + yyerror(@17, "error: End label doesn't match " "interface name."); break; default: @@ -4452,9 +4505,9 @@ module yyerror(@8, "error: Module end labels require " "SystemVerilog."); } - delete[]$16; + delete[]$17; } - delete[]$3; + delete[]$4; } ; @@ -4597,58 +4650,102 @@ module_item delete $4; } - | attribute_list_opt port_direction unsigned_signed_opt dimensions_opt delay3_opt list_of_identifiers ';' - { pform_set_port_type(@2, $6, $4, $3, $2, $1); } - /* The next two rules handle Verilog 2001 statements of the form: + /* The next two rules handle port declarations that include a net type, e.g. input wire signed [h:l] ; This creates the wire and sets the port type all at once. */ - | attribute_list_opt port_direction net_type unsigned_signed_opt dimensions_opt list_of_identifiers ';' - { pform_makewire(@2, $5, $4, $6, $3, $2, IVL_VT_NO_TYPE, $1, SR_BOTH); } + | attribute_list_opt port_direction net_type data_type_or_implicit list_of_port_identifiers ';' + { pform_module_define_port(@2, $5, $2, $3, $4, $1); } - | attribute_list_opt K_output var_type unsigned_signed_opt dimensions_opt list_of_port_identifiers ';' - { list >::const_iterator pp; - list*tmp = new list; - for (pp = $6->begin(); pp != $6->end(); ++ pp ) { - tmp->push_back((*pp).first); + | attribute_list_opt port_direction K_wreal list_of_port_identifiers ';' + { real_type_t*real_type = new real_type_t(real_type_t::REAL); + pform_module_define_port(@2, $4, $2, NetNet::WIRE, real_type, $1); + } + + /* The next three rules handle port declarations that include a variable + type, e.g. + output reg signed [h:l] ; + and also handle incomplete port declarations, e.g. + input signed [h:l] ; + */ + | attribute_list_opt K_inout data_type_or_implicit list_of_port_identifiers ';' + { NetNet::Type use_type = $3 ? NetNet::IMPLICIT : NetNet::NONE; + if (vector_type_t*dtype = dynamic_cast ($3)) { + if (dtype->implicit_flag) + use_type = NetNet::NONE; } - pform_makewire(@2, $5, $4, tmp, $3, NetNet::POUTPUT, - IVL_VT_NO_TYPE, $1, SR_BOTH); - for (pp = $6->begin(); pp != $6->end(); ++ pp ) { - if ((*pp).second) { - pform_make_reginit(@2, (*pp).first, (*pp).second); - } + if (use_type == NetNet::NONE) + pform_set_port_type(@2, $4, NetNet::PINOUT, $3, $1); + else + pform_module_define_port(@2, $4, NetNet::PINOUT, use_type, $3, $1); + } + + | attribute_list_opt K_input data_type_or_implicit list_of_port_identifiers ';' + { NetNet::Type use_type = $3 ? NetNet::IMPLICIT : NetNet::NONE; + if (vector_type_t*dtype = dynamic_cast ($3)) { + if (dtype->implicit_flag) + use_type = NetNet::NONE; } - delete $6; + if (use_type == NetNet::NONE) + pform_set_port_type(@2, $4, NetNet::PINPUT, $3, $1); + else + pform_module_define_port(@2, $4, NetNet::PINPUT, use_type, $3, $1); } - | attribute_list_opt port_direction K_wreal list_of_identifiers ';' - { pform_makewire(@2, 0, true, $4, NetNet::WIRE, $2, - IVL_VT_REAL, $1, SR_BOTH); + | attribute_list_opt K_output data_type_or_implicit list_of_variable_port_identifiers ';' + { NetNet::Type use_type = $3 ? NetNet::IMPLICIT : NetNet::NONE; + if (vector_type_t*dtype = dynamic_cast ($3)) { + if (dtype->implicit_flag) + use_type = NetNet::NONE; + else if (dtype->reg_flag) + use_type = NetNet::REG; + else + use_type = NetNet::IMPLICIT_REG; + + // The SystemVerilog types that can show up as + // output ports are implicitly (on the inside) + // variables because "reg" is not valid syntax + // here. + } else if (dynamic_cast ($3)) { + use_type = NetNet::IMPLICIT_REG; + } else if (dynamic_cast ($3)) { + use_type = NetNet::IMPLICIT_REG; + } else if (enum_type_t*etype = dynamic_cast ($3)) { + if(etype->base_type == IVL_VT_LOGIC) + use_type = NetNet::IMPLICIT_REG; + } + if (use_type == NetNet::NONE) + pform_set_port_type(@2, $4, NetNet::POUTPUT, $3, $1); + else + pform_module_define_port(@2, $4, NetNet::POUTPUT, use_type, $3, $1); } - /* var_type declaration (reg variables) cannot be input or output, - because the port declaration implies an external driver, which - cannot be attached to a reg. These rules catch that error early. */ - - | attribute_list_opt K_input var_type unsigned_signed_opt dimensions_opt list_of_identifiers ';' - { pform_makewire(@2, $5, $4, $6, $3, NetNet::PINPUT, - IVL_VT_NO_TYPE, $1); - yyerror(@3, "error: reg variables cannot be inputs."); - } - - | attribute_list_opt K_inout var_type unsigned_signed_opt dimensions_opt list_of_identifiers ';' - { pform_makewire(@2, $5, $4, $6, $3, NetNet::PINOUT, - IVL_VT_NO_TYPE, $1); - yyerror(@3, "error: reg variables cannot be inouts."); - } - - | attribute_list_opt port_direction unsigned_signed_opt dimensions_opt delay3_opt error ';' + | attribute_list_opt port_direction net_type data_type_or_implicit error ';' { yyerror(@2, "error: Invalid variable list in port declaration."); if ($1) delete $1; if ($4) delete $4; - if ($5) delete $5; + yyerrok; + } + + | attribute_list_opt K_inout data_type_or_implicit error ';' + { yyerror(@2, "error: Invalid variable list in port declaration."); + if ($1) delete $1; + if ($3) delete $3; + yyerrok; + } + + | attribute_list_opt K_input data_type_or_implicit error ';' + { yyerror(@2, "error: Invalid variable list in port declaration."); + if ($1) delete $1; + if ($3) delete $3; + yyerrok; + } + + | attribute_list_opt K_output data_type_or_implicit error ';' + { yyerror(@2, "error: Invalid variable list in port declaration."); + if ($1) delete $1; + if ($3) delete $3; yyerrok; } @@ -5048,10 +5145,6 @@ net_type | K_uwire { $$ = NetNet::UNRESOLVED_WIRE; } ; -var_type - : K_reg { $$ = NetNet::REG; } - ; - param_type : bit_logic_opt unsigned_signed_opt dimensions_opt { param_active_range = $3; @@ -5477,11 +5570,17 @@ register_variable $$ = $1; } | IDENTIFIER dimensions_opt '=' expression - { perm_string name = lex_strings.make($1); + { if (pform_peek_scope()->var_init_needs_explicit_lifetime() + && (var_lifetime == LexicalScope::INHERITED)) { + cerr << @3 << ": warning: Static variable initialization requires " + "explicit lifetime in this context." << endl; + warn_count += 1; + } + perm_string name = lex_strings.make($1); pform_makewire(@1, name, NetNet::REG, NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0); pform_set_reg_idx(name, $2); - pform_make_reginit(@1, name, $4); + pform_make_var_init(@1, name, $4); $$ = $1; } ; @@ -5734,7 +5833,23 @@ specify_path_identifiers delete[]$1; } | IDENTIFIER '[' expr_primary ']' - { list*tmp = new list; + { if (gn_specify_blocks_flag) { + yywarn(@4, "Bit selects are not currently supported " + "in path declarations. The declaration " + "will be applied to the whole vector."); + } + list*tmp = new list; + tmp->push_back(lex_strings.make($1)); + $$ = tmp; + delete[]$1; + } + | IDENTIFIER '[' expr_primary polarity_operator expr_primary ']' + { if (gn_specify_blocks_flag) { + yywarn(@4, "Part selects are not currently supported " + "in path declarations. The declaration " + "will be applied to the whole vector."); + } + list*tmp = new list; tmp->push_back(lex_strings.make($1)); $$ = tmp; delete[]$1; @@ -5746,7 +5861,23 @@ specify_path_identifiers delete[]$3; } | specify_path_identifiers ',' IDENTIFIER '[' expr_primary ']' - { list*tmp = $1; + { if (gn_specify_blocks_flag) { + yywarn(@4, "Bit selects are not currently supported " + "in path declarations. The declaration " + "will be applied to the whole vector."); + } + list*tmp = $1; + tmp->push_back(lex_strings.make($3)); + $$ = tmp; + delete[]$3; + } + | specify_path_identifiers ',' IDENTIFIER '[' expr_primary polarity_operator expr_primary ']' + { if (gn_specify_blocks_flag) { + yywarn(@4, "Part selects are not currently supported " + "in path declarations. The declaration " + "will be applied to the whole vector."); + } + list*tmp = $1; tmp->push_back(lex_strings.make($3)); $$ = tmp; delete[]$3; @@ -6777,7 +6908,6 @@ udp_primitive presence is significant. This is a fairly common pattern so collect those rules here. */ -K_automatic_opt: K_automatic { $$ = true; } | { $$ = false; } ; K_packed_opt : K_packed { $$ = true; } | { $$ = false; } ; K_reg_opt : K_reg { $$ = true; } | { $$ = false; } ; K_static_opt : K_static { $$ = true; } | { $$ = false; } ; diff --git a/pform.cc b/pform.cc index 594026993..fed8aefbf 100644 --- a/pform.cc +++ b/pform.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -312,12 +312,29 @@ static inline void FILE_NAME(LineInfo*obj, const char*file, unsigned lineno) */ static LexicalScope* lexical_scope = 0; +LexicalScope* pform_peek_scope(void) +{ + assert(lexical_scope); + return lexical_scope; +} + void pform_pop_scope() { assert(lexical_scope); lexical_scope = lexical_scope->parent_scope(); } +static LexicalScope::lifetime_t find_lifetime(LexicalScope::lifetime_t lifetime) +{ + if (lifetime != LexicalScope::INHERITED) + return lifetime; + + if (lexical_scope != 0) + return lexical_scope->default_lifetime; + + return LexicalScope::STATIC; +} + static PScopeExtra* find_nearest_scopex(LexicalScope*scope) { PScopeExtra*scopex = dynamic_cast (scope); @@ -328,17 +345,37 @@ static PScopeExtra* find_nearest_scopex(LexicalScope*scope) return scopex; } -LexicalScope* pform_peek_scope(void) +/* + * Set the local time unit/precision to the global value. + */ +static void pform_set_scope_timescale(PScope*scope, const struct vlltype&loc) { - assert(lexical_scope); - return lexical_scope; + scope->time_unit = pform_time_unit; + scope->time_precision = pform_time_prec; + /* If we have a timescale file then the time information is from + * a timescale directive. */ + scope->time_from_timescale = pform_timescale_file != 0; + + if (warn_timescale && (lexical_scope == 0) && pform_timescale_file + && (strcmp(pform_timescale_file, loc.text) != 0)) { + + cerr << loc.get_fileline() << ": warning: " + << "timescale for " << scope->pscope_name() + << " inherited from another file." << endl; + cerr << pform_timescale_file << ":" << pform_timescale_line + << ": ...: The inherited timescale is here." << endl; + } } -PClass* pform_push_class_scope(const struct vlltype&loc, perm_string name) +PClass* pform_push_class_scope(const struct vlltype&loc, perm_string name, + LexicalScope::lifetime_t lifetime) { PClass*class_scope = new PClass(name, lexical_scope); + class_scope->default_lifetime = find_lifetime(lifetime); FILE_NAME(class_scope, loc); + pform_set_scope_timescale(class_scope, loc); + PScopeExtra*scopex = find_nearest_scopex(lexical_scope); assert(!pform_cur_generate); @@ -364,22 +401,33 @@ PClass* pform_push_class_scope(const struct vlltype&loc, perm_string name) return class_scope; } -PPackage* pform_push_package_scope(const struct vlltype&loc, perm_string name) +PPackage* pform_push_package_scope(const struct vlltype&loc, perm_string name, + LexicalScope::lifetime_t lifetime) { PPackage*pkg_scope = new PPackage(name, lexical_scope); + pkg_scope->default_lifetime = find_lifetime(lifetime); FILE_NAME(pkg_scope, loc); + pform_set_scope_timescale(pkg_scope, loc); + lexical_scope = pkg_scope; return pkg_scope; } -PTask* pform_push_task_scope(const struct vlltype&loc, char*name, bool is_auto) +PTask* pform_push_task_scope(const struct vlltype&loc, char*name, + LexicalScope::lifetime_t lifetime) { perm_string task_name = lex_strings.make(name); + LexicalScope::lifetime_t default_lifetime = find_lifetime(lifetime); + bool is_auto = default_lifetime == LexicalScope::AUTOMATIC; + PTask*task = new PTask(task_name, lexical_scope, is_auto); + task->default_lifetime = default_lifetime; FILE_NAME(task, loc); + pform_set_scope_timescale(task, loc); + PScopeExtra*scopex = find_nearest_scopex(lexical_scope); if ((scopex == 0) && !gn_system_verilog()) { cerr << task->get_fileline() << ": error: task declarations " @@ -424,15 +472,21 @@ PTask* pform_push_task_scope(const struct vlltype&loc, char*name, bool is_auto) } PFunction* pform_push_function_scope(const struct vlltype&loc, const char*name, - bool is_auto) + LexicalScope::lifetime_t lifetime) { perm_string func_name = lex_strings.make(name); + LexicalScope::lifetime_t default_lifetime = find_lifetime(lifetime); + bool is_auto = default_lifetime == LexicalScope::AUTOMATIC; + PFunction*func = new PFunction(func_name, lexical_scope, is_auto); + func->default_lifetime = default_lifetime; FILE_NAME(func, loc); + pform_set_scope_timescale(func, loc); + PScopeExtra*scopex = find_nearest_scopex(lexical_scope); - if ((scopex == 0) && (generation_flag < GN_VER2005_SV)) { + if ((scopex == 0) && !gn_system_verilog()) { cerr << func->get_fileline() << ": error: function declarations " "must be contained within a module." << endl; error_count += 1; @@ -489,6 +543,7 @@ PBlock* pform_push_block_scope(char*name, PBlock::BL_TYPE bt) } PBlock*block = new PBlock(block_name, lexical_scope, bt); + block->default_lifetime = find_lifetime(LexicalScope::INHERITED); lexical_scope = block; return block; @@ -1041,7 +1096,10 @@ void pform_set_timeunit(const char*txt, bool in_module, bool only_check) int pform_get_timeunit() { - return pform_cur_module.front()->time_unit; + if (pform_cur_module.empty()) + return pform_time_unit; + else + return pform_cur_module.front()->time_unit; } void pform_set_timeprecision(const char*txt, bool in_module, bool only_check) @@ -1131,6 +1189,7 @@ verinum* pform_verinum_with_size(verinum*siz, verinum*val, void pform_startmodule(const struct vlltype&loc, const char*name, bool program_block, bool is_interface, + LexicalScope::lifetime_t lifetime, list*attr) { if (! pform_cur_module.empty() && !gn_system_verilog()) { @@ -1139,6 +1198,12 @@ void pform_startmodule(const struct vlltype&loc, const char*name, error_count += 1; } + if (lifetime != LexicalScope::INHERITED && !gn_system_verilog()) { + cerr << loc << ": error: Default subroutine lifetimes " + "require SystemVerilog." << endl; + error_count += 1; + } + if (gn_system_verilog() && ! pform_cur_module.empty()) { if (pform_cur_module.front()->program_block) { cerr << loc << ": error: module, program, or interface " @@ -1158,17 +1223,14 @@ void pform_startmodule(const struct vlltype&loc, const char*name, Module*cur_module = new Module(lexical_scope, lex_name); cur_module->program_block = program_block; cur_module->is_interface = is_interface; - /* Set the local time unit/precision to the global value. */ - cur_module->time_unit = pform_time_unit; - cur_module->time_precision = pform_time_prec; + cur_module->default_lifetime = find_lifetime(lifetime); + + FILE_NAME(cur_module, loc); + + pform_set_scope_timescale(cur_module, loc); tu_local_flag = tu_global_flag; tp_local_flag = tp_global_flag; - /* If we have a timescale file then the time information is from - * a timescale directive. */ - cur_module->time_from_timescale = pform_timescale_file != 0; - - FILE_NAME(cur_module, loc); cur_module->library_flag = pform_library_flag; pform_cur_module.push_front(cur_module); @@ -1179,15 +1241,6 @@ void pform_startmodule(const struct vlltype&loc, const char*name, zero. That's just the way it is, thanks to the standard. */ scope_generate_counter = 1; - if (warn_timescale && pform_timescale_file - && (strcmp(pform_timescale_file,loc.text) != 0)) { - - cerr << cur_module->get_fileline() << ": warning: " - << "timescale for " << name - << " inherited from another file." << endl; - cerr << pform_timescale_file << ":" << pform_timescale_line - << ": ...: The inherited timescale is here." << endl; - } pform_bind_attributes(cur_module->attributes, attr); } @@ -1199,7 +1252,7 @@ void pform_startmodule(const struct vlltype&loc, const char*name, void pform_check_timeunit_prec() { assert(! pform_cur_module.empty()); - if ((generation_flag & (GN_VER2005_SV | GN_VER2009 | GN_VER2012)) && + if (gn_system_verilog() && (pform_cur_module.front()->time_unit < pform_cur_module.front()->time_precision)) { VLerror("error: a timeprecision is missing or is too large!"); } else assert(pform_cur_module.front()->time_unit >= @@ -1905,6 +1958,7 @@ static void pform_set_net_range(perm_string name, VLerror("error: name is not a valid net."); return; } + // If this is not implicit ("implicit" meaning we don't // know what the type is yet) then set the type now. if (net_type != NetNet::IMPLICIT && net_type != NetNet::NONE) { @@ -2248,23 +2302,30 @@ void pform_make_pgassign_list(list*alist, } /* - * this function makes the initial assignment to a register as given - * in the source. It handles the case where a reg variable is assigned - * where it it declared: + * This function makes the initial assignment to a variable as given + * in the source. It handles the case where a variable is assigned + * where it is declared, e.g. * * reg foo = ; * - * This is equivalent to the combination of statements: + * In Verilog-2001 this is only supported at the module level, and is + * equivalent to the combination of statements: * * reg foo; * initial foo = ; * - * and that is how it is parsed. This syntax is not part of the - * IEEE1364-1995 standard, but is approved by OVI as enhancement - * BTF-B14. + * In SystemVerilog, variable initializations are allowed in any scope. + * For static variables, initializations are performed before the start + * of simulation. For automatic variables, initializations are performed + * each time the enclosing block is entered. Here we store the variable + * assignments in the current scope, and later elaboration creates an + * initialization block that will be executed at the appropriate time. + * + * This syntax is not part of the IEEE1364-1995 standard, but is + * approved by OVI as enhancement BTF-B14. */ -void pform_make_reginit(const struct vlltype&li, - perm_string name, PExpr*expr) +void pform_make_var_init(const struct vlltype&li, + perm_string name, PExpr*expr) { if (! pform_at_module_level() && !gn_system_verilog()) { VLerror(li, "error: variable declaration assignments are only " @@ -2275,7 +2336,7 @@ void pform_make_reginit(const struct vlltype&li, PWire*cur = pform_get_wire_in_scope(name); if (cur == 0) { - VLerror(li, "internal error: reginit to non-register?"); + VLerror(li, "internal error: var_init to non-register?"); delete expr; return; } @@ -2284,10 +2345,8 @@ void pform_make_reginit(const struct vlltype&li, FILE_NAME(lval, li); PAssign*ass = new PAssign(lval, expr, true); FILE_NAME(ass, li); - PProcess*top = new PProcess(IVL_PR_INITIAL, ass); - FILE_NAME(top, li); - pform_put_behavior_in_scope(top); + lexical_scope->var_inits.push_back(ass); } /* @@ -2305,7 +2364,8 @@ void pform_module_define_port(const struct vlltype&li, NetNet::PortType port_kind, NetNet::Type type, data_type_t*vtype, - list*attr) + list*attr, + bool keep_attr) { struct_type_t*struct_type = 0; ivl_variable_type_t data_type = IVL_VT_NO_TYPE; @@ -2394,10 +2454,37 @@ void pform_module_define_port(const struct vlltype&li, cur->set_unpacked_idx(*urange); } - pform_bind_attributes(cur->attributes, attr); + pform_bind_attributes(cur->attributes, attr, keep_attr); pform_put_wire_in_scope(name, cur); } +void pform_module_define_port(const struct vlltype&li, + list*ports, + NetNet::PortType port_kind, + NetNet::Type type, + data_type_t*vtype, + list*attr) +{ + for (list::iterator cur = ports->begin() + ; cur != ports->end() ; ++ cur ) { + + data_type_t*use_type = vtype; + if (cur->udims) + use_type = new uarray_type_t(vtype, cur->udims); + + pform_module_define_port(li, cur->name, port_kind, type, use_type, + attr, true); + if (cur->udims) + delete use_type; + + if (cur->expr) + pform_make_var_init(li, cur->name, cur->expr); + } + + delete ports; + delete attr; +} + /* * This function makes a single signal (a wire, a reg, etc) as * requested by the parser. The name is unscoped, so I attach the @@ -2591,7 +2678,7 @@ void pform_makewire(const struct vlltype&li, NetNet::Type type, data_type_t*data_type) { - if ((lexical_scope == 0) && (generation_flag < GN_VER2005_SV)) { + if ((lexical_scope == 0) && !gn_system_verilog()) { VLerror(li, "error: variable declarations must be contained within a module."); return; } @@ -2896,7 +2983,7 @@ void pform_set_parameter(const struct vlltype&loc, LexicalScope::range_t*value_range) { LexicalScope*scope = lexical_scope; - if ((scope == 0) && (generation_flag < GN_VER2005_SV)) { + if ((scope == 0) && !gn_system_verilog()) { VLerror(loc, "error: parameter declarations must be contained within a module."); return; } @@ -2971,7 +3058,7 @@ void pform_set_localparam(const struct vlltype&loc, bool signed_flag, list*range, PExpr*expr) { LexicalScope*scope = lexical_scope; - if ((scope == 0) && (generation_flag < GN_VER2005_SV)) { + if ((scope == 0) && !gn_system_verilog()) { VLerror(loc, "error: localparam declarations must be contained within a module."); return; } @@ -3200,24 +3287,53 @@ static void pform_set_port_type(perm_string name, NetNet::PortType pt, } void pform_set_port_type(const struct vlltype&li, - list*names, - list*range, - bool signed_flag, + list*ports, NetNet::PortType pt, + data_type_t*dt, list*attr) { assert(pt != NetNet::PIMPLICIT && pt != NetNet::NOT_A_PORT); - for (list::iterator cur = names->begin() - ; cur != names->end() ; ++ cur ) { - perm_string txt = *cur; - pform_set_port_type(txt, pt, li.text, li.first_line); - pform_set_net_range(txt, NetNet::NONE, range, signed_flag, IVL_VT_NO_TYPE, - SR_PORT, attr); + list*range = 0; + bool signed_flag = false; + if (vector_type_t*vt = dynamic_cast (dt)) { + assert(vt->implicit_flag); + range = vt->pdims.get(); + signed_flag = vt->signed_flag; + } else { + assert(dt == 0); } - delete names; - delete range; + bool have_init_expr = false; + for (list::iterator cur = ports->begin() + ; cur != ports->end() ; ++ cur ) { + + pform_set_port_type(cur->name, pt, li.text, li.first_line); + pform_set_net_range(cur->name, NetNet::NONE, range, signed_flag, + IVL_VT_NO_TYPE, SR_PORT, attr); + if (cur->udims) { + cerr << li.text << ":" << li.first_line << ": warning: " + << "Array dimensions in incomplete port declarations " + << "are currently ignored." << endl; + cerr << li.text << ":" << li.first_line << ": : " + << "The dimensions specified in the net or variable " + << "declaration will be used." << endl; + delete cur->udims; + } + if (cur->expr) { + have_init_expr = true; + delete cur->expr; + } + } + if (have_init_expr) { + cerr << li.text << ":" << li.first_line << ": error: " + << "Incomplete port declarations cannot be initialized." + << endl; + error_count += 1; + } + + delete ports; + delete dt; delete attr; } diff --git a/pform.h b/pform.h index 2e7f8ea5e..b3544f9e9 100644 --- a/pform.h +++ b/pform.h @@ -1,7 +1,7 @@ #ifndef IVL_pform_H #define IVL_pform_H /* - * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2016 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 @@ -163,18 +163,26 @@ extern PWire* pform_get_make_wire_in_scope(perm_string name, NetNet::Type net_ty */ extern void pform_startmodule(const struct vlltype&loc, const char*name, bool program_block, bool is_interface, + LexicalScope::lifetime_t lifetime, list*attr); extern void pform_check_timeunit_prec(); extern void pform_module_set_ports(vector*); -/* This function is used to support the port definition in a - port_definition_list. In this case, we have everything needed to - define the port, all in one place. */ +/* These functions are used when we have a complete port definition, either + in an ansi style or non-ansi style declaration. In this case, we have + everything needed to define the port, all in one place. */ extern void pform_module_define_port(const struct vlltype&li, perm_string name, NetNet::PortType, NetNet::Type type, data_type_t*vtype, + list*attr, + bool keep_attr =false); +extern void pform_module_define_port(const struct vlltype&li, + list*ports, + NetNet::PortType, + NetNet::Type type, + data_type_t*vtype, list*attr); extern Module::port_t* pform_module_port_reference(perm_string name, @@ -186,7 +194,8 @@ extern void pform_endmodule(const char*, bool inside_celldefine, extern void pform_start_class_declaration(const struct vlltype&loc, class_type_t*type, data_type_t*base_type, - std::list*base_exprs); + std::list*base_exprs, + LexicalScope::lifetime_t lifetime); extern void pform_class_property(const struct vlltype&loc, property_qualifier_t pq, data_type_t*data_type, @@ -211,7 +220,8 @@ extern void pform_make_udp(perm_string name, * Package related functions. */ extern void pform_start_package_declaration(const struct vlltype&loc, - const char*type); + const char*type, + LexicalScope::lifetime_t lifetime); extern void pform_end_package_declaration(const struct vlltype&loc); extern void pform_package_import(const struct vlltype&loc, PPackage*pkg, const char*ident); @@ -246,13 +256,20 @@ extern void pform_pop_scope(); */ extern LexicalScope* pform_peek_scope(); -extern PClass* pform_push_class_scope(const struct vlltype&loc, perm_string name); +extern PClass* pform_push_class_scope(const struct vlltype&loc, perm_string name, + LexicalScope::lifetime_t lifetime); + extern PFunction*pform_push_constructor_scope(const struct vlltype&loc); -extern PPackage* pform_push_package_scope(const struct vlltype&loc, perm_string name); + +extern PPackage* pform_push_package_scope(const struct vlltype&loc, perm_string name, + LexicalScope::lifetime_t lifetime); + extern PTask*pform_push_task_scope(const struct vlltype&loc, char*name, - bool is_auto); + LexicalScope::lifetime_t lifetime); + extern PFunction*pform_push_function_scope(const struct vlltype&loc, const char*name, - bool is_auto); + LexicalScope::lifetime_t lifetime); + extern PBlock*pform_push_block_scope(char*name, PBlock::BL_TYPE tt); extern void pform_put_behavior_in_scope(AProcess*proc); @@ -351,17 +368,17 @@ extern void pform_makewire(const struct vlltype&li, list*names, list*attr); -extern void pform_make_reginit(const struct vlltype&li, - perm_string name, PExpr*expr); +extern void pform_make_var_init(const struct vlltype&li, + perm_string name, PExpr*expr); - /* Look up the names of the wires, and set the port type, - i.e. input, output or inout. If the wire does not exist, create - it. The second form takes a single name. */ +/* This function is used when we have an incomplete port definition in + a non-ansi style declaration. Look up the names of the wires, and set + the port type, i.e. input, output or inout, and, if specified, the + range and signedness. If the wire does not exist, create it. */ extern void pform_set_port_type(const struct vlltype&li, - list*names, - list*range, - bool signed_flag, + list*ports, NetNet::PortType, + data_type_t*dt, list*attr); extern void pform_set_reg_idx(perm_string name, diff --git a/pform_dump.cc b/pform_dump.cc index 4cbd994ec..bc44c0519 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2016 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 @@ -318,7 +318,7 @@ void PECallFunction::dump(ostream &out) const void PECastSize::dump(ostream &out) const { - out << size_ << "'("; + out << *size_ << "'("; base_->dump(out); out << ")"; } @@ -789,6 +789,8 @@ void PBlock::dump(ostream&out, unsigned ind) const dump_events_(out, ind+2); dump_wires_(out, ind+2); + + dump_var_inits_(out, ind+2); } for (unsigned idx = 0 ; idx < list_.size() ; idx += 1) { @@ -1030,6 +1032,8 @@ void PFunction::dump(ostream&out, unsigned ind) const dump_wires_(out, ind+2); + dump_var_inits_(out, ind+2); + if (statement_) statement_->dump(out, ind+2); else @@ -1072,6 +1076,8 @@ void PTask::dump(ostream&out, unsigned ind) const dump_wires_(out, ind+2); + dump_var_inits_(out, ind+2); + if (statement_) statement_->dump(out, ind+2); else @@ -1269,6 +1275,8 @@ void PGenerate::dump(ostream&out, unsigned indent) const (*idx)->dump(out, indent+2); } + dump_var_inits_(out, indent+2); + for (list::const_iterator idx = behaviors.begin() ; idx != behaviors.end() ; ++ idx ) { (*idx)->dump(out, indent+2); @@ -1408,6 +1416,14 @@ void LexicalScope::dump_wires_(ostream&out, unsigned indent) const } } +void LexicalScope::dump_var_inits_(ostream&out, unsigned indent) const +{ + // Iterate through and display all the register initializations. + for (unsigned idx = 0; idx < var_inits.size(); idx += 1) { + var_inits[idx]->dump(out, indent); + } +} + void PScopeExtra::dump_classes_(ostream&out, unsigned indent) const { // Dump the task definitions. @@ -1564,6 +1580,7 @@ void Module::dump(ostream&out) const (*gate)->dump(out); } + dump_var_inits_(out, 4); for (list::const_iterator behav = behaviors.begin() ; behav != behaviors.end() ; ++ behav ) { diff --git a/pform_package.cc b/pform_package.cc index a4ed9c832..4c4b38440 100644 --- a/pform_package.cc +++ b/pform_package.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -35,12 +35,13 @@ map pform_packages; static PPackage*pform_cur_package = 0; -void pform_start_package_declaration(const struct vlltype&loc, const char*name) +void pform_start_package_declaration(const struct vlltype&loc, const char*name, + LexicalScope::lifetime_t lifetime) { ivl_assert(loc, pform_cur_package == 0); perm_string use_name = lex_strings.make(name); - PPackage*pkg_scope = pform_push_package_scope(loc, use_name); + PPackage*pkg_scope = pform_push_package_scope(loc, use_name, lifetime); FILE_NAME(pkg_scope, loc); pform_cur_package = pkg_scope; } diff --git a/pform_pclass.cc b/pform_pclass.cc index 0636126f4..a3b7df4f0 100644 --- a/pform_pclass.cc +++ b/pform_pclass.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2016 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 @@ -36,9 +36,13 @@ static PClass*pform_cur_class = 0; * if present, are the "exprs" that would be passed to a chained * constructor. */ -void pform_start_class_declaration(const struct vlltype&loc, class_type_t*type, data_type_t*base_type, list*base_exprs) +void pform_start_class_declaration(const struct vlltype&loc, + class_type_t*type, + data_type_t*base_type, + list*base_exprs, + LexicalScope::lifetime_t lifetime) { - PClass*class_scope = pform_push_class_scope(loc, type->name); + PClass*class_scope = pform_push_class_scope(loc, type->name, lifetime); class_scope->type = type; assert(pform_cur_class == 0); pform_cur_class = class_scope; @@ -75,6 +79,7 @@ void pform_class_property(const struct vlltype&loc, if (! curp->index.empty()) { list*pd = new list (curp->index); use_type = new uarray_type_t(use_type, pd); + FILE_NAME(use_type, loc); } pform_cur_class->type->properties[curp->name] @@ -127,7 +132,7 @@ void pform_set_constructor_return(PFunction*net) PFunction*pform_push_constructor_scope(const struct vlltype&loc) { assert(pform_cur_class); - PFunction*func = pform_push_function_scope(loc, "new", true); + PFunction*func = pform_push_function_scope(loc, "new", LexicalScope::AUTOMATIC); return func; } @@ -138,7 +143,7 @@ void pform_end_class_declaration(const struct vlltype&loc) // If there were initializer statements, then collect them // into an implicit constructor function. if (! pform_cur_class->type->initialize.empty()) { - PFunction*func = pform_push_function_scope(loc, "new@", true); + PFunction*func = pform_push_function_scope(loc, "new@", LexicalScope::AUTOMATIC); func->set_ports(0); pform_set_constructor_return(func); pform_set_this_class(loc, func); diff --git a/pform_types.h b/pform_types.h index 149ae9f27..22dab4b46 100644 --- a/pform_types.h +++ b/pform_types.h @@ -1,7 +1,7 @@ #ifndef IVL_pform_types_H #define IVL_pform_types_H /* - * Copyright (c) 2007-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2007-2016 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 @@ -72,6 +72,21 @@ typedef named named_pexpr_t; */ typedef std::pair pform_range_t; +/* + * The pform_port_t holds the name and optional unpacked dimensions + * and initialization expression for a single port in a list of port + * declarations. + */ +struct pform_port_t { + pform_port_t(perm_string n, list*ud, PExpr*e) + : name(n), udims(ud), expr(e) { } + ~pform_port_t() { } + + perm_string name; + list*udims; + PExpr*expr; +}; + /* * Semantic NOTES: * - The SEL_BIT is a single expression. This might me a bit select diff --git a/synth2.cc b/synth2.cc index 3d3805891..da173a1c6 100644 --- a/synth2.cc +++ b/synth2.cc @@ -565,7 +565,7 @@ bool NetCase::synth_async(Design*des, NetScope*scope, // The minimum selector width is the number of inputs that // are selected, rounded up to the nearest power of 2. - unsigned sel_need = ceil(log2(max_guard_value + 1)); + unsigned sel_need = max(ceil(log2(max_guard_value + 1)), 1.0); // If the sel_width can select more than just the explicit // guard values, and there is a default statement, then adjust diff --git a/tgt-vlog95/misc.c b/tgt-vlog95/misc.c index 8787c0302..7c8d32987 100644 --- a/tgt-vlog95/misc.c +++ b/tgt-vlog95/misc.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2016 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -747,7 +747,9 @@ void emit_name_of_nexus(ivl_scope_t scope, ivl_nexus_t nex, unsigned allow_UD) * This function traverses the scope tree looking for the enclosing module * scope. When it is found the module scope is returned. As far as this * translation is concerned a package is a special form of a module - * definition and a class is also a top level scope. + * definition and a class is also a top level scope. In SystemVerilog, + * tasks and functions can also be top level scopes - we create a wrapper + * module for these later. */ ivl_scope_t get_module_scope(ivl_scope_t scope) { @@ -756,6 +758,12 @@ ivl_scope_t get_module_scope(ivl_scope_t scope) (ivl_scope_type(scope) != IVL_SCT_PACKAGE) && (ivl_scope_type(scope) != IVL_SCT_CLASS)) { ivl_scope_t pscope = ivl_scope_parent(scope); + if (pscope == 0) { + if (ivl_scope_type(scope) == IVL_SCT_TASK) + break; + if (ivl_scope_type(scope) == IVL_SCT_FUNCTION) + break; + } assert(pscope); scope = pscope; } @@ -872,7 +880,8 @@ void emit_scope_path(ivl_scope_t scope, ivl_scope_t call_scope) /* Check to see if this is a root scope task or function. */ if (ivl_scope_parent(call_scope) == 0) { - fprintf(vlog_out, "ivl_root_scope."); + fprintf(vlog_out, "ivl_root_scope_%s.", + ivl_scope_basename(call_scope)); mod_scope = 0; call_mod_scope = 0; } else { @@ -938,3 +947,30 @@ void get_sig_msb_lsb(ivl_signal_t sig, int *msb, int *lsb) break; } } + +const char*get_time_const(int time_value) +{ + switch (time_value) { + case 2: return "100s"; + case 1: return "10s"; + case 0: return "1s"; + case -1: return "100ms"; + case -2: return "10ms"; + case -3: return "1ms"; + case -4: return "100us"; + case -5: return "10us"; + case -6: return "1us"; + case -7: return "100ns"; + case -8: return "10ns"; + case -9: return "1ns"; + case -10: return "100ps"; + case -11: return "10ps"; + case -12: return "1ps"; + case -13: return "100fs"; + case -14: return "10fs"; + case -15: return "1fs"; + default: + fprintf(stderr, "Invalid time constant value %d.\n", time_value); + return "N/A"; + } +} diff --git a/tgt-vlog95/scope.c b/tgt-vlog95/scope.c index 1f939d8d2..116d696f7 100644 --- a/tgt-vlog95/scope.c +++ b/tgt-vlog95/scope.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2015 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2010-2016 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,33 +24,6 @@ const char *func_rtn_name = 0; -static const char*get_time_const(int time_value) -{ - switch (time_value) { - case 2: return "100s"; - case 1: return "10s"; - case 0: return "1s"; - case -1: return "100ms"; - case -2: return "10ms"; - case -3: return "1ms"; - case -4: return "100us"; - case -5: return "10us"; - case -6: return "1us"; - case -7: return "100ns"; - case -8: return "10ns"; - case -9: return "1ns"; - case -10: return "100ps"; - case -11: return "10ps"; - case -12: return "1ps"; - case -13: return "100fs"; - case -14: return "10fs"; - case -15: return "1fs"; - default: - fprintf(stderr, "Invalid time constant value %d.\n", time_value); - return "N/A"; - } -} - static void emit_func_return(ivl_signal_t sig) { if (ivl_signal_dimensions(sig) > 0) { diff --git a/tgt-vlog95/vlog95.c b/tgt-vlog95/vlog95.c index 4841cb522..1890d76b6 100644 --- a/tgt-vlog95/vlog95.c +++ b/tgt-vlog95/vlog95.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2013 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2010-2016 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,7 +60,6 @@ int target_design(ivl_design_t des) { ivl_scope_t *roots; unsigned nroots, idx; - unsigned has_root_scope = 0; const char*path = ivl_design_flag(des, "-o"); /* Set the indent spacing with the -pspacing flag passed to iverilog * (e.g. -pspacing=4). The default is 2 spaces. */ @@ -190,23 +189,27 @@ int target_design(ivl_design_t des) switch(ivl_scope_type(roots[idx])) { case IVL_SCT_FUNCTION: case IVL_SCT_TASK: - if (! has_root_scope) { - fprintf(vlog_out, "module ivl_root_scope;\n"); - indent += indent_incr; - has_root_scope = 1; - } + /* Create a separate module for each task/function. + This allows us to handle different timescales. */ + fprintf(vlog_out, "\n`timescale %s/%s\n", + get_time_const(ivl_scope_time_units(roots[idx])), + get_time_const(ivl_scope_time_precision(roots[idx]))); + fprintf(vlog_out, "module ivl_root_scope_%s;\n", + ivl_scope_basename(roots[idx])); + indent += indent_incr; + /* Say this task/function has a parent so the * definition is emitted correctly. */ emit_scope(roots[idx], roots[idx]); + + indent -= indent_incr; + assert(indent == 0); + fprintf(vlog_out, "endmodule /* ivl_root_scope_%p */\n", + roots[idx]); break; default: break; } - } - if (has_root_scope) { - indent -= indent_incr; - assert(indent == 0); - fprintf(vlog_out, "endmodule /* ivl_root_scope */\n"); } /* Emit the rest of the scope objects. */ for (idx = 0; idx < nroots; idx += 1) emit_scope(roots[idx], 0); diff --git a/tgt-vlog95/vlog95_priv.h b/tgt-vlog95/vlog95_priv.h index fb632d95e..913fd0424 100644 --- a/tgt-vlog95/vlog95_priv.h +++ b/tgt-vlog95/vlog95_priv.h @@ -1,7 +1,7 @@ #ifndef IVL_vlog95_priv_H #define IVL_vlog95_priv_H /* - * Copyright (C) 2010-2014 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2010-2016 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -145,6 +145,11 @@ extern char * get_package_name(ivl_scope_t scope); */ extern void get_sig_msb_lsb(ivl_signal_t sig, int *msb, int *lsb); +/* + * Convert a timescale value to a string. + */ +extern const char*get_time_const(int time_value); + /* * Cleanup functions. */ diff --git a/tgt-vvp/Makefile.in b/tgt-vvp/Makefile.in index 82a4a78e2..d09c9e709 100644 --- a/tgt-vvp/Makefile.in +++ b/tgt-vvp/Makefile.in @@ -47,8 +47,8 @@ CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ LDFLAGS = @LDFLAGS@ -O = vvp.o draw_class.o draw_enum.o draw_mux.o draw_substitute.o draw_net_input.o \ - draw_switch.o draw_ufunc.o draw_vpi.o \ +O = vvp.o draw_class.o draw_delay.o draw_enum.o draw_mux.o draw_net_input.o \ + draw_substitute.o draw_switch.o draw_ufunc.o draw_vpi.o \ eval_bool.o \ eval_condit.o \ eval_expr.o eval_object.o eval_real.o eval_string.o \ diff --git a/tgt-vvp/draw_delay.c b/tgt-vvp/draw_delay.c new file mode 100644 index 000000000..1fcc92a21 --- /dev/null +++ b/tgt-vvp/draw_delay.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2016 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 + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +# include "vvp_priv.h" +# include +# include +# include + + +/* + * This function draws a BUFT to drive a constant delay value. + */ +static char* draw_const_net(void*ptr, char*suffix, uint64_t value) +{ + char tmp[64]; + char c4_value[69]; + unsigned idx; + c4_value[0] = 'C'; + c4_value[1] = '4'; + c4_value[2] = '<'; + for (idx = 0; idx < 64; idx += 1) { + c4_value[66-idx] = (value & 1) ? '1' : '0'; + value >>= 1; + } + c4_value[67] = '>'; + c4_value[68] = 0; + + /* Make the constant an argument to a BUFT, which is + what we use to drive the value. */ + fprintf(vvp_out, "L_%p/%s .functor BUFT 1, %s, C4<0>, C4<0>, C4<0>;\n", + ptr, suffix, c4_value); + snprintf(tmp, sizeof tmp, "L_%p/%s", ptr, suffix); + return strdup(tmp); +} + +/* + * Draw the appropriate delay statement. + */ +void draw_delay(void*ptr, unsigned wid, const char*input, ivl_expr_t rise_exp, + ivl_expr_t fall_exp, ivl_expr_t decay_exp) +{ + char tmp[64]; + if (input == 0) { + snprintf(tmp, sizeof tmp, "L_%p/d", ptr); + input = tmp; + } + + /* If the delays are all constants then process them here. */ + if (number_is_immediate(rise_exp, 64, 0) && + number_is_immediate(fall_exp, 64, 0) && + number_is_immediate(decay_exp, 64, 0)) { + + assert(! number_is_unknown(rise_exp)); + assert(! number_is_unknown(fall_exp)); + assert(! number_is_unknown(decay_exp)); + + fprintf(vvp_out, "L_%p .delay %u " + "(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") %s;\n", + ptr, wid, + get_number_immediate64(rise_exp), + get_number_immediate64(fall_exp), + get_number_immediate64(decay_exp), + input); + + /* For a variable delay we indicate only two delays by setting the + * decay time to zero. */ + } else { + char*rise_const = 0; + char*fall_const = 0; + char*decay_const = 0; + const char*rise_str; + const char*fall_str; + const char*decay_str; + + if (number_is_immediate(rise_exp, 64, 0)) { + uint64_t value = get_number_immediate64(rise_exp); + rise_str = rise_const = draw_const_net(ptr, "tr", value); + } else { + ivl_signal_t sig = ivl_expr_signal(rise_exp); + assert(sig && ivl_signal_dimensions(sig) == 0); + rise_str = draw_net_input(ivl_signal_nex(sig,0)); + } + + if (number_is_immediate(fall_exp, 64, 0)) { + uint64_t value = get_number_immediate64(fall_exp); + fall_str = fall_const = draw_const_net(ptr, "tf", value); + } else { + ivl_signal_t sig = ivl_expr_signal(fall_exp); + assert(sig && ivl_signal_dimensions(sig) == 0); + fall_str = draw_net_input(ivl_signal_nex(sig,0)); + } + + if (decay_exp == 0) { + decay_str = "0"; + } else if (number_is_immediate(decay_exp, 64, 0)) { + uint64_t value = get_number_immediate64(decay_exp); + decay_str = decay_const = draw_const_net(ptr, "td", value); + } else { + ivl_signal_t sig = ivl_expr_signal(decay_exp); + assert(sig && ivl_signal_dimensions(sig) == 0); + decay_str = draw_net_input(ivl_signal_nex(sig,0)); + } + + fprintf(vvp_out, "L_%p .delay %u %s, %s, %s, %s;\n", + ptr, wid, input, rise_str, fall_str, decay_str); + + free(rise_const); + free(fall_const); + free(decay_const); + } +} diff --git a/tgt-vvp/draw_mux.c b/tgt-vvp/draw_mux.c index 4fb27270f..41b952267 100644 --- a/tgt-vvp/draw_mux.c +++ b/tgt-vvp/draw_mux.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2016 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 @@ -51,44 +51,8 @@ static void draw_lpm_mux_ab(ivl_lpm_t net, const char*muxz) if (data_type_of_nexus(ivl_lpm_q(net)) == IVL_VT_REAL) dly_width = 0; + draw_delay(net, dly_width, 0, d_rise, d_fall, d_decay); dly = "/d"; - if (number_is_immediate(d_rise, 64, 0) && - number_is_immediate(d_fall, 64, 0) && - number_is_immediate(d_decay, 64, 0)) { - - assert( ! number_is_unknown(d_rise)); - assert( ! number_is_unknown(d_fall)); - assert( ! number_is_unknown(d_decay)); - - fprintf(vvp_out, "L_%p .delay %u (%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") L_%p/d;\n", - net, dly_width, - get_number_immediate64(d_rise), - get_number_immediate64(d_fall), - get_number_immediate64(d_decay), net); - } else { - ivl_signal_t sig; - // We do not currently support calculating the decay from - // the rise and fall variable delays. - assert(d_decay != 0); - assert(ivl_expr_type(d_rise) == IVL_EX_SIGNAL); - assert(ivl_expr_type(d_fall) == IVL_EX_SIGNAL); - assert(ivl_expr_type(d_decay) == IVL_EX_SIGNAL); - - fprintf(vvp_out, "L_%p .delay %u L_%p/d", - net, dly_width, net); - - sig = ivl_expr_signal(d_rise); - assert(ivl_signal_dimensions(sig) == 0); - fprintf(vvp_out, ", v%p_0", sig); - - sig = ivl_expr_signal(d_fall); - assert(ivl_signal_dimensions(sig) == 0); - fprintf(vvp_out, ", v%p_0", sig); - - sig = ivl_expr_signal(d_decay); - assert(ivl_signal_dimensions(sig) == 0); - fprintf(vvp_out, ", v%p_0;\n", sig); - } } input[0] = draw_net_input(ivl_lpm_data(net,0)); diff --git a/tgt-vvp/draw_net_input.c b/tgt-vvp/draw_net_input.c index e2f94fb20..e98d394e3 100644 --- a/tgt-vvp/draw_net_input.c +++ b/tgt-vvp/draw_net_input.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -330,9 +330,11 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr) cptr = ivl_nexus_ptr_con(nptr); if (cptr) { + char tmp[64]; char *result = 0; ivl_expr_t d_rise, d_fall, d_decay; unsigned dly_width = 0; + char *dly; /* Constants should have exactly 1 pin, with a literal value. */ assert(nptr_pin == 0); @@ -368,68 +370,17 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr) d_fall = ivl_const_delay(cptr, 1); d_decay = ivl_const_delay(cptr, 2); - /* We have a delayed constant, so we need to build some code. */ + dly = ""; if (d_rise != 0) { - char tmp[128]; - fprintf(vvp_out, "L_%p/d .functor BUFT 1, %s, " - "C4<0>, C4<0>, C4<0>;\n", cptr, result); - free(result); - - /* Is this a fixed or variable delay? */ - if (number_is_immediate(d_rise, 64, 0) && - number_is_immediate(d_fall, 64, 0) && - number_is_immediate(d_decay, 64, 0)) { - - assert(! number_is_unknown(d_rise)); - assert(! number_is_unknown(d_fall)); - assert(! number_is_unknown(d_decay)); - - fprintf(vvp_out, "L_%p .delay %u " - "(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") L_%p/d;\n", - cptr, dly_width, - get_number_immediate64(d_rise), - get_number_immediate64(d_fall), - get_number_immediate64(d_decay), cptr); - - } else { - ivl_signal_t sig; - // We do not currently support calculating the decay - // from the rise and fall variable delays. - assert(d_decay != 0); - assert(ivl_expr_type(d_rise) == IVL_EX_SIGNAL); - assert(ivl_expr_type(d_fall) == IVL_EX_SIGNAL); - assert(ivl_expr_type(d_decay) == IVL_EX_SIGNAL); - - fprintf(vvp_out, "L_%p .delay %u L_%p/d", - cptr, dly_width, cptr); - - sig = ivl_expr_signal(d_rise); - assert(ivl_signal_dimensions(sig) == 0); - fprintf(vvp_out, ", v%p_0", sig); - - sig = ivl_expr_signal(d_fall); - assert(ivl_signal_dimensions(sig) == 0); - fprintf(vvp_out, ", v%p_0", sig); - - sig = ivl_expr_signal(d_decay); - assert(ivl_signal_dimensions(sig) == 0); - fprintf(vvp_out, ", v%p_0;\n", sig); - } - - snprintf(tmp, sizeof tmp, "L_%p", cptr); - result = strdup(tmp); - - } else { - char tmp[64]; - fprintf(vvp_out, "L_%p .functor BUFT 1, %s, " - "C4<0>, C4<0>, C4<0>;\n", cptr, result); - free(result); - - snprintf(tmp, sizeof tmp, "L_%p", cptr); - result = strdup(tmp); + draw_delay(cptr, dly_width, 0, d_rise, d_fall, d_decay); + dly = "/d"; } + fprintf(vvp_out, "L_%p%s .functor BUFT 1, %s, C4<0>, C4<0>, C4<0>;\n", + cptr, dly, result); + free(result); - return result; + snprintf(tmp, sizeof tmp, "L_%p", cptr); + return strdup(tmp); } lpm = ivl_nexus_ptr_lpm(nptr); @@ -711,6 +662,8 @@ static void draw_net_input_x(ivl_nexus_t nex, tmp += strlen(tmp); switch (res) { case IVL_SIT_TRI: + case IVL_SIT_TRIAND: + case IVL_SIT_TRIOR: case IVL_SIT_UWIRE: for (jdx = 0 ; jdx < wid ; jdx += 1) *tmp++ = 'z'; diff --git a/tgt-vvp/draw_switch.c b/tgt-vvp/draw_switch.c index b53d214b0..01a6f30a5 100644 --- a/tgt-vvp/draw_switch.c +++ b/tgt-vvp/draw_switch.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2010,2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2008-2016 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 @@ -38,18 +38,6 @@ void draw_switch_in_scope(ivl_switch_t sw) ivl_expr_t fall_exp = ivl_switch_delay(sw, 1); ivl_expr_t decay_exp= ivl_switch_delay(sw, 2); - if ((rise_exp || fall_exp || decay_exp) && - (!number_is_immediate(rise_exp, 64, 0) || - number_is_unknown(rise_exp) || - !number_is_immediate(fall_exp, 64, 0) || - number_is_unknown(fall_exp) || - !number_is_immediate(decay_exp, 64, 0) || - number_is_unknown(decay_exp))) { - fprintf(stderr, "%s:%u: error: Invalid tranif delay expression.\n", - ivl_switch_file(sw), ivl_switch_lineno(sw)); - vvp_errors += 1; - } - island = ivl_switch_island(sw); if (ivl_island_flag_test(island, 0) == 0) draw_tran_island(island); @@ -67,24 +55,18 @@ void draw_switch_in_scope(ivl_switch_t sw) char str_e_buf[4 + 2*sizeof(void*)]; if (enable && rise_exp) { - assert(fall_exp && decay_exp); - /* If the enable has a delay, then generate a .delay node to delay the input by the specified amount. Do the delay outside of the island so that the island processing doesn't have to deal with it. */ const char*raw = draw_net_input(enable); + draw_delay(sw, 1, raw, rise_exp, fall_exp, decay_exp); + snprintf(str_e_buf, sizeof str_e_buf, "p%p", sw); str_e = str_e_buf; - fprintf(vvp_out, "%s/d .delay 1 " - "(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") %s;\n", - str_e, get_number_immediate64(rise_exp), - get_number_immediate64(fall_exp), - get_number_immediate64(decay_exp), raw); - - fprintf(vvp_out, "%s .import I%p, %s/d;\n", str_e, island, str_e); + fprintf(vvp_out, "%s .import I%p, L_%p;\n", str_e, island, sw); } else if (enable) { str_e = draw_island_net_input(island, enable); diff --git a/tgt-vvp/stmt_assign.c b/tgt-vvp/stmt_assign.c index 7a788e807..94503fe41 100644 --- a/tgt-vvp/stmt_assign.c +++ b/tgt-vvp/stmt_assign.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2016 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 @@ -183,17 +183,17 @@ static void get_vec_from_lval(ivl_statement_t net, struct vec_slice_info*slices) unsigned wid = ivl_stmt_lwidth(net); cur_bit = 0; - for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) { + for (lidx = ivl_stmt_lvals(net) ; lidx > 0 ; lidx -= 1) { ivl_lval_t lval; unsigned bit_limit = wid - cur_bit; - lval = ivl_stmt_lval(net, lidx); + lval = ivl_stmt_lval(net, lidx-1); if (bit_limit > ivl_lval_width(lval)) bit_limit = ivl_lval_width(lval); - get_vec_from_lval_slice(lval, slices+lidx, bit_limit); - if (lidx > 0) { + get_vec_from_lval_slice(lval, slices+lidx-1, bit_limit); + if (cur_bit > 0) { fprintf(vvp_out, " %%concat/vec4;\n"); } @@ -555,28 +555,130 @@ static int show_stmt_assign_vector(ivl_statement_t net) return 0; } -/* - * This function assigns a value to a real variable. This is destined - * for /dev/null when typed ivl_signal_t takes over all the real - * variable support. - */ -static int show_stmt_assign_sig_real(ivl_statement_t net) +enum real_lval_type_e { + REAL_NO_TYPE = 0, + REAL_SIMPLE_WORD, + REAL_MEMORY_WORD_STATIC, + REAL_MEMORY_WORD_DYNAMIC +}; + +struct real_lval_info { + enum real_lval_type_e type; + + union { + struct { + unsigned long use_word; + } simple_word; + + struct { + unsigned long use_word; + } memory_word_static; + + struct { + /* Index reg that holds the memory word index */ + int word_idx_reg; + /* Stored x/non-x flag */ + unsigned x_flag; + } memory_word_dynamic; + } u_; +}; + +static void get_real_from_lval(ivl_lval_t lval, struct real_lval_info*slice) +{ + ivl_signal_t sig = ivl_lval_sig(lval); + ivl_expr_t word_ix = ivl_lval_idx(lval); + unsigned long use_word = 0; + + /* If the word index is a constant expression, then evaluate + it to select the word, and pay no further heed to the + expression itself. */ + if (word_ix && number_is_immediate(word_ix, IMM_WID, 0)) { + assert(! number_is_unknown(word_ix)); + use_word = get_number_immediate(word_ix); + word_ix = 0; + } + + if (ivl_signal_dimensions(sig)==0 && word_ix==0) { + + slice->type = REAL_SIMPLE_WORD; + slice->u_.simple_word.use_word = use_word; + fprintf(vvp_out, " %%load/real v%p_%lu;\n", sig, use_word); + + } else if (ivl_signal_dimensions(sig) > 0 && word_ix == 0) { + + slice->type = REAL_MEMORY_WORD_STATIC; + slice->u_.memory_word_static.use_word = use_word; + if (use_word < ivl_signal_array_count(sig)) { + fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", + use_word); + fprintf(vvp_out, " %%load/reala v%p, 3;\n", sig); + } else { + fprintf(vvp_out, " %%pushi/real 0, 0;\n"); + } + + } else if (ivl_signal_dimensions(sig) > 0 && word_ix != 0) { + + slice->type = REAL_MEMORY_WORD_DYNAMIC; + + slice->u_.memory_word_dynamic.word_idx_reg = allocate_word(); + slice->u_.memory_word_dynamic.x_flag = allocate_flag(); + + draw_eval_expr_into_integer(word_ix, slice->u_.memory_word_dynamic.word_idx_reg); + fprintf(vvp_out, " %%flag_mov %u, 4;\n", slice->u_.memory_word_dynamic.x_flag); + fprintf(vvp_out, " %%load/reala v%p, %d;\n", sig, slice->u_.memory_word_dynamic.word_idx_reg); + + } else { + assert(0); + } +} + +static void put_real_to_lval(ivl_lval_t lval, struct real_lval_info*slice) +{ + ivl_signal_t sig = ivl_lval_sig(lval); + + switch (slice->type) { + default: + fprintf(vvp_out, " ; XXXX slice->type=%d\n", slice->type); + assert(0); + break; + + case REAL_SIMPLE_WORD: + fprintf(vvp_out, " %%store/real v%p_%lu;\n", + sig, slice->u_.simple_word.use_word); + break; + + case REAL_MEMORY_WORD_STATIC: + if (slice->u_.memory_word_static.use_word < ivl_signal_array_count(sig)) { + int word_idx = allocate_word(); + fprintf(vvp_out," %%flag_set/imm 4, 0;\n"); + fprintf(vvp_out," %%ix/load %d, %lu, 0;\n", word_idx, slice->u_.memory_word_static.use_word); + fprintf(vvp_out," %%store/reala v%p, %d;\n", sig, word_idx); + clr_word(word_idx); + } else { + fprintf(vvp_out," ; Skip this slice write to v%p [%lu]\n", sig, slice->u_.memory_word_static.use_word); + } + break; + + case REAL_MEMORY_WORD_DYNAMIC: + fprintf(vvp_out, " %%flag_mov 4, %u;\n", slice->u_.memory_word_dynamic.x_flag); + fprintf(vvp_out, " %%store/reala v%p, %d;\n", sig, slice->u_.memory_word_dynamic.word_idx_reg); + clr_word(slice->u_.memory_word_dynamic.word_idx_reg); + clr_flag(slice->u_.memory_word_dynamic.x_flag); + break; + + } +} + +static void store_real_to_lval(ivl_lval_t lval) { - ivl_lval_t lval; ivl_signal_t var; - assert(ivl_stmt_opcode(net) == 0); - - draw_eval_real(ivl_stmt_rval(net)); - - assert(ivl_stmt_lvals(net) == 1); - lval = ivl_stmt_lval(net, 0); var = ivl_lval_sig(lval); assert(var != 0); if (ivl_signal_dimensions(var) == 0) { fprintf(vvp_out, " %%store/real v%p_0;\n", var); - return 0; + return; } // For now, only support 1-dimensional arrays. @@ -612,7 +714,66 @@ static int show_stmt_assign_sig_real(ivl_statement_t net) } clr_word(word_ix); +} +/* + * This function assigns a value to a real variable. This is destined + * for /dev/null when typed ivl_signal_t takes over all the real + * variable support. + */ +static int show_stmt_assign_sig_real(ivl_statement_t net) +{ + struct real_lval_info*slice = 0; + ivl_lval_t lval; + + assert(ivl_stmt_lvals(net) == 1); + lval = ivl_stmt_lval(net, 0); + + /* If this is a compressed assignment, then get the contents + of the l-value. We need this value as part of the r-value + calculation. */ + if (ivl_stmt_opcode(net) != 0) { + fprintf(vvp_out, " ; show_stmt_assign_real: Get l-value for compressed %c= operand\n", ivl_stmt_opcode(net)); + slice = calloc(1, sizeof(struct real_lval_info)); + get_real_from_lval(lval, slice); + } + + draw_eval_real(ivl_stmt_rval(net)); + + switch (ivl_stmt_opcode(net)) { + case 0: + store_real_to_lval(lval); + if (slice) free(slice); + return 0; + + case '+': + fprintf(vvp_out, " %%add/wr;\n"); + break; + + case '-': + fprintf(vvp_out, " %%sub/wr;\n"); + break; + + case '*': + fprintf(vvp_out, " %%mul/wr;\n"); + break; + + case '/': + fprintf(vvp_out, " %%div/wr;\n"); + break; + + case '%': + fprintf(vvp_out, " %%mod/wr;\n"); + break; + + default: + fprintf(vvp_out, "; UNSUPPORTED ASSIGNMENT OPCODE: %c\n", ivl_stmt_opcode(net)); + assert(0); + break; + } + + put_real_to_lval(lval, slice); + free(slice); return 0; } diff --git a/tgt-vvp/vvp_priv.h b/tgt-vvp/vvp_priv.h index 5255058cc..85a26021d 100644 --- a/tgt-vvp/vvp_priv.h +++ b/tgt-vvp/vvp_priv.h @@ -256,6 +256,13 @@ extern void show_stmt_file_line(ivl_statement_t net, const char*desc); extern int test_immediate_vec4_ok(ivl_expr_t expr); extern void draw_immediate_vec4(ivl_expr_t expr, const char*opcode); +/* + * Draw a delay statement. + */ +extern void draw_delay(void*ptr, unsigned wid, const char*input, + ivl_expr_t rise_exp, ivl_expr_t fall_exp, + ivl_expr_t decay_exp); + /* * These functions manage word register allocation. */ diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index 571c8b100..435184b7f 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -225,6 +225,15 @@ static void assign_to_lvector(ivl_lval_t lval, const unsigned long use_word = 0; + if (ivl_signal_type(sig) == IVL_SIT_UWIRE) { + fprintf(stderr, "%s:%u: tgt-vvp sorry: V10 does not support " + "mixed continuous and non-blocking assignments to " + "different parts of the same vector (%s).\n", + ivl_signal_file(sig), ivl_signal_lineno(sig), + ivl_signal_basename(sig)); + vvp_errors += 1; + } + // Detect the case that this is actually a non-blocking assign // to an array word. In that case, run off somewhere else to // deal with it. @@ -2298,6 +2307,7 @@ int draw_process(ivl_process_t net, void*x) ivl_scope_t scope = ivl_process_scope(net); ivl_statement_t stmt = ivl_process_stmt(net); + int init_flag = 0; int push_flag = 0; (void)x; /* Parameter is not used. */ @@ -2306,6 +2316,12 @@ int draw_process(ivl_process_t net, void*x) ivl_attribute_t attr = ivl_process_attr_val(net, idx); + if (strcmp(attr->key, "_ivl_schedule_init") == 0) { + + init_flag = 1; + + } + if (strcmp(attr->key, "_ivl_schedule_push") == 0) { push_flag = 1; @@ -2349,7 +2365,9 @@ int draw_process(ivl_process_t net, void*x) case IVL_PR_INITIAL: case IVL_PR_ALWAYS: - if (push_flag) { + if (init_flag) { + fprintf(vvp_out, " .thread T_%u, $init;\n", thread_count); + } else if (push_flag) { fprintf(vvp_out, " .thread T_%u, $push;\n", thread_count); } else { fprintf(vvp_out, " .thread T_%u;\n", thread_count); diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 6a333d926..80534c429 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -669,7 +669,7 @@ static void draw_net_in_scope(ivl_signal_t sig) swapped ? last : first ); } - fprintf(vvp_out, "v%p_%u .alias%s v%p %u, %d %d, " + fprintf(vvp_out, "v%p_%u .net%s v%p %u, %d %d, " "v%p_%u; Alias to %s\n", sig, iword, datatype_flag, sig, iword, msb, lsb, nex_data->net, nex_data->net_word, @@ -717,7 +717,7 @@ static unsigned need_delay(ivl_net_logic_t lptr) /* * Draw the appropriate delay statement. Returns zero if there is not a delay. */ -static void draw_delay(ivl_net_logic_t lptr) +static void draw_logic_delay(ivl_net_logic_t lptr) { ivl_expr_t rise_exp = ivl_logic_delay(lptr, 0); ivl_expr_t fall_exp = ivl_logic_delay(lptr, 1); @@ -730,49 +730,7 @@ static void draw_delay(ivl_net_logic_t lptr) delay_wid = 0; } - /* If the delays are all constants then process them here. */ - if (number_is_immediate(rise_exp, 64, 0) && - number_is_immediate(fall_exp, 64, 0) && - number_is_immediate(decay_exp, 64, 0)) { - - assert(! number_is_unknown(rise_exp)); - assert(! number_is_unknown(fall_exp)); - assert(! number_is_unknown(decay_exp)); - - fprintf(vvp_out, "L_%p .delay %u " - "(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") L_%p/d;\n", - lptr, delay_wid, - get_number_immediate64(rise_exp), - get_number_immediate64(fall_exp), - get_number_immediate64(decay_exp), lptr); - /* For a variable delay we indicate only two delays by setting the - * decay time to zero. */ - } else { - ivl_signal_t sig; - assert(ivl_expr_type(rise_exp) == IVL_EX_SIGNAL); - assert(ivl_expr_type(fall_exp) == IVL_EX_SIGNAL); - assert((decay_exp == 0) || - (ivl_expr_type(decay_exp) == IVL_EX_SIGNAL)); - - fprintf(vvp_out, "L_%p .delay %u L_%p/d", lptr, delay_wid, lptr); - - sig = ivl_expr_signal(rise_exp); - assert(ivl_signal_dimensions(sig) == 0); - fprintf(vvp_out, ", %s", draw_net_input(ivl_signal_nex(sig,0))); - - sig = ivl_expr_signal(fall_exp); - assert(ivl_signal_dimensions(sig) == 0); - fprintf(vvp_out, ", %s", draw_net_input(ivl_signal_nex(sig,0))); - - if (decay_exp) { - sig = ivl_expr_signal(decay_exp); - assert(ivl_signal_dimensions(sig) == 0); - fprintf(vvp_out, ", %s;\n", - draw_net_input(ivl_signal_nex(sig,0))); - } else { - fprintf(vvp_out, ", 0;\n"); - } - } + draw_delay(lptr, delay_wid, 0, rise_exp, fall_exp, decay_exp); } static void draw_udp_def(ivl_udp_t udp) @@ -908,7 +866,7 @@ static void draw_udp_in_scope(ivl_net_logic_t lptr) fprintf(vvp_out, ";\n"); /* Generate a delay when needed. */ - if (need_delay_flag) draw_delay(lptr); + if (need_delay_flag) draw_logic_delay(lptr); } static void draw_logic_in_scope(ivl_net_logic_t lptr) @@ -1112,7 +1070,7 @@ static void draw_logic_in_scope(ivl_net_logic_t lptr) free(input_strings); /* Generate a delay when needed. */ - if (need_delay_flag) draw_delay(lptr); + if (need_delay_flag) draw_logic_delay(lptr); } static void draw_event_in_scope(ivl_event_t obj) @@ -1312,20 +1270,8 @@ static const char* draw_lpm_output_delay(ivl_lpm_t net, ivl_variable_type_t dt) const char*dly = ""; if (d_rise != 0) { - assert(number_is_immediate(d_rise, 64, 0)); - assert(number_is_immediate(d_fall, 64, 0)); - assert(number_is_immediate(d_decay, 64, 0)); - - assert(! number_is_unknown(d_rise)); - assert(! number_is_unknown(d_fall)); - assert(! number_is_unknown(d_decay)); - + draw_delay(net, width, 0, d_rise, d_fall, d_decay); dly = "/d"; - fprintf(vvp_out, "L_%p .delay %u (%" PRIu64 ",%" PRIu64 ",%" PRIu64 ")" - " L_%p/d;\n", net, width, - get_number_immediate64(d_rise), - get_number_immediate64(d_fall), - get_number_immediate64(d_decay), net); } return dly; diff --git a/vpi/fstapi.c b/vpi/fstapi.c index 18e06735c..18d38ec99 100644 --- a/vpi/fstapi.c +++ b/vpi/fstapi.c @@ -3074,6 +3074,7 @@ uint32_t *rvat_chain_table_lengths; uint64_t rvat_vc_maxhandle; off_t rvat_vc_start; uint32_t *rvat_sig_offs; +int rvat_packtype; uint32_t rvat_chain_len; unsigned char *rvat_chain_mem; @@ -5886,6 +5887,7 @@ if(frame_uclen == frame_clen) xc->rvat_vc_maxhandle = fstReaderVarint64(xc->f); xc->rvat_vc_start = ftello(xc->f); /* points to '!' character */ +xc->rvat_packtype = fgetc(xc->f); #ifdef FST_DEBUG fprintf(stderr, "\tframe_uclen: %d, frame_clen: %d, frame_maxhandle: %d\n", @@ -5910,37 +5912,84 @@ xc->rvat_chain_table_lengths = calloc((xc->rvat_vc_maxhandle+1), sizeof(uint32_t pnt = chain_cmem; idx = 0; pval = 0; -do + +if(sectype == FST_BL_VCDATA_DYN_ALIAS2) { - int skiplen; - uint64_t val = fstGetVarint32(pnt, &skiplen); - - if(!val) - { - pnt += skiplen; - val = fstGetVarint32(pnt, &skiplen); - xc->rvat_chain_table[idx] = 0; - xc->rvat_chain_table_lengths[idx] = -val; - idx++; - } - else - if(val&1) - { - pval = xc->rvat_chain_table[idx] = pval + (val >> 1); - if(idx) { xc->rvat_chain_table_lengths[pidx] = pval - xc->rvat_chain_table[pidx]; } - pidx = idx++; - } - else - { - fstHandle loopcnt = val >> 1; - for(i=0;irvat_chain_table[idx++] = 0; + int64_t shval = fstGetSVarint64(pnt, &skiplen) >> 1; + if(shval > 0) + { + pval = xc->rvat_chain_table[idx] = pval + shval; + if(idx) { xc->rvat_chain_table_lengths[pidx] = pval - xc->rvat_chain_table[pidx]; } + pidx = idx++; + } + else if(shval < 0) + { + xc->rvat_chain_table[idx] = 0; /* need to explicitly zero as calloc above might not run */ + xc->rvat_chain_table_lengths[idx] = prev_alias = shval; /* because during this loop iter would give stale data! */ + idx++; + } + else + { + xc->rvat_chain_table[idx] = 0; /* need to explicitly zero as calloc above might not run */ + xc->rvat_chain_table_lengths[idx] = prev_alias; /* because during this loop iter would give stale data! */ + idx++; + } } - } - - pnt += skiplen; - } while (pnt != (chain_cmem + chain_clen)); + else + { + uint64_t val = fstGetVarint32(pnt, &skiplen); + + fstHandle loopcnt = val >> 1; + for(i=0;irvat_chain_table[idx++] = 0; + } + } + + pnt += skiplen; + } while (pnt != (chain_cmem + chain_clen)); + } + else + { + do + { + int skiplen; + uint64_t val = fstGetVarint32(pnt, &skiplen); + + if(!val) + { + pnt += skiplen; + val = fstGetVarint32(pnt, &skiplen); + xc->rvat_chain_table[idx] = 0; + xc->rvat_chain_table_lengths[idx] = -val; + idx++; + } + else + if(val&1) + { + pval = xc->rvat_chain_table[idx] = pval + (val >> 1); + if(idx) { xc->rvat_chain_table_lengths[pidx] = pval - xc->rvat_chain_table[pidx]; } + pidx = idx++; + } + else + { + fstHandle loopcnt = val >> 1; + for(i=0;irvat_chain_table[idx++] = 0; + } + } + + pnt += skiplen; + } while (pnt != (chain_cmem + chain_clen)); + } free(chain_cmem); xc->rvat_chain_table[idx] = indx_pos - xc->rvat_vc_start; @@ -6004,10 +6053,20 @@ if(!xc->rvat_chain_mem) unsigned char *mc = malloc(xc->rvat_chain_table_lengths[facidx]); unsigned long destlen = xc->rvat_chain_len; unsigned long sourcelen = xc->rvat_chain_table_lengths[facidx]; - int rc; + int rc = Z_OK; fstFread(mc, xc->rvat_chain_table_lengths[facidx], 1, xc->f); - rc = uncompress(mu, &destlen, mc, sourcelen); + + switch(xc->rvat_packtype) + { + case '4': rc = (destlen == (unsigned long)LZ4_decompress_safe_partial((char *)mc, (char *)mu, sourcelen, destlen, destlen)) ? Z_OK : Z_DATA_ERROR; + break; + case 'F': fastlz_decompress(mc, sourcelen, mu, destlen); /* rc appears unreliable */ + break; + default: rc = uncompress(mu, &destlen, mc, sourcelen); + break; + } + free(mc); if(rc != Z_OK) diff --git a/vpi/lxt2_write.c b/vpi/lxt2_write.c index b50aa2359..1ebf3e179 100644 --- a/vpi/lxt2_write.c +++ b/vpi/lxt2_write.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2012 Tony Bybell. + * Copyright (c) 2003-2016 Tony Bybell. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -1029,12 +1029,12 @@ for(i=len;i>0;i--) if(!i) { - sprintf(tname, "%s_%03d.lxt", lt->lxtname, ++lt->break_number); + sprintf(tname, "%s_%03u.lxt", lt->lxtname, ++lt->break_number); } else { memcpy(tname, lt->lxtname, i); - sprintf(tname+i, "_%03d.lxt", ++lt->break_number); + sprintf(tname+i, "_%03u.lxt", ++lt->break_number); } f2 = fopen(tname, "wb"); diff --git a/vpi/sys_display.c b/vpi/sys_display.c index a7a34dfa7..b5e0737c8 100644 --- a/vpi/sys_display.c +++ b/vpi/sys_display.c @@ -1971,12 +1971,7 @@ static PLI_INT32 sys_printtimescale_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) item = vpi_scan(argv); vpi_free_object(argv); } - - if (vpi_get(vpiType, item) != vpiModule) { - scope = vpi_handle(vpiModule, item); - } else { - scope = item; - } + scope = sys_func_module(item); vpi_printf("Time scale of (%s) is ", vpi_get_str(vpiFullName, item)); vpi_printf("%s / ", pts_convert(vpi_get(vpiTimeUnit, scope))); diff --git a/vpi/sys_icarus.c b/vpi/sys_icarus.c index 40d8c52e3..7a1037572 100644 --- a/vpi/sys_icarus.c +++ b/vpi/sys_icarus.c @@ -60,7 +60,7 @@ static PLI_INT32 missing_optional_compiletf(ICARUS_VPI_CONST PLI_BYTE8* name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpi_printf("SORRY: %s:%d: %s() is not available in Icarus verilog.\n", + vpi_printf("SORRY: %s:%d: %s() is not available in Icarus Verilog.\n", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh), name); vpi_control(vpiFinish, 1); diff --git a/vpi/sys_priv.c b/vpi/sys_priv.c index 08529cced..1d5e977bb 100644 --- a/vpi/sys_priv.c +++ b/vpi/sys_priv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2016 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 @@ -229,15 +229,18 @@ unsigned is_string_obj(vpiHandle obj) /* - * Find the enclosing module. + * Find the enclosing module. If there is no enclosing module (which can be + * the case in SystemVerilog), return the highest enclosing scope. */ vpiHandle sys_func_module(vpiHandle obj) { assert(obj); while (vpi_get(vpiType, obj) != vpiModule) { - obj = vpi_handle(vpiScope, obj); - assert(obj); + vpiHandle scope = vpi_handle(vpiScope, obj); + if (scope == 0) + break; + obj = scope; } return obj; diff --git a/vvp/arith.cc b/vvp/arith.cc index 7fe9ebcef..c67846d9f 100644 --- a/vvp/arith.cc +++ b/vvp/arith.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -26,13 +26,13 @@ # include vvp_arith_::vvp_arith_(unsigned wid) -: wid_(wid), x_val_(wid) +: wid_(wid), op_a_(wid), op_b_(wid), x_val_(wid) { - for (unsigned idx = 0 ; idx < wid ; idx += 1) + for (unsigned idx = 0 ; idx < wid ; idx += 1) { + op_a_ .set_bit(idx, BIT4_Z); + op_b_ .set_bit(idx, BIT4_Z); x_val_.set_bit(idx, BIT4_X); - - op_a_ = x_val_; - op_b_ = x_val_; + } } void vvp_arith_::dispatch_operand_(vvp_net_ptr_t ptr, vvp_vector4_t bit) @@ -51,6 +51,12 @@ void vvp_arith_::dispatch_operand_(vvp_net_ptr_t ptr, vvp_vector4_t bit) } } +void vvp_arith_::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} vvp_arith_abs::vvp_arith_abs() { @@ -82,6 +88,13 @@ void vvp_arith_abs::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, ptr.ptr()->send_vec4(out, 0); } +void vvp_arith_abs::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} + void vvp_arith_abs::recv_real(vvp_net_ptr_t ptr, double bit, vvp_context_t) { @@ -121,6 +134,13 @@ void vvp_arith_cast_real::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, ptr.ptr()->send_real(val, 0); } +void vvp_arith_cast_real::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} + vvp_arith_cast_vec2::vvp_arith_cast_vec2(unsigned wid) : wid_(wid) { @@ -143,6 +163,13 @@ void vvp_arith_cast_vec2::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, ptr.ptr()->send_vec4(vector2_to_vector4(tmp,wid_), 0); } +void vvp_arith_cast_vec2::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} + // Division vvp_arith_div::vvp_arith_div(unsigned wid, bool signed_flag) diff --git a/vvp/arith.h b/vvp/arith.h index 612f060b9..5c6945526 100644 --- a/vvp/arith.h +++ b/vvp/arith.h @@ -1,7 +1,7 @@ #ifndef IVL_arith_H #define IVL_arith_H /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -37,6 +37,10 @@ class vvp_arith_ : public vvp_net_fun_t { public: explicit vvp_arith_(unsigned wid); + void recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx); + protected: void dispatch_operand_(vvp_net_ptr_t ptr, vvp_vector4_t bit); @@ -59,6 +63,10 @@ class vvp_arith_abs : public vvp_net_fun_t { void recv_real(vvp_net_ptr_t ptr, double bit, vvp_context_t); + void recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx); + private: }; @@ -82,6 +90,10 @@ class vvp_arith_cast_real : public vvp_net_fun_t { void recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, vvp_context_t); + void recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx); + private: bool signed_; }; @@ -96,6 +108,10 @@ class vvp_arith_cast_vec2 : public vvp_net_fun_t { void recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, vvp_context_t); + void recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx); + private: unsigned wid_; }; diff --git a/vvp/bufif.cc b/vvp/bufif.cc index 9a1bc8df0..25469936e 100644 --- a/vvp/bufif.cc +++ b/vvp/bufif.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -81,3 +81,10 @@ void vvp_fun_bufif::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, ptr.ptr()->send_vec8(out); } + +void vvp_fun_bufif::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} diff --git a/vvp/bufif.h b/vvp/bufif.h index 8cb3bc57e..1bbf79544 100644 --- a/vvp/bufif.h +++ b/vvp/bufif.h @@ -1,7 +1,7 @@ #ifndef IVL_bufif_H #define IVL_bufif_H /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -40,6 +40,10 @@ class vvp_fun_bufif : public vvp_net_fun_t { void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, vvp_context_t); + void recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx); + private: vvp_vector4_t bit_; vvp_vector4_t en_; diff --git a/vvp/compile.cc b/vvp/compile.cc index 3bf1d7474..ef1e49bf9 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -1935,7 +1935,9 @@ void compile_thread(char*start_sym, char*flag) vthread_t thr = vthread_new(pc, vpip_peek_current_scope()); - if (flag && (strcmp(flag,"$final") == 0)) + if (flag && (strcmp(flag,"$init") == 0)) + schedule_init_vthread(thr); + else if (flag && (strcmp(flag,"$final") == 0)) schedule_final_vthread(thr); else schedule_vthread(thr, 0, push_flag); diff --git a/vvp/delay.cc b/vvp/delay.cc index 5bda67ad8..fbb83236d 100644 --- a/vvp/delay.cc +++ b/vvp/delay.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2014 Stephen Williams + * Copyright (c) 2005-2016 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -332,6 +332,13 @@ void vvp_fun_delay::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, } } +void vvp_fun_delay::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} + /* See the recv_vec4 comment above. */ void vvp_fun_delay::recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit) { @@ -397,6 +404,12 @@ void vvp_fun_delay::recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit) } } +void vvp_fun_delay::recv_vec8_pv(vvp_net_ptr_t ptr, const vvp_vector8_t&bit, + unsigned base, unsigned wid, unsigned vwid) +{ + recv_vec8_pv_(ptr, bit, base, wid, vwid); +} + void vvp_fun_delay::recv_real(vvp_net_ptr_t port, double bit, vvp_context_t) { diff --git a/vvp/delay.h b/vvp/delay.h index 6a3815ce3..b565a434f 100644 --- a/vvp/delay.h +++ b/vvp/delay.h @@ -1,7 +1,7 @@ #ifndef IVL_delay_H #define IVL_delay_H /* - * Copyright 2005-2014 Stephen Williams + * Copyright 2005-2016 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -99,6 +99,12 @@ class vvp_fun_delay : public vvp_net_fun_t, private vvp_gen_event_s { vvp_context_t); //void recv_long(vvp_net_ptr_t port, long bit); + void recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx); + void recv_vec8_pv(vvp_net_ptr_t ptr, const vvp_vector8_t&bit, + unsigned base, unsigned wid, unsigned vwid); + private: virtual void run_run(); diff --git a/vvp/dff.cc b/vvp/dff.cc index 2df5d0878..b77cbf547 100644 --- a/vvp/dff.cc +++ b/vvp/dff.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2005-2016 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 @@ -103,6 +103,13 @@ void vvp_dff::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, } } +void vvp_dff::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} + /* * The recv_async functions respond to the asynchronous * set/clear input by propagating the desired output. diff --git a/vvp/dff.h b/vvp/dff.h index d069a68ba..17924af08 100644 --- a/vvp/dff.h +++ b/vvp/dff.h @@ -1,7 +1,7 @@ #ifndef IVL_dff_H #define IVL_dff_H /* - * Copyright (c) 2005-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2005-2016 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 @@ -44,6 +44,10 @@ class vvp_dff : public vvp_net_fun_t { void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, vvp_context_t); + void recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx); + private: virtual void recv_async(vvp_net_ptr_t port); diff --git a/vvp/extend.cc b/vvp/extend.cc index cb484ea83..ea7c8419b 100644 --- a/vvp/extend.cc +++ b/vvp/extend.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2010 Stephen Williams + * Copyright (c) 2005-2016 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -51,3 +51,10 @@ void vvp_fun_extend_signed::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bi port.ptr()->send_vec4(res, 0); } + +void vvp_fun_extend_signed::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} diff --git a/vvp/npmos.cc b/vvp/npmos.cc index 11b633916..1359c9691 100644 --- a/vvp/npmos.cc +++ b/vvp/npmos.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Stephen Williams (steve@icarus.com) + * Copyright (c) 2005-2016 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 @@ -43,6 +43,19 @@ void vvp_fun_pmos_::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, generate_output_(ptr); } +void vvp_fun_pmos_::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} + +void vvp_fun_pmos_::recv_vec8_pv(vvp_net_ptr_t ptr, const vvp_vector8_t&bit, + unsigned base, unsigned wid, unsigned vwid) +{ + recv_vec8_pv_(ptr, bit, base, wid, vwid); +} + void vvp_fun_pmos_::generate_output_(vvp_net_ptr_t ptr) { vvp_vector8_t out (bit_.size()); @@ -147,7 +160,18 @@ void vvp_fun_cmos_::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t &bit, generate_output_(ptr); } -#include +void vvp_fun_cmos_::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} + +void vvp_fun_cmos_::recv_vec8_pv(vvp_net_ptr_t ptr, const vvp_vector8_t&bit, + unsigned base, unsigned wid, unsigned vwid) +{ + recv_vec8_pv_(ptr, bit, base, wid, vwid); +} void vvp_fun_cmos_::generate_output_(vvp_net_ptr_t ptr) { diff --git a/vvp/npmos.h b/vvp/npmos.h index 4bbcc199e..cb6950685 100644 --- a/vvp/npmos.h +++ b/vvp/npmos.h @@ -1,7 +1,7 @@ #ifndef IVL_npmos_H #define IVL_npmos_H /* - * Copyright (c) 2005-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2005-2016 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 @@ -51,6 +51,12 @@ class vvp_fun_pmos_ : public vvp_net_fun_t { void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, vvp_context_t); + void recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx); + void recv_vec8_pv(vvp_net_ptr_t ptr, const vvp_vector8_t&bit, + unsigned base, unsigned wid, unsigned vwid); + protected: void generate_output_(vvp_net_ptr_t port); @@ -108,6 +114,12 @@ class vvp_fun_cmos_ : public vvp_net_fun_t { void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t &bit, vvp_context_t); + void recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx); + void recv_vec8_pv(vvp_net_ptr_t ptr, const vvp_vector8_t&bit, + unsigned base, unsigned wid, unsigned vwid); + protected: void generate_output_(vvp_net_ptr_t port); diff --git a/vvp/part.cc b/vvp/part.cc index 6ba40927a..a999f5a97 100644 --- a/vvp/part.cc +++ b/vvp/part.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2004-2016 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 @@ -208,6 +208,21 @@ void vvp_fun_part_pv::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, port.ptr()->send_vec4_pv(bit, base_, wid_, vwid_, context); } +void vvp_fun_part_pv::recv_vec4_pv(vvp_net_ptr_t port, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + assert(port.port() == 0); + assert(bit.size() == wid); + assert(base + wid <= vwid); + assert(vwid == wid_); + + vvp_vector4_t tmp(wid_, BIT4_Z); + tmp.set_vec(base, bit); + + port.ptr()->send_vec4_pv(tmp, base_, wid_, vwid_, ctx); +} + void vvp_fun_part_pv::recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit) { assert(port.port() == 0); diff --git a/vvp/part.h b/vvp/part.h index 90a59a225..89d0018c6 100644 --- a/vvp/part.h +++ b/vvp/part.h @@ -109,6 +109,10 @@ class vvp_fun_part_pv : public vvp_net_fun_t { void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, vvp_context_t context); + void recv_vec4_pv(vvp_net_ptr_t port, const vvp_vector4_t&bit, + unsigned, unsigned, unsigned, + vvp_context_t); + void recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit); private: diff --git a/vvp/schedule.cc b/vvp/schedule.cc index 9ed169fab..e6d06604b 100644 --- a/vvp/schedule.cc +++ b/vvp/schedule.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -760,6 +760,16 @@ void schedule_vthread(vthread_t thr, vvp_time64_t delay, bool push_flag) } } +void schedule_init_vthread(vthread_t thr) +{ + struct vthread_event_s*cur = new vthread_event_s; + + cur->thr = thr; + vthread_mark_scheduled(thr); + + schedule_init_event(cur); +} + void schedule_final_vthread(vthread_t thr) { struct vthread_event_s*cur = new vthread_event_s; @@ -939,6 +949,10 @@ extern void vpiStartOfSim(); extern void vpiPostsim(); extern void vpiNextSimTime(void); +static bool sim_at_rosync = false; +bool schedule_at_rosync(void) +{ return sim_at_rosync; } + /* * The scheduler uses this function to drain the rosync events of the * current time. The ctim object is still in the event queue, because @@ -951,6 +965,7 @@ extern void vpiNextSimTime(void); */ static void run_rosync(struct event_time_s*ctim) { + sim_at_rosync = true; while (ctim->rosync) { struct event_s*cur = ctim->rosync->next; if (cur->next == cur) { @@ -962,6 +977,7 @@ static void run_rosync(struct event_time_s*ctim) cur->run_run(); delete cur; } + sim_at_rosync = false; while (ctim->del_thr) { struct event_s*cur = ctim->del_thr->next; diff --git a/vvp/schedule.h b/vvp/schedule.h index 49db903e9..e1a465364 100644 --- a/vvp/schedule.h +++ b/vvp/schedule.h @@ -1,7 +1,7 @@ #ifndef IVL_schedule_H #define IVL_schedule_H /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -35,6 +35,8 @@ extern void schedule_vthread(vthread_t thr, vvp_time64_t delay, bool push_flag =false); +extern void schedule_init_vthread(vthread_t thr); + extern void schedule_final_vthread(vthread_t thr); /* @@ -153,6 +155,13 @@ extern void schedule_simulate(void); */ extern vvp_time64_t schedule_simtime(void); +/* + * Indicate that the simulator is running the rosync callbacks. This is + * used to prevent the callbacks from performing any write operations + * in the current simulation time slot. + */ +extern bool schedule_at_rosync(void); + /* * This function is the equivalent of the $finish system task. It * tells the simulator that simulation is done, the current thread diff --git a/vvp/substitute.cc b/vvp/substitute.cc index 04e83d460..09d516733 100644 --- a/vvp/substitute.cc +++ b/vvp/substitute.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2016 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 @@ -32,6 +32,10 @@ class vvp_fun_substitute : public vvp_net_fun_t { void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, vvp_context_t); + void recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx); + private: unsigned wid_; unsigned soff_; @@ -77,6 +81,13 @@ void vvp_fun_substitute::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, port.ptr()->send_vec4(val_, 0); } +void vvp_fun_substitute::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} + void compile_substitute(char*label, unsigned width, unsigned soff, unsigned swidth, unsigned argc, struct symb_s*argv) diff --git a/vvp/vpi_priv.cc b/vvp/vpi_priv.cc index 858286b9e..01fae5621 100644 --- a/vvp/vpi_priv.cc +++ b/vvp/vpi_priv.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2008-2016 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 @@ -1067,13 +1067,13 @@ vpiHandle vpi_put_value(vpiHandle obj, s_vpi_value*vp, vvp_time64_t dly; int scale; - if (vpi_get(vpiAutomatic, obj)) { - fprintf(stderr, "vpi error: cannot put a value with " - "a delay on automatically allocated " - "variable '%s'\n", - vpi_get_str(vpiName, obj)); - return 0; - } + if (vpi_get(vpiAutomatic, obj)) { + fprintf(stderr, "VPI error: cannot put a value with " + "a delay on automatically allocated " + "variable '%s'.\n", + vpi_get_str(vpiName, obj)); + return 0; + } assert(when != 0); @@ -1095,6 +1095,13 @@ vpiHandle vpi_put_value(vpiHandle obj, s_vpi_value*vp, break; } + if ((dly == 0) && schedule_at_rosync()) { + fprintf(stderr, "VPI error: attempted to put a value to " + "variable '%s' during a read-only synch " + "callback.\n", vpi_get_str(vpiName, obj)); + return 0; + } + vpip_put_value_event*put = new vpip_put_value_event; put->handle = obj; put->value = *vp; @@ -1132,6 +1139,13 @@ vpiHandle vpi_put_value(vpiHandle obj, s_vpi_value*vp, return 0; } + if (schedule_at_rosync()) { + fprintf(stderr, "VPI error: attempted to put a value to " + "variable '%s' during a read-only synch " + "callback.\n", vpi_get_str(vpiName, obj)); + return 0; + } + obj->vpi_put_value(vp, flags); return 0; diff --git a/vvp/vpi_signal.cc b/vvp/vpi_signal.cc index 608e41cee..243254853 100644 --- a/vvp/vpi_signal.cc +++ b/vvp/vpi_signal.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -1409,6 +1409,9 @@ static vpiHandle PV_get_handle(int code, vpiHandle ref) case vpiParent: return rfp->parent; + case vpiScope: + return vpi_handle(vpiScope, rfp->parent); + case vpiModule: return vpi_handle(vpiModule, rfp->parent); } diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index bf8edffff..864f72bbd 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -3249,6 +3249,31 @@ void vvp_net_fun_t::recv_vec4(vvp_net_ptr_t, const vvp_vector4_t&, assert(0); } +void vvp_net_fun_t::recv_vec4_pv_(vvp_net_ptr_t p, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t) +{ + // The majority of functors don't normally expect to receive part + // values, because the primary operands of an expression will be + // extended to the expression width. But in the case that a primary + // operand is a wire that is only partly driven by a single driver, + // the part value driven onto the wire propagates directly to the + // inputs of any functors connected to that wire. In this case we + // know that the remaining bits are undriven, so can simply build + // the full width value from the part we have received. + // + // Note that this case is almost certainly a bug in the user's + // code, but we still need to handle it correctly. See GitHub + // issue #99 and br_gh99*.v in the test suite for examples. + + assert(bit.size() == wid); + assert(base + wid <= vwid); + + vvp_vector4_t tmp(vwid, BIT4_Z); + tmp.set_vec(base, bit); + recv_vec4(p, tmp, 0); +} + void vvp_net_fun_t::recv_vec4_pv(vvp_net_ptr_t, const vvp_vector4_t&bit, unsigned base, unsigned wid, unsigned vwid, vvp_context_t) @@ -3264,6 +3289,19 @@ void vvp_net_fun_t::recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit) recv_vec4(port, reduce4(bit), 0); } +void vvp_net_fun_t::recv_vec8_pv_(vvp_net_ptr_t p, const vvp_vector8_t&bit, + unsigned base, unsigned wid, unsigned vwid) +{ + // This is the strength-aware version of recv_vec4_pv_. + + assert(bit.size() == wid); + assert(base + wid <= vwid); + + vvp_vector8_t tmp(vwid); + tmp.set_vec(base, bit); + recv_vec8(p, tmp); +} + void vvp_net_fun_t::recv_vec8_pv(vvp_net_ptr_t port, const vvp_vector8_t&bit, unsigned base, unsigned wid, unsigned vwid) { @@ -3331,6 +3369,12 @@ void vvp_fun_drive::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, port.ptr()->send_vec8(vvp_vector8_t(bit, drive0_, drive1_)); } +void vvp_fun_drive::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} /* **** vvp_wide_fun_* methods **** */ @@ -3426,6 +3470,13 @@ void vvp_wide_fun_t::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, core_->dispatch_vec4_from_input_(pidx, bit); } +void vvp_wide_fun_t::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} + void vvp_wide_fun_t::recv_real(vvp_net_ptr_t port, double bit, vvp_context_t) { diff --git a/vvp/vvp_net.h b/vvp/vvp_net.h index ab90e4221..a59045780 100644 --- a/vvp/vvp_net.h +++ b/vvp/vvp_net.h @@ -1,7 +1,7 @@ #ifndef IVL_vvp_net_H #define IVL_vvp_net_H /* - * Copyright (c) 2004-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2004-2016 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 @@ -1230,6 +1230,13 @@ class vvp_net_fun_t { // do something about it. virtual void force_flag(bool run_now); + protected: + void recv_vec4_pv_(vvp_net_ptr_t p, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t context); + void recv_vec8_pv_(vvp_net_ptr_t p, const vvp_vector8_t&bit, + unsigned base, unsigned wid, unsigned vwid); + public: // These objects are only permallocated. static void* operator new(std::size_t size) { return heap_.alloc(size); } static void operator delete(void*); // not implemented @@ -1470,6 +1477,9 @@ class vvp_fun_drive : public vvp_net_fun_t { vvp_context_t context); //void recv_long(vvp_net_ptr_t port, long bit); + void recv_vec4_pv(vvp_net_ptr_t port, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t); private: unsigned char drive0_; unsigned char drive1_; @@ -1490,6 +1500,9 @@ class vvp_fun_extend_signed : public vvp_net_fun_t { void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, vvp_context_t context); + void recv_vec4_pv(vvp_net_ptr_t port, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t); private: unsigned width_; }; @@ -1571,6 +1584,10 @@ class vvp_wide_fun_t : public vvp_net_fun_t { void recv_real(vvp_net_ptr_t port, double bit, vvp_context_t context); + void recv_vec4_pv(vvp_net_ptr_t p, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t context); + private: vvp_wide_fun_core*core_; unsigned port_base_; diff --git a/vvp/vvp_net_sig.h b/vvp/vvp_net_sig.h index d9ac0c3ae..893b28b39 100644 --- a/vvp/vvp_net_sig.h +++ b/vvp/vvp_net_sig.h @@ -393,7 +393,7 @@ class vvp_fun_signal_object_aa : public vvp_fun_signal_object, public automatic_ /* vvp_wire * The vvp_wire is different from vvp_variable objects in that it * exists only as a filter. The vvp_wire class tree is for - * implementing verilog wires/nets (as opposed to regs/variables). + * implementing Verilog wires/nets (as opposed to regs/variables). * * vvp_vpi_callback * |