diff --git a/BUGS.txt b/BUGS.txt index 14e39b774..3d201b240 100644 --- a/BUGS.txt +++ b/BUGS.txt @@ -116,13 +116,17 @@ programs. RESEARCHING EXISTING/PAST BUGS, AND FILING REPORTS -The URL is the main -bug tracking system. Once you believe you have found a bug, you may -browse the bugs database for existing bugs that may be related to -yours. You might find that your bug has already been fixed in a later -release or snapshot. If that's the case, then you are set. Also, -consider if you are reporting a bug or really asking for a new -feature, and use the appropriate tracker. +The URL is the main +bug tracking system, although some users have reported bugs at +. Once you believe +you have found a bug, you may browse the bugs database for existing +bugs that may be related to yours. You might find that your bug has +already been fixed in a later release or snapshot. If that's the case, +then you are set. Also, consider if you are reporting a bug or really +asking for a new feature, and use the appropriate tracker. + + system (although you will also find bug rep + The bug database supports basic keyword searches, and you can optionally limit your search to active bugs, or fixed bugs. You may @@ -145,7 +149,7 @@ version from git. Please see the developer documentation for more detailed instructions -- . When you make a patch, submit it to the "Patches" tracker at -. Patches added to +. Patches added to the "Patches" tracker enter the developer workflow, are checked, applied to the appropriate git branch, and are pushed. Then the tracker item is closed. @@ -158,7 +162,7 @@ clarification before applying it.) COPYRIGHT ISSUES -Icarus Verilog is Copyright (c) 1998-2008 Stephen Williams except +Icarus Verilog is Copyright (c) 1998-2018 Stephen Williams except where otherwise noted. Minor patches are covered as derivative works (or editorial comment or whatever the appropriate legal term is) and folded into the rest of ivl. However, if a submission can reasonably diff --git a/HName.cc b/HName.cc index c783d57bb..6b40eafcd 100644 --- a/HName.cc +++ b/HName.cc @@ -40,7 +40,7 @@ hname_t::hname_t(perm_string text, int num) number_[0] = num; } -hname_t::hname_t(perm_string text, vector&nums) +hname_t::hname_t(perm_string text, const vector&nums) : name_(text), number_(nums) { } diff --git a/HName.h b/HName.h index 90f0d7942..b278aae55 100644 --- a/HName.h +++ b/HName.h @@ -42,7 +42,7 @@ class hname_t { hname_t (); explicit hname_t (perm_string text); explicit hname_t (perm_string text, int num); - explicit hname_t (perm_string text, std::vector&nums); + explicit hname_t (perm_string text, const std::vector&nums); hname_t (const hname_t&that); ~hname_t(); @@ -56,12 +56,12 @@ class hname_t { size_t has_numbers() const; int peek_number(size_t idx) const; + const std::vector&peek_numbers() const; private: perm_string name_; - // If the number is anything other than INT_MIN, then this is - // the numeric part of the name. Otherwise, it is not part of - // the name at all. + // If this vector has size, then the numbers all together make + // up part of the hierarchical name. std::vector number_; private: // not implemented @@ -82,6 +82,11 @@ inline int hname_t::peek_number(size_t idx) const return number_[idx]; } +inline const std::vector& hname_t::peek_numbers(void) const +{ + return number_; +} + inline size_t hname_t::has_numbers() const { return number_.size(); diff --git a/Makefile.in b/Makefile.in index 9e23ddaaf..d5f79a199 100644 --- a/Makefile.in +++ b/Makefile.in @@ -100,7 +100,7 @@ CTARGETFLAGS = @CTARGETFLAGS@ M = LineInfo.o StringHeap.o TT = t-dll.o t-dll-api.o t-dll-expr.o t-dll-proc.o t-dll-analog.o -FF = cprop.o nodangle.o synth.o synth2.o syn-rules.o +FF = cprop.o exposenodes.o nodangle.o synth.o synth2.o syn-rules.o O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ elab_expr.o elaborate_analog.o elab_lval.o elab_net.o \ diff --git a/Module.cc b/Module.cc index 7c69204c8..c5980637e 100644 --- a/Module.cc +++ b/Module.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2017 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,12 +32,9 @@ Module::Module(LexicalScope*parent, perm_string n) { library_flag = false; is_cell = false; + is_interface = false; program_block = false; uc_drive = UCD_NONE; - timescale_warn_done = false; - time_unit = 0; - time_precision = 0; - time_from_timescale = false; } Module::~Module() @@ -94,13 +91,15 @@ perm_string Module::get_port_name(unsigned idx) const { assert(idx < ports.size()); - if (ports[idx] == 0) { + if (ports[idx] == 0 || ports[idx]->name.str() == 0) { /* It is possible to have undeclared ports. These are ports that are skipped in the declaration, for example like so: module foo(x ,, y); The port between x and y is unnamed and thus - inaccessible to binding by name. */ - return perm_string::literal(""); + inaccessible to binding by name. Port references + that aren't simple or escaped identifiers are + also inaccessible to binding by name. */ + return perm_string::literal("unnamed"); } return ports[idx]->name; } diff --git a/Module.h b/Module.h index f142a761d..411c99f9f 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-2017 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,12 +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 the module definition. These are used at elaboration time. */ list generate_schemes; diff --git a/PDelays.cc b/PDelays.cc index 5fd72a37b..3759d39fe 100644 --- a/PDelays.cc +++ b/PDelays.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2017 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,11 +26,6 @@ # include "verinum.h" # include "netmisc.h" -bool dly_used_no_timescale = false; -bool dly_used_timescale = false; -bool display_ts_dly_warning = true; - - PDelays::PDelays() { delete_flag_ = true; @@ -80,19 +75,7 @@ static NetExpr*calculate_val(Design*des, NetScope*scope, PExpr*expr) { NetExpr*dex = elab_and_eval(des, scope, expr, -1); - /* Print a warning if we find default and `timescale based - * delays in the design, since this is likely an error. */ - if (scope->time_from_timescale()) dly_used_timescale = true; - else dly_used_no_timescale = true; - - if (display_ts_dly_warning && - dly_used_no_timescale && dly_used_timescale) { - cerr << "warning: Found both default and " - "`timescale based delays. Use" << endl; - cerr << " -Wtimescale to find the " - "module(s) with no `timescale." << endl; - display_ts_dly_warning = false; - } + check_for_inconsistent_delays(scope); /* If the delay expression is a real constant or vector constant, then evaluate it, scale it to the local time 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/PGate.h b/PGate.h index 5d9bde9f2..ee6f10f79 100644 --- a/PGate.h +++ b/PGate.h @@ -217,6 +217,8 @@ class PGModule : public PGate { // method to pass the range to the pform. void set_range(PExpr*msb, PExpr*lsb); + map attributes; + virtual void dump(ostream&out, unsigned ind =4) const; virtual void elaborate(Design*, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*sc) const; diff --git a/PScope.cc b/PScope.cc index 377142bf9..c0738cc29 100644 --- a/PScope.cc +++ b/PScope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008,2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2008-2017 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,29 @@ PWire* LexicalScope::wires_find(perm_string name) return (*cur).second; } +PScope::PScope(perm_string n, LexicalScope*parent) +: LexicalScope(parent), name_(n) +{ + time_unit = 0; + time_precision = 0; + time_unit_is_default = true; + time_prec_is_default = true; +} + +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(perm_string n) -: PScope(n) -{ + time_unit_is_local = false; + time_prec_is_local = false; } PScopeExtra::~PScopeExtra() { } - diff --git a/PScope.h b/PScope.h index 30e1fea7e..24674d6df 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-2017 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,24 @@ 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 value is + set by the `timescale directive or, in SystemVerilog, + by timeunit and timeprecision statements. */ + int time_unit, time_precision; + + /* Flags used to support warnings about timescales. */ + bool time_unit_is_default; + bool time_prec_is_default; + + bool has_explicit_timescale() const { + return !(time_unit_is_default || time_prec_is_default); + } + protected: bool elaborate_sig_wires_(Design*des, NetScope*scope) const; @@ -168,19 +194,22 @@ 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. */ std::vector classes_lexical; + /* Flags used to support warnings about timescales. */ + bool time_unit_is_local; + bool time_prec_is_local; + protected: void dump_classes_(ostream&out, unsigned indent) const; void dump_tasks_(ostream&out, unsigned indent) const; diff --git a/PTask.cc b/PTask.cc index 75e68bc22..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); @@ -41,7 +46,7 @@ void PTaskFunc::set_this(class_type_t*type, PWire*this_wire) assert(this_type_ == 0); this_type_ = type; - // Push a synthethis argument that is the "this" value. + // Push a synthesis argument that is the "this" value. if (ports_==0) ports_ = new vector; 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/README.txt b/README.txt index f47d03f5f..3ba1503db 100644 --- a/README.txt +++ b/README.txt @@ -77,10 +77,16 @@ the configure scripts. Unpack the tar-ball and cd into the verilog-######### directory (presumably that is how you got to this README) and compile the source with the commands: - + ./configure make +If you are building from git, you have to run the command below before +compile the source. This will generate the "configure" file, which is +automatically done when building from tarball. + + sh autoconf.sh + Normally, this command automatically figures out everything it needs to know. It generally works pretty well. There are a few flags to the configure script that modify its behavior: diff --git a/Statement.cc b/Statement.cc index e0ed46d24..92a87a12d 100644 --- a/Statement.cc +++ b/Statement.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2017 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 @@ -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()) @@ -289,20 +294,20 @@ PDoWhile::~PDoWhile() } PEventStatement::PEventStatement(const svector&ee) -: expr_(ee), statement_(0) +: expr_(ee), statement_(0), search_funcs_(false) { assert(expr_.count() > 0); } PEventStatement::PEventStatement(PEEvent*ee) -: expr_(1), statement_(0) +: expr_(1), statement_(0), search_funcs_(false) { expr_[0] = ee; } -PEventStatement::PEventStatement(void) -: statement_(0) +PEventStatement::PEventStatement(bool search_funcs) +: statement_(0), search_funcs_(search_funcs) { } diff --git a/Statement.h b/Statement.h index b650a03f9..30f3b8229 100644 --- a/Statement.h +++ b/Statement.h @@ -1,7 +1,7 @@ #ifndef IVL_Statement_H #define IVL_Statement_H /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2017 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 @@ -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. @@ -289,7 +292,7 @@ class PCAssign : public Statement { */ class PChainConstructor : public Statement { public: - PChainConstructor(const list&parms); + explicit PChainConstructor(const list&parms); ~PChainConstructor(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; @@ -399,8 +402,9 @@ class PEventStatement : public Statement { explicit PEventStatement(const svector&ee); explicit PEventStatement(PEEvent*ee); - // Make an @* statement. - explicit PEventStatement(void); + // Make an @* statement or make a special @* version with the items + // from functions added and ouputs removed for always_comb/latch. + explicit PEventStatement(bool search_funcs = false); ~PEventStatement(); @@ -426,6 +430,7 @@ class PEventStatement : public Statement { private: svectorexpr_; Statement*statement_; + bool search_funcs_; }; ostream& operator << (ostream&o, const PEventStatement&obj); diff --git a/aclocal.m4 b/aclocal.m4 index 99bfe9516..7a113bf44 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -223,8 +223,9 @@ AC_SUBST(strip_dynamic) # ------------- AC_DEFUN([AX_C99_STRTOD], [# On MinGW we need to jump through hoops to get a C99 compliant strtod(). + # mingw-w64 doesn't need this, and the 64-bit version doesn't support it. case "${host}" in - *-*-mingw*) + *-pc-mingw32) LDFLAGS+=" -Wl,--undefined=___strtod,--wrap,strtod,--defsym,___wrap_strtod=___strtod" ;; esac diff --git a/compiler.h b/compiler.h index cbd0e969f..6eccc8743 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-2017 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 @@ -80,6 +80,10 @@ extern unsigned recursive_mod_limit; /* Implicit definitions of wires. */ extern bool warn_implicit; +/* Warn if dimensions of port or var/net are implicitly taken from + the input/output/inout declaration. */ +extern bool warn_implicit_dimensions; + /* inherit timescales across files. */ extern bool warn_timescale; @@ -100,6 +104,9 @@ extern bool warn_sens_entire_arr; /* Warn about level-appropriate anachronisms. */ extern bool warn_anachronisms; +/* Warn about nets that are references but not driven. */ +extern bool warn_floating_nets; + /* This is true if verbose output is requested. */ extern bool verbose_flag; @@ -110,6 +117,13 @@ extern bool debug_emit; extern bool debug_synth2; extern bool debug_optimizer; +/* Ignore errors about missing modules */ +extern bool ignore_missing_modules; + +/* Treat each source file as a separate compilation unit (as defined + by SystemVerilog). */ +extern bool separate_compilation; + /* Control evaluation of functions at compile time: * 0 = only for functions in constant expressions * 1 = only for automatic functions @@ -138,7 +152,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, @@ -182,25 +197,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/config.h.in b/config.h.in index 30470a22b..8cd8ddf45 100644 --- a/config.h.in +++ b/config.h.in @@ -1,7 +1,7 @@ #ifndef IVL_config_H /* -*- c++ -*- */ #define IVL_config_H /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2015 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 @@ -55,6 +55,7 @@ # undef HAVE_FSEEKO /* And this is needed by the fst files (copied from GTKWave). */ # undef HAVE_LIBPTHREAD +# undef HAVE_REALPATH /* * Define this if you want to compile vvp with memory freeing and diff --git a/configure.in b/configure.in index 7fa9bc8e4..fb93c3674 100644 --- a/configure.in +++ b/configure.in @@ -223,6 +223,10 @@ case "${host}" in CPPFLAGS="-mieee $CPPFLAGS" CFLAGS="-mieee $CFLAGS" ;; + *-*-mingw*) + CXXFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $CXXFLAGS" + CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $CFLAGS" + ;; esac # Do some more operating system specific setup. We put the file64_support @@ -238,6 +242,9 @@ case "${host}" in esac AC_SUBST(file64_support) +# fstapi.c (from GTKWave) needs this define. +AC_CHECK_FUNCS(realpath) + # Check that these functions exist. They are mostly C99 # functions that older compilers may not yet support. AC_CHECK_FUNCS(fopen64) diff --git a/cppcheck.sup b/cppcheck.sup index a149fad77..500f28945 100644 --- a/cppcheck.sup +++ b/cppcheck.sup @@ -1,3 +1,619 @@ // These are correct and are used to find the base (zero) pin. -thisSubtraction:netlist.h:4976 -thisSubtraction:netlist.h:4985 +thisSubtraction:netlist.h:5082 +thisSubtraction:netlist.h:5091 + +// These are the functions that the compiler exports to the targets. +//ivl_branch_island() +unusedFunction:t-dll-api.cc:39 +//ivl_branch_terminal() +unusedFunction:t-dll-api.cc:45 + +//ivl_const_bits() +unusedFunction:t-dll-api.cc:196 +//ivl_const_delay() +unusedFunction:t-dll-api.cc:214 +//ivl_const_file() +unusedFunction:t-dll-api.cc:221 +//ivl_const_lineno() +unusedFunction:t-dll-api.cc:227 +//ivl_const_nex() +unusedFunction:t-dll-api.cc:233 +//ivl_const_real() +unusedFunction:t-dll-api.cc:239 +//ivl_const_scope() +unusedFunction:t-dll-api.cc:246 +//ivl_const_signed() +unusedFunction:t-dll-api.cc:252 +//ivl_const_type() +unusedFunction:t-dll-api.cc:190 +//ivl_const_width() +unusedFunction:t-dll-api.cc:258 + +//ivl_design_const() +unusedFunction:t-dll-api.cc:127 +//ivl_design_consts() +unusedFunction:t-dll-api.cc:121 +//ivl_design_delay_sel() +unusedFunction:t-dll-api.cc:52 +//ivl_design_discipline() +unusedFunction:t-dll-api.cc:140 +//ivl_design_disciplines() +unusedFunction:t-dll-api.cc:134 +//ivl_design_flag() +unusedFunction:t-dll-api.cc:59 +//ivl_design_process() +unusedFunction:t-dll-api.cc:66 +//ivl_design_root() +unusedFunction:t-dll-api.cc:80 +//ivl_design_roots() +unusedFunction:t-dll-api.cc:89 +//ivl_design_time_precision() +unusedFunction:t-dll-api.cc:115 + +//ivl_discipline_domain() +unusedFunction:t-dll-api.cc:147 +//ivl_discipline_flow() +unusedFunction:t-dll-api.cc:153 +//ivl_discipline_name() +unusedFunction:t-dll-api.cc:159 +//ivl_discipline_potential() +unusedFunction:t-dll-api.cc:165 + +//ivl_enum_bits() +unusedFunction:t-dll-api.cc:277 +//ivl_enum_file() +unusedFunction:t-dll-api.cc:302 +//ivl_enum_lineno() +unusedFunction:t-dll-api.cc:308 +//ivl_enum_name() +unusedFunction:t-dll-api.cc:270 +//ivl_enum_names() +unusedFunction:t-dll-api.cc:264 +//ivl_enum_signed() +unusedFunction:t-dll-api.cc:296 +//ivl_enum_type() +unusedFunction:t-dll-api.cc:284 +//ivl_enum_width() +unusedFunction:t-dll-api.cc:290 + +//ivl_event_any() +unusedFunction:t-dll-api.cc:369 +//ivl_event_file() +unusedFunction:t-dll-api.cc:345 +//ivl_event_lineno() +unusedFunction:t-dll-api.cc:351 +//ivl_event_name() +unusedFunction:t-dll-api.cc:314 +//ivl_event_nany() +unusedFunction:t-dll-api.cc:363 +//ivl_event_neg() +unusedFunction:t-dll-api.cc:382 +//ivl_event_nneg() +unusedFunction:t-dll-api.cc:376 +//ivl_event_npos() +unusedFunction:t-dll-api.cc:389 +//ivl_event_pos() +unusedFunction:t-dll-api.cc:395 +//ivl_event_scope() +unusedFunction:t-dll-api.cc:357 + +//ivl_expr_bits() +unusedFunction:t-dll-api.cc:402 +//ivl_expr_branch() +unusedFunction:t-dll-api.cc:409 +//ivl_expr_def() +unusedFunction:t-dll-api.cc:416 +//ivl_expr_delay_val() +unusedFunction:t-dll-api.cc:432 +//ivl_expr_dvalue() +unusedFunction:t-dll-api.cc:439 +//ivl_expr_enumtype() +unusedFunction:t-dll-api.cc:446 +//ivl_expr_event() +unusedFunction:t-dll-api.cc:656 +//ivl_expr_file() +unusedFunction:t-dll-api.cc:178 +//ivl_expr_lineno() +unusedFunction:t-dll-api.cc:184 +//ivl_expr_name() +unusedFunction:t-dll-api.cc:459 +//ivl_expr_nature() +unusedFunction:t-dll-api.cc:483 +//ivl_expr_net_type() +unusedFunction:t-dll-api.cc:453 +//ivl_expr_opcode() +unusedFunction:t-dll-api.cc:490 +//ivl_expr_oper1() +unusedFunction:t-dll-api.cc:506 +//ivl_expr_oper2() +unusedFunction:t-dll-api.cc:544 +//ivl_expr_oper3() +unusedFunction:t-dll-api.cc:570 +//ivl_expr_parameter() +unusedFunction:t-dll-api.cc:584 +//ivl_expr_parm() +unusedFunction:t-dll-api.cc:599 +//ivl_expr_parms() +unusedFunction:t-dll-api.cc:626 +//ivl_expr_repeat() +unusedFunction:t-dll-api.cc:649 +//ivl_expr_scope() +unusedFunction:t-dll-api.cc:670 +//ivl_expr_sel_type() +unusedFunction:t-dll-api.cc:677 +//ivl_expr_signed() +unusedFunction:t-dll-api.cc:702 +//ivl_expr_sized() +unusedFunction:t-dll-api.cc:708 +//ivl_expr_string() +unusedFunction:t-dll-api.cc:714 +//ivl_expr_type() +unusedFunction:t-dll-api.cc:171 +//ivl_expr_uvalue() +unusedFunction:t-dll-api.cc:721 +//ivl_expr_value() +unusedFunction:t-dll-api.cc:747 +//ivl_expr_width() +unusedFunction:t-dll-api.cc:753 + +//ivl_file_table_index() +unusedFunction:t-dll-api.cc:795 +//ivl_file_table_item() +unusedFunction:t-dll-api.cc:785 +//ivl_file_table_size() +unusedFunction:t-dll-api.cc:813 + +//ivl_island_flag_set() +unusedFunction:t-dll-api.cc:822 +//ivl_island_flag_test() +unusedFunction:t-dll-api.cc:837 + +//ivl_logic_attr() +unusedFunction:t-dll-api.cc:864 +//ivl_logic_attr_cnt() +unusedFunction:t-dll-api.cc:880 +//ivl_logic_attr_val() +unusedFunction:t-dll-api.cc:886 +//ivl_logic_basename() +unusedFunction:t-dll-api.cc:935 +//ivl_logic_delay() +unusedFunction:t-dll-api.cc:974 +//ivl_logic_drive0() +unusedFunction:t-dll-api.cc:894 +//ivl_logic_drive1() +unusedFunction:t-dll-api.cc:911 +//ivl_logic_file() +unusedFunction:t-dll-api.cc:846 +//ivl_logic_is_cassign() +unusedFunction:t-dll-api.cc:858 +//ivl_logic_lineno() +unusedFunction:t-dll-api.cc:852 +//ivl_logic_name() +unusedFunction:t-dll-api.cc:928 +//ivl_logic_pins() +unusedFunction:t-dll-api.cc:953 +//ivl_logic_scope() +unusedFunction:t-dll-api.cc:941 +//ivl_logic_type() +unusedFunction:t-dll-api.cc:947 +//ivl_logic_udp() +unusedFunction:t-dll-api.cc:966 +//ivl_logic_width() +unusedFunction:t-dll-api.cc:981 + +//ivl_lpm_array() +unusedFunction:t-dll-api.cc:1109 +//ivl_lpm_aset_value() +unusedFunction:t-dll-api.cc:1160 +//ivl_lpm_async_clr() +unusedFunction:t-dll-api.cc:1054 +//ivl_lpm_async_set() +unusedFunction:t-dll-api.cc:1085 +//ivl_lpm_base() +unusedFunction:t-dll-api.cc:1121 +//ivl_lpm_basename() +unusedFunction:t-dll-api.cc:1048 +//ivl_lpm_clk() +unusedFunction:t-dll-api.cc:1148 +//ivl_lpm_data() +unusedFunction:t-dll-api.cc:1221 +//ivl_lpm_datab() +unusedFunction:t-dll-api.cc:1321 +//ivl_lpm_define() +unusedFunction:t-dll-api.cc:1183 +//ivl_lpm_delay() +unusedFunction:t-dll-api.cc:1078 +//ivl_lpm_drive0() +unusedFunction:t-dll-api.cc:1453 +//ivl_lpm_drive1() +unusedFunction:t-dll-api.cc:1470 +//ivl_lpm_enable() +unusedFunction:t-dll-api.cc:1195 +//ivl_lpm_file() +unusedFunction:t-dll-api.cc:1209 +//ivl_lpm_lineno() +unusedFunction:t-dll-api.cc:1215 +//ivl_lpm_name() +unusedFunction:t-dll-api.cc:1355 +//ivl_lpm_negedge() +unusedFunction:t-dll-api.cc:1136 +//ivl_lpm_select() +unusedFunction:t-dll-api.cc:1493 +//ivl_lpm_selects() +unusedFunction:t-dll-api.cc:1510 +//ivl_lpm_signed() +unusedFunction:t-dll-api.cc:1528 +//ivl_lpm_size() +unusedFunction:t-dll-api.cc:1587 +//ivl_lpm_sset_value() +unusedFunction:t-dll-api.cc:1171 +//ivl_lpm_string() +unusedFunction:t-dll-api.cc:1640 +//ivl_lpm_sync_clr() +unusedFunction:t-dll-api.cc:1066 +//ivl_lpm_sync_set() +unusedFunction:t-dll-api.cc:1097 +//ivl_lpm_trigger() +unusedFunction:t-dll-api.cc:1659 +//ivl_lpm_type() +unusedFunction:t-dll-api.cc:1647 +//ivl_lpm_width() +unusedFunction:t-dll-api.cc:1653 + +//ivl_lval_idx() +unusedFunction:t-dll-api.cc:1681 +//ivl_lval_mux() +unusedFunction:t-dll-api.cc:1676 +//ivl_lval_nest() +unusedFunction:t-dll-api.cc:1726 +//ivl_lval_part_off() +unusedFunction:t-dll-api.cc:1690 +//ivl_lval_property_idx() +unusedFunction:t-dll-api.cc:1708 +//ivl_lval_sel_type() +unusedFunction:t-dll-api.cc:1696 +//ivl_lval_sig() +unusedFunction:t-dll-api.cc:1714 + +//ivl_nature_name() +unusedFunction:t-dll-api.cc:1735 + +//ivl_nexus_get_private() +unusedFunction:t-dll-api.cc:1756 +//ivl_nexus_name() +unusedFunction:t-dll-api.cc:1745 +//ivl_nexus_ptr_branch() +unusedFunction:t-dll-api.cc:1799 +//ivl_nexus_ptr_con() +unusedFunction:t-dll-api.cc:1808 +//ivl_nexus_ptr_sig() +unusedFunction:t-dll-api.cc:1835 +//ivl_nexus_ptr_switch() +unusedFunction:t-dll-api.cc:1844 +//ivl_nexus_set_private() +unusedFunction:t-dll-api.cc:1762 + +//ivl_parameter_basename() +unusedFunction:t-dll-api.cc:1853 +//ivl_parameter_expr() +unusedFunction:t-dll-api.cc:1896 +//ivl_parameter_file() +unusedFunction:t-dll-api.cc:1902 +//ivl_parameter_lineno() +unusedFunction:t-dll-api.cc:1908 +//ivl_parameter_local() +unusedFunction:t-dll-api.cc:1859 +//ivl_parameter_lsb() +unusedFunction:t-dll-api.cc:1877 +//ivl_parameter_msb() +unusedFunction:t-dll-api.cc:1871 +//ivl_parameter_scope() +unusedFunction:t-dll-api.cc:1914 +//ivl_parameter_signed() +unusedFunction:t-dll-api.cc:1865 +//ivl_parameter_width() +unusedFunction:t-dll-api.cc:1887 + +//ivl_path_condit() +unusedFunction:t-dll-api.cc:1920 +//ivl_path_delay() +unusedFunction:t-dll-api.cc:1932 +//ivl_path_is_condit() +unusedFunction:t-dll-api.cc:1926 +//ivl_path_scope() +unusedFunction:t-dll-api.cc:1938 +//ivl_path_source() +unusedFunction:t-dll-api.cc:1945 +//ivl_path_source_negedge() +unusedFunction:t-dll-api.cc:1957 +//ivl_path_source_posedge() +unusedFunction:t-dll-api.cc:1951 + +//ivl_process_analog() +unusedFunction:t-dll-api.cc:1981 +//ivl_process_attr_cnt() +unusedFunction:t-dll-api.cc:1999 +//ivl_process_attr_val() +unusedFunction:t-dll-api.cc:2005 +//ivl_process_file() +unusedFunction:t-dll-api.cc:1963 +//ivl_process_lineno() +unusedFunction:t-dll-api.cc:1969 +//ivl_process_scope() +unusedFunction:t-dll-api.cc:1987 +//ivl_process_stmt() +unusedFunction:t-dll-api.cc:1993 +//ivl_process_type() +unusedFunction:t-dll-api.cc:1975 + +//ivl_scope_attr_cnt() +unusedFunction:t-dll-api.cc:2013 +//ivl_scope_attr_val() +unusedFunction:t-dll-api.cc:2019 +//ivl_scope_basename() +unusedFunction:t-dll-api.cc:2027 +//ivl_scope_child() +unusedFunction:t-dll-api.cc:2054 +//ivl_scope_children() +unusedFunction:t-dll-api.cc:2033 +//ivl_scope_childs() +unusedFunction:t-dll-api.cc:2047 +//ivl_scope_class() +unusedFunction:t-dll-api.cc:2061 +//ivl_scope_classes() +unusedFunction:t-dll-api.cc:2068 +//ivl_scope_def() +unusedFunction:t-dll-api.cc:2075 +//ivl_scope_def_file() +unusedFunction:t-dll-api.cc:2081 +//ivl_scope_def_lineno() +unusedFunction:t-dll-api.cc:2087 +//ivl_scope_enumerate() +unusedFunction:t-dll-api.cc:2099 +//ivl_scope_enumerates() +unusedFunction:t-dll-api.cc:2093 +//ivl_scope_event() +unusedFunction:t-dll-api.cc:2112 +//ivl_scope_events() +unusedFunction:t-dll-api.cc:2106 +//ivl_scope_file() +unusedFunction:t-dll-api.cc:2119 +//ivl_scope_func_signed +unusedFunction:t-dll-api.cc:2132 +//ivl_scope_func_type +unusedFunction:t-dll-api.cc:2125 +//ivl_scope_func_width +unusedFunction:t-dll-api.cc:2140 +//ivl_scope_is_auto() +unusedFunction:t-dll-api.cc:2148 +//ivl_scope_is_cell() +unusedFunction:t-dll-api.cc:2154 +//ivl_scope_lineno() +unusedFunction:t-dll-api.cc:2160 +//ivl_scope_log() +unusedFunction:t-dll-api.cc:2172 +//ivl_scope_logs() +unusedFunction:t-dll-api.cc:2166 +//ivl_scope_lpm() +unusedFunction:t-dll-api.cc:2185 +//ivl_scope_lpms() +unusedFunction:t-dll-api.cc:2179 +//ivl_scope_mod_module_port_name() +unusedFunction:t-dll-api.cc:2264 +//ivl_scope_mod_module_port_type() +unusedFunction:t-dll-api.cc:2273 +//ivl_scope_mod_module_port_width() +unusedFunction:t-dll-api.cc:2285 +//ivl_scope_mod_module_ports() +unusedFunction:t-dll-api.cc:2257 +//ivl_scope_mod_port() +unusedFunction:t-dll-api.cc:2310 +//ivl_scope_param() +unusedFunction:t-dll-api.cc:2243 +//ivl_scope_params() +unusedFunction:t-dll-api.cc:2237 +//ivl_scope_parent() +unusedFunction:t-dll-api.cc:2250 +//ivl_scope_port() +unusedFunction:t-dll-api.cc:2301 +//ivl_scope_ports() +unusedFunction:t-dll-api.cc:2292 +//ivl_scope_sig() +unusedFunction:t-dll-api.cc:2324 +//ivl_scope_sigs() +unusedFunction:t-dll-api.cc:2318 +//ivl_scope_switch() +unusedFunction:t-dll-api.cc:2337 +//ivl_scope_switches() +unusedFunction:t-dll-api.cc:2331 +//ivl_scope_time_precision() +unusedFunction:t-dll-api.cc:2344 +//ivl_scope_time_units() +unusedFunction:t-dll-api.cc:2350 +//ivl_scope_tname() +unusedFunction:t-dll-api.cc:2362 +//ivl_scope_type() +unusedFunction:t-dll-api.cc:2356 + +//ivl_signal_array_addr_swapped() +unusedFunction:t-dll-api.cc:2380 +//ivl_signal_array_base() +unusedFunction:t-dll-api.cc:2368 +//ivl_signal_array_count() +unusedFunction:t-dll-api.cc:2374 +//ivl_signal_attr() +unusedFunction:t-dll-api.cc:2398 +//ivl_signal_attr_cnt() +unusedFunction:t-dll-api.cc:2414 +//ivl_signal_attr_val() +unusedFunction:t-dll-api.cc:2420 +//ivl_signal_basename() +unusedFunction:t-dll-api.cc:2427 +//ivl_signal_data_type() +unusedFunction:t-dll-api.cc:2578 +//ivl_signal_dimensions() +unusedFunction:t-dll-api.cc:2386 +//ivl_signal_discipline() +unusedFunction:t-dll-api.cc:2392 +//ivl_signal_file() +unusedFunction:t-dll-api.cc:2555 +//ivl_signal_forced_net() +unusedFunction:t-dll-api.cc:2549 +//ivl_signal_integer() +unusedFunction:t-dll-api.cc:2567 +//ivl_signal_lineno() +unusedFunction:t-dll-api.cc:2561 +//ivl_signal_local() +unusedFunction:t-dll-api.cc:2536 +//ivl_signal_lsb() +unusedFunction:t-dll-api.cc:2501 +//ivl_signal_module_port_index() +unusedFunction:t-dll-api.cc:2530 +//ivl_signal_msb() +unusedFunction:t-dll-api.cc:2491 +//ivl_signal_name() +unusedFunction:t-dll-api.cc:2433 +//ivl_signal_nex() +unusedFunction:t-dll-api.cc:2454 +//ivl_signal_npath() +unusedFunction:t-dll-api.cc:2591 +//ivl_signal_packed_dimensions() +unusedFunction:t-dll-api.cc:2471 +//ivl_signal_packed_lsb() +unusedFunction:t-dll-api.cc:2484 +//ivl_signal_packed_msb() +unusedFunction:t-dll-api.cc:2477 +//ivl_signal_path() +unusedFunction:t-dll-api.cc:2597 +//ivl_signal_port() +unusedFunction:t-dll-api.cc:2524 +//ivl_signal_scope() +unusedFunction:t-dll-api.cc:2511 +//ivl_signal_signed() +unusedFunction:t-dll-api.cc:2542 +//ivl_signal_width() +unusedFunction:t-dll-api.cc:2517 + +//ivl_statement_type() +unusedFunction:t-dll-api.cc:2610 + +//ivl_stmt_block_count() +unusedFunction:t-dll-api.cc:2643 +//ivl_stmt_block_scope() +unusedFunction:t-dll-api.cc:2628 +//ivl_stmt_block_stmt() +unusedFunction:t-dll-api.cc:2658 +//ivl_stmt_call() +unusedFunction:t-dll-api.cc:2674 +//ivl_stmt_case_count() +unusedFunction:t-dll-api.cc:2695 +//ivl_stmt_case_expr() +unusedFunction:t-dll-api.cc:2710 +//ivl_stmt_case_stmt() +unusedFunction:t-dll-api.cc:2727 +//ivl_stmt_cond_expr() +unusedFunction:t-dll-api.cc:2744 +//ivl_stmt_cond_false() +unusedFunction:t-dll-api.cc:2771 +//ivl_stmt_cond_true() +unusedFunction:t-dll-api.cc:2781 +//ivl_stmt_delay_expr() +unusedFunction:t-dll-api.cc:2791 +//ivl_stmt_delay_val() +unusedFunction:t-dll-api.cc:2808 +//ivl_stmt_events() +unusedFunction:t-dll-api.cc:2834 +//ivl_stmt_file() +unusedFunction:t-dll-api.cc:2616 +//ivl_stmt_lexp() +unusedFunction:t-dll-api.cc:2862 +//ivl_stmt_lineno() +unusedFunction:t-dll-api.cc:2622 +//ivl_stmt_lval() +unusedFunction:t-dll-api.cc:2874 +//ivl_stmt_lvals() +unusedFunction:t-dll-api.cc:2893 +//ivl_stmt_lwidth() +unusedFunction:t-dll-api.cc:2911 +//ivl_stmt_name() +unusedFunction:t-dll-api.cc:2944 +//ivl_stmt_nevent() +unusedFunction:t-dll-api.cc:2815 +//ivl_stmt_opcode() +unusedFunction:t-dll-api.cc:2957 +//ivl_stmt_parm() +unusedFunction:t-dll-api.cc:2969 +//ivl_stmt_parm_count() +unusedFunction:t-dll-api.cc:2983 +//ivl_stmt_rval() +unusedFunction:t-dll-api.cc:2995 +//ivl_stmt_sfunc_as_task() +unusedFunction:t-dll-api.cc:3013 +//ivl_stmt_sub_stmt() +unusedFunction:t-dll-api.cc:3026 + +//ivl_switch_a() +unusedFunction:t-dll-api.cc:3067 +//ivl_switch_b() +unusedFunction:t-dll-api.cc:3073 +//ivl_switch_basename() +unusedFunction:t-dll-api.cc:3049 +//ivl_switch_delay() +unusedFunction:t-dll-api.cc:3103 +//ivl_switch_enable() +unusedFunction:t-dll-api.cc:3079 +//ivl_switch_file() +unusedFunction:t-dll-api.cc:3110 +//ivl_switch_island() +unusedFunction:t-dll-api.cc:3116 +//ivl_switch_lineno() +unusedFunction:t-dll-api.cc:3122 +//ivl_switch_offset() +unusedFunction:t-dll-api.cc:3097 +//ivl_switch_part() +unusedFunction:t-dll-api.cc:3091 +//ivl_switch_scope() +unusedFunction:t-dll-api.cc:3055 +//ivl_switch_type() +unusedFunction:t-dll-api.cc:3061 +//ivl_switch_width() +unusedFunction:t-dll-api.cc:3085 + +//ivl_type_base() +unusedFunction:t-dll-api.cc:3128 +//ivl_type_element() +unusedFunction:t-dll-api.cc:3134 +//ivl_type_name() +unusedFunction:t-dll-api.cc:3166 +//ivl_type_packed_dimensions() +unusedFunction:t-dll-api.cc:3143 +//ivl_type_packed_lsb() +unusedFunction:t-dll-api.cc:3150 +//ivl_type_packed_msb() +unusedFunction:t-dll-api.cc:3158 +//ivl_type_prop_type() +unusedFunction:t-dll-api.cc:3191 +//ivl_type_properties() +unusedFunction:t-dll-api.cc:3174 +//ivl_type_signed() +unusedFunction:t-dll-api.cc:3199 + +//ivl_udp_file() +unusedFunction:t-dll-api.cc:1036 +//ivl_udp_init() +unusedFunction:t-dll-api.cc:999 +//ivl_udp_lineno() +unusedFunction:t-dll-api.cc:1042 +//ivl_udp_name() +unusedFunction:t-dll-api.cc:1029 +//ivl_udp_nin() +unusedFunction:t-dll-api.cc:993 +//ivl_udp_port() +unusedFunction:t-dll-api.cc:1005 +//ivl_udp_row() +unusedFunction:t-dll-api.cc:1014 +//ivl_udp_rows() +unusedFunction:t-dll-api.cc:1023 +// ivl_udp_sequ() +unusedFunction:t-dll-api.cc:987 + diff --git a/design_dump.cc b/design_dump.cc index 123917d16..b5f52cfad 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2017 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 @@ -187,9 +187,15 @@ ostream& operator << (ostream&fd, NetCaseCmp::kind_t that) case NetCaseCmp::NEQ: fd << "!=="; break; - case NetCaseCmp::XEQ: + case NetCaseCmp::WEQ: fd << "==?"; break; + case NetCaseCmp::WNE: + fd << "!=?"; + break; + case NetCaseCmp::XEQ: + fd << "==x?"; + break; case NetCaseCmp::ZEQ: fd << "==z?"; break; @@ -677,13 +683,25 @@ void NetConst::dump_node(ostream&o, unsigned ind) const void NetFF::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "LPM_FF: " << name() - << " scope=" << scope_path(scope()) - << " aset_value=" << aset_value_ << endl; + << " scope=" << scope_path(scope()); + if (negedge_) + o << " negedge"; + else + o << " posedge"; + o << " aset_value=" << aset_value_ << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } +void NetLatch::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "LPM_LATCH: " << name() + << " scope=" << scope_path(scope()) << endl; + dump_node_pins(o, ind+4); + dump_obj_attr(o, ind+4); +} + void NetLiteral::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "constant real " << real_ @@ -972,6 +990,18 @@ void NetProcTop::dump(ostream&o, unsigned ind) const o << "always /* " << get_fileline() << " in " << scope_path(scope_) << " */" << endl; break; + case IVL_PR_ALWAYS_COMB: + o << "always_comb /* " << get_fileline() << " in " + << scope_path(scope_) << " */" << endl; + break; + case IVL_PR_ALWAYS_FF: + o << "always_ff /* " << get_fileline() << " in " + << scope_path(scope_) << " */" << endl; + break; + case IVL_PR_ALWAYS_LATCH: + o << "always_latch /* " << get_fileline() << " in " + << scope_path(scope_) << " */" << endl; + break; case IVL_PR_FINAL: o << "final /* " << get_fileline() << " in " << scope_path(scope_) << " */" << endl; @@ -999,6 +1029,13 @@ void NetAnalogTop::dump(ostream&o, unsigned ind) const << scope_path(scope_) << " */" << endl; break; + // These are not used in an analog context. + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: + assert(0); + break; + case IVL_PR_FINAL: o << "analog final /* " << get_fileline() << " in " << scope_path(scope_) << " */" << endl; @@ -1378,6 +1415,8 @@ void NetScope::dump(ostream&o) const if (is_interface()) o << " (interface)"; o << " " << children_.size() << " children, " << classes_.size() << " classes" << endl; + if (unit() && !is_unit()) + o << " in compilation unit " << unit()->basename() << endl; for (unsigned idx = 0 ; idx < attr_cnt() ; idx += 1) o << " (* " << attr_key(idx) << " = " @@ -1617,11 +1656,14 @@ void NetEBinary::dump(ostream&o) const case 'A': o << "~&"; break; + case 'e': + o << "=="; + break; case 'E': o << "==="; break; - case 'e': - o << "=="; + case 'w': + o << "==?"; break; case 'G': o << ">="; @@ -1638,6 +1680,9 @@ void NetEBinary::dump(ostream&o) const case 'N': o << "!=="; break; + case 'W': + o << "!=?"; + break; case 'o': o << "||"; break; @@ -1890,18 +1935,6 @@ void Design::dump(ostream&o) const cur->second->dump(o); } - o << "$ROOT CLASSESS:" << endl; - for (map::const_iterator cur = classes_.begin() - ; cur != classes_.end() ; ++cur) { - cur->second->dump_scope(o); - } - - o << "$ROOT TASKS/FUNCTIONS:" << endl; - for (map::const_iterator cur = root_tasks_.begin() - ; cur != root_tasks_.end() ; ++ cur) { - cur->first->dump(o); - } - o << "SCOPES:" << endl; for (list::const_iterator scope = root_scopes_.begin(); scope != root_scopes_.end(); ++ scope ) { diff --git a/dosify.c b/dosify.c index 5f58d894e..2f8d97827 100644 --- a/dosify.c +++ b/dosify.c @@ -19,7 +19,7 @@ /* * This is a simple program to make a dosified copy of the - * original. That is, it converts unix style line ends to DOS + * original. That is, it converts Unix style line ends to DOS * style. This is useful for installing text files. * * The exact substitution is to replace \n with \r\n. If the line diff --git a/driver-vpi/Makefile.in b/driver-vpi/Makefile.in index d062dc1e8..f4cc6b167 100644 --- a/driver-vpi/Makefile.in +++ b/driver-vpi/Makefile.in @@ -86,7 +86,7 @@ config.h: $(srcdir)/config.h.in Makefile # Windows specific... res.rc: $(srcdir)/res.rc.in ../version.exe - sed -e 's;@PRODUCTVERSION@;'`../version.exe '%M,%m,%n,0'`';' \ + sed -e 's;@PRODUCTVERSION@;'`../version.exe '%M,%n,0,0'`';' \ $(srcdir)/res.rc.in > $@ res.o: res.rc diff --git a/driver-vpi/main.c b/driver-vpi/main.c index 5acb46b8d..19ed5cbe7 100644 --- a/driver-vpi/main.c +++ b/driver-vpi/main.c @@ -1,5 +1,6 @@ /* - * Copyright (c) 2002-2015 Gus Baldauf (gus@picturel.com) + * Copyright (c) 2002 Gus Baldauf (gus@picturel.com) + * Copyright (c) 2015 Martin Whitaker * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -20,7 +21,7 @@ /* * iverilog-vpi.c * - * this program provides the functionality of iverilog-vpi.sh under Win32 + * this program provides the functionality of iverilog-vpi.sh under Windows */ #include @@ -52,8 +53,8 @@ static struct global_strings { char *pIVL; /* path to IVL directory */ char *pCCFLAGS; /* compiler flags for compiling C source files */ char *pCXFLAGS; /* compiler flags for compiling C++ source files */ - char *pLDLIBS; /* LDLIBS option */ - char *pNewPath; /* new PATH environment variable setting */ + char *pLDLIBS; /* linker flags for final linking stage */ + char *pCCNAME; /* base name of compiler */ char *pLD; /* what to use for a linker */ } gstr; @@ -79,8 +80,8 @@ static void myExit(int exitVal) deInitDynString(gstr.pCCFLAGS); deInitDynString(gstr.pCXFLAGS); deInitDynString(gstr.pLDLIBS); - deInitDynString(gstr.pNewPath); - free(gstr.pLD); + deInitDynString(gstr.pCCNAME); + deInitDynString(gstr.pLD); exit(exitVal); } @@ -89,9 +90,8 @@ static void myExit(int exitVal) static void usage(void) { - fprintf(stderr,"usage: iverilog-vpi" IVERILOG_SUFFIX " [src and obj files]...\n"); - fprintf(stderr," or iverilog-vpi" IVERILOG_SUFFIX " -mingw=dir\n"); - fprintf(stderr," or iverilog-vpi" IVERILOG_SUFFIX " -ivl=dir\n"); + fprintf(stderr, "usage: iverilog-vpi" IVERILOG_SUFFIX " [options] [src and obj files]...\n"); + fprintf(stderr, " or iverilog-vpi" IVERILOG_SUFFIX " -mingw=dir\n"); myExit(1); } @@ -100,7 +100,7 @@ static void initDynString(char **str) *str = (char *) malloc(1); if (!*str) { - fprintf(stderr,"error: out of memory\n"); + fprintf(stderr, "error: out of memory\n"); myExit(4); } @@ -111,6 +111,8 @@ static void initDynString(char **str) static void init(void) { + char *ptr; + initDynString(&gstr.pCCSRC); initDynString(&gstr.pCXSRC); initDynString(&gstr.pOBJ); @@ -123,7 +125,14 @@ static void init(void) initDynString(&gstr.pCCFLAGS); initDynString(&gstr.pCXFLAGS); initDynString(&gstr.pLDLIBS); - initDynString(&gstr.pNewPath); + initDynString(&gstr.pCCNAME); + initDynString(&gstr.pLD); + + /* Get the base name of the C compiler. */ + assign(&gstr.pCCNAME, IVERILOG_VPI_CC); + ptr = strchr(gstr.pCCNAME, ' '); + if (ptr != NULL) *ptr = '\0'; + /* By default use the C compiler to link the programs. */ assign(&gstr.pLD, IVERILOG_VPI_CC); } @@ -139,7 +148,7 @@ static int endsIn (char *end, char *str) ext = str + (strlen(str) - strlen(end)); - return stricmp(end,ext) ? 0 : 1; + return stricmp(end, ext) ? 0 : 1; } /* return true if "str" begins with "prefix", case insensitive */ @@ -149,7 +158,7 @@ static int startsWith (char *prefix, char *str) if (strlen(prefix) >= strlen(str)) return 0; - return strnicmp(prefix,str,strlen(prefix)) ? 0 : 1; + return strnicmp(prefix, str, strlen(prefix)) ? 0 : 1; } /* append "app" to "ptr", allocating memory as needed */ @@ -158,26 +167,26 @@ static int startsWith (char *prefix, char *str) static void appendn (char **ptr, char *app, size_t count) { char *nptr = (char *) realloc(*ptr, strlen(*ptr) + - (count?count:strlen(app)) + 1); + (count ? count : strlen(app)) + 1); if (nptr == NULL) { - fprintf(stderr,"error: out of memory\n"); + fprintf(stderr, "error: out of memory\n"); free(*ptr); myExit(4); } *ptr = nptr; if (count) - strncat(*ptr,app,count); + strncat(*ptr, app, count); else - strcat(*ptr,app); + strcat(*ptr, app); } /* append "app" to "ptr", allocating memory as needed */ static void append (char **ptr, char *app) { - appendn(ptr,app,0); + appendn(ptr, app, 0); } /* if the string does not end with a backslash, add one */ @@ -185,7 +194,7 @@ static void append (char **ptr, char *app) static void appendBackSlash(char **str) { if ((*str)[strlen(*str)-1] != '\\') - append(str,"\\"); + append(str, "\\"); } /* copy count characters of "str" to "ptr", allocating memory as needed */ @@ -193,28 +202,28 @@ static void appendBackSlash(char **str) static void assignn (char **ptr, char *str, size_t count) { - char *nptr = (char *) realloc(*ptr, (count?count:strlen(str)) + 1); + char *nptr = (char *) realloc(*ptr, (count ? count : strlen(str)) + 1); if (nptr == NULL) { - fprintf(stderr,"error: out of memory\n"); + fprintf(stderr, "error: out of memory\n"); free(*ptr); myExit(4); } *ptr = nptr; if (count) { - strncpy(*ptr,str,count); + strncpy(*ptr, str, count); (*ptr)[count] = 0; } else - strcpy(*ptr,str); + strcpy(*ptr, str); } /* copy count characters of "str" to "ptr", allocating memory as needed */ static void assign (char **ptr, char *str) { - assignn(ptr,str,0); + assignn(ptr, str, 0); } /* get a copy of a Icarus Verilog registry string key */ @@ -226,11 +235,11 @@ static int GetRegistryKey(char *key, char **value) char *regKeyBuffer; DWORD regKeyType, regKeySize; - lrv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,"Software\\Icarus Verilog",0,KEY_QUERY_VALUE,&hkKey); + lrv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Icarus Verilog", 0, KEY_QUERY_VALUE, &hkKey); if (lrv != ERROR_SUCCESS) return 0; - lrv = RegQueryValueEx(hkKey,key,NULL,®KeyType,NULL,®KeySize); + lrv = RegQueryValueEx(hkKey, key, NULL, ®KeyType, NULL, ®KeySize); if ((lrv != ERROR_SUCCESS) || (regKeyType != REG_SZ) || (!regKeySize)) { lrv = RegCloseKey(hkKey); return 0; @@ -239,7 +248,7 @@ static int GetRegistryKey(char *key, char **value) regKeyBuffer = (char *) malloc(regKeySize+1); if (!regKeyBuffer) { lrv = RegCloseKey(hkKey); - fprintf(stderr,"error: out of memory\n"); + fprintf(stderr, "error: out of memory\n"); myExit(4); } regKeyBuffer[regKeySize] = 0; /* makes sure there is a trailing NULL */ @@ -255,7 +264,7 @@ static int GetRegistryKey(char *key, char **value) RegCloseKey(hkKey); - assign(value,regKeyBuffer); + assign(value, regKeyBuffer); free(regKeyBuffer); return 1; @@ -265,27 +274,30 @@ static int GetRegistryKey(char *key, char **value) static void SetRegistryKey(char *key, char *value) { + long lrv; HKEY hkKey; DWORD res; - if (RegCreateKeyEx( - HKEY_LOCAL_MACHINE, - "Software\\Icarus Verilog", - 0, - "", - REG_OPTION_NON_VOLATILE, - KEY_ALL_ACCESS,NULL, - &hkKey, - &res) != ERROR_SUCCESS) - return; + lrv = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\Icarus Verilog", 0, "", + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkKey, &res); + if (lrv != ERROR_SUCCESS) { + char message[1024]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, lrv, LANG_USER_DEFAULT, + message, sizeof(message), NULL); + fprintf(stderr, "error: couldn't write to registry - %s\n", message); + if (lrv == ERROR_ACCESS_DENIED) { + fprintf(stderr, " try running as administrator\n"); + } + return; + } /* This needs an unsigned char *, but for MinGW the char is signed. */ RegSetValueEx(hkKey, key, 0, REG_SZ, (unsigned char *) value, strlen(value)+1); RegCloseKey(hkKey); - printf("info: storing %s in Windows' registry entry\n",value); - printf(" HKEY_LOCAL_MACHINE\\Software\\Icarus Verilog\\%s\n",key); + printf("info: storing %s in Windows' registry entry\n", value); + printf(" HKEY_LOCAL_MACHINE\\Software\\Icarus Verilog\\%s\n", key); } /* parse the command line, assign results to global variable strings */ @@ -302,7 +314,6 @@ static int parse(int argc, char *argv[]) char lib_option[] = "-l"; char inc_option[] = "-I"; char mingw_option[] = "-mingw="; - char ivl_option[] = "-ivl="; char def_option[] = "-D"; if (argc == 1) return 0; @@ -342,7 +353,7 @@ static int parse(int argc, char *argv[]) /* Check for compiled object files */ else if (endsIn(dot_o_ext, argv[idx])) { ++srcFileCnt; - append(&gstr.pOBJ," "); + append(&gstr.pOBJ, " "); append(&gstr.pOBJ, argv[idx]); if (!*gstr.pOUT) assignn(&gstr.pOUT, argv[idx], @@ -352,10 +363,6 @@ static int parse(int argc, char *argv[]) else if (startsWith(mingw_option, argv[idx])) assignn(&gstr.pMINGW, argv[idx]+sizeof(mingw_option)-1, strlen(argv[idx])-(sizeof(mingw_option)-1)); - /* Check for the -ivl option */ - else if (startsWith(ivl_option, argv[idx])) - assignn(&gstr.pIVL, argv[idx]+sizeof(ivl_option)-1, - strlen(argv[idx])-(sizeof(ivl_option)-1)); /* Check for the --name option */ else if (startsWith(name_option, argv[idx])) { assignn(&gstr.pOUT, argv[idx]+sizeof(name_option)-1, @@ -396,7 +403,7 @@ static int parse(int argc, char *argv[]) /* Check for the --install-dir option */ else if (stricmp("--install-dir", argv[idx]) == 0) { setup_ivl_environment(); - printf("%s\\\\lib\\\\ivl" IVERILOG_SUFFIX "\\\\.\n", gstr.pIVL); + printf("%s\\lib\\ivl" IVERILOG_SUFFIX "\\.\n", gstr.pIVL); myExit(0); } /* This is different than iverilog-vpi.sh, we don't @@ -405,15 +412,16 @@ static int parse(int argc, char *argv[]) } /* In case there is a --name without source/object files */ - if (0 == srcFileCnt) assign(&gstr.pOUT,""); + if (0 == srcFileCnt) assign(&gstr.pOUT, ""); - /* Normally it's an error if there are no source or object files */ - /* Unless we are setting the IVL or MinGW registry entries */ - if (!*gstr.pOUT) { - if (!*gstr.pMINGW && !*gstr.pIVL) return 0; - } else + if (*gstr.pOUT) { /* We have a valid result file so add the .vpi extension */ append(&gstr.pOUT, ".vpi"); + } else { + /* Unless we are setting the MinGW registry entry, it's + an error if there are no source or object files */ + if (!*gstr.pMINGW) return 0; + } return 1; } @@ -424,58 +432,27 @@ static void checkMingwDir(char *root) { int irv; struct _stat stat_buf; - - char *path, *comp, *cp; - initDynString(&path); - assign(&path,gstr.pMINGW); - appendBackSlash(&path); - append(&path,"bin\\"); - /* Get just the compiler name (the first word) */ - comp = strdup(IVERILOG_VPI_CC); - cp = strchr(comp, ' '); - if (cp != NULL) *cp = '\0'; - append(&path, comp); - append(&path,".exe"); - free(comp); - - irv = _stat(path,&stat_buf); - deInitDynString(path); - - if (irv) { - fprintf(stderr,"error: %s does not appear to be the valid root directory\n",root); - fprintf(stderr," of MinGW. Use the -mingw option of iverilog-vpi.exe to\n"); - fprintf(stderr," point to the MinGW root directory. For a Windows command\n"); - fprintf(stderr," shell the option would be something like -mingw=c:\\mingw\n"); - fprintf(stderr," For a Cygwin shell the option would be something like\n"); - fprintf(stderr," -mingw=c:\\\\mingw\n"); - myExit(5); - } -} - -/* do minimal check that the Icarus Verilog root directory looks valid */ - -static void checkIvlDir(char *root) -{ - int irv; - struct _stat stat_buf; - char *path; + initDynString(&path); - assign(&path,gstr.pIVL); + assign(&path, gstr.pMINGW); appendBackSlash(&path); - append(&path,"bin\\vvp" IVERILOG_SUFFIX ".exe"); + append(&path, "bin\\"); + append(&path, gstr.pCCNAME); + append(&path, ".exe"); irv = _stat(path,&stat_buf); deInitDynString(path); if (irv) { - fprintf(stderr,"error: %s does not appear to be the valid root directory of\n",root); - fprintf(stderr," Icarus Verilog. Use the -ivl option of iverilog-vpi" IVERILOG_SUFFIX " to\n"); - fprintf(stderr," point to the Icarus Verilog root directory. For a Windows\n"); - fprintf(stderr," command shell the option would be something like -ivl=c:\\iverilog\n"); - fprintf(stderr," For a Cygwin shell the option would be something like\n"); - fprintf(stderr," -ivl=c:\\\\iverilog\n"); - myExit(6); + fprintf(stderr, "error: %s\n", root); + fprintf(stderr, " does not appear to be the valid root directory of\n"); + fprintf(stderr, " MinGW. Use the -mingw option of iverilog-vpi.exe to\n"); + fprintf(stderr, " point to the MinGW root directory. For a Windows command\n"); + fprintf(stderr, " shell the option would be something like -mingw=c:\\mingw\n"); + fprintf(stderr, " For a Cygwin shell the option would be something like\n"); + fprintf(stderr, " -mingw=c:\\\\mingw\n"); + myExit(5); } } @@ -485,67 +462,91 @@ static void checkIvlDir(char *root) static void setup_mingw_environment(void) { - char *pOldPATH = getenv("PATH"); /* get current path */ + char buffer[1]; /* doesn't matter how big this is, as we don't use the result */ + char *path; if (*gstr.pMINGW) { checkMingwDir(gstr.pMINGW); - SetRegistryKey(IVL_REGKEY_MINGW,gstr.pMINGW); - } else if (!GetRegistryKey(IVL_REGKEY_MINGW,&gstr.pMINGW)) { - fprintf(stderr,"error: can not locate the MinGW root directory, use the -mingw option of\n"); - fprintf(stderr," iverilog-vpi.exe to point to the MinGW root directory. For\n"); - fprintf(stderr," a Windows command shell the option would be something like\n"); - fprintf(stderr," -mingw=c:\\mingw For a Cygwin shell the option would be\n"); - fprintf(stderr," something like -mingw=c:\\\\mingw\n"); - myExit(5); - } + if (!*gstr.pOUT) SetRegistryKey(IVL_REGKEY_MINGW, gstr.pMINGW); + + } else if (GetRegistryKey(IVL_REGKEY_MINGW, &gstr.pMINGW)) { + checkMingwDir(gstr.pMINGW); + + } else if (SearchPath(NULL, gstr.pCCNAME, ".exe", sizeof(buffer), buffer, NULL)) { + return; + + } else { + fprintf(stderr, "error: cannot locate the MinGW C compiler - either add its location\n"); + fprintf(stderr, " to the PATH environment variable or use the -mingw option of\n"); + fprintf(stderr, " iverilog-vpi.exe to point to the MinGW root directory. For\n"); + fprintf(stderr, " a Windows command shell the option would be something like\n"); + fprintf(stderr, " -mingw=c:\\mingw For a Cygwin shell the option would be\n"); + fprintf(stderr, " something like -mingw=c:\\\\mingw\n"); + myExit(5); + } /* Create new path with MinGW in it */ - assign(&gstr.pNewPath,"PATH="); - append(&gstr.pNewPath,gstr.pMINGW); - appendBackSlash(&gstr.pNewPath); - append(&gstr.pNewPath, "\\"); - append(&gstr.pNewPath,"bin;"); - append(&gstr.pNewPath,pOldPATH); + initDynString(&path); + assign(&path, "PATH="); + append(&path, gstr.pMINGW); + appendBackSlash(&path); + append(&path, "bin;"); + append(&path, getenv("PATH")); /* Place new path in environment */ - _putenv(gstr.pNewPath); + _putenv(path); + deInitDynString(path); } -/* see if we can find iverilog root */ - -#define IVL_REGKEY_IVL "InstallDir" +/* find the iverilog root and initialise the compiler options */ static void setup_ivl_environment(void) { - if (*gstr.pIVL) { - checkIvlDir(gstr.pIVL); - SetRegistryKey(IVL_REGKEY_IVL,gstr.pIVL); - } else if (!GetRegistryKey(IVL_REGKEY_IVL,&gstr.pIVL)) { - fprintf(stderr,"error: can not locate the Icarus Verilog root directory, use the -ivl option\n"); - fprintf(stderr," of iverilog-vpi" IVERILOG_SUFFIX " to point to the Icarus Verilog root directory.\n"); - fprintf(stderr," For a Windows command shell the option would be something like\n"); - fprintf(stderr," -ivl=c:\\iverilog For a Cygwin shell the option would be something\n"); - fprintf(stderr," like -ivl=c:\\\\iverilog\n"); - myExit(6); - } + char path[4096]; + char *ptr; + + /* Extract the Icarus Verilog root directory from the path to the + command. The command path will look something like this: + + C:\iverilog\bin\iverilog-vpi.exe + + The corresponding root directory is + + C:\iverilog + + so we chop off the file name and the last directory. */ + GetModuleFileName(NULL, path, sizeof(path)); + ptr = strrchr(path, '\\'); + if (!ptr) { + fprintf(stderr, "error: couldn't find start of program name in command path '%s'\n", path); + myExit(6); + } + *ptr = 0; + ptr = strrchr(path, '\\'); + if (!ptr) { + fprintf(stderr, "error: couldn't find start of bin directory in command path '%s'\n", path); + myExit(6); + } + *ptr = 0; + assign(&gstr.pIVL, path); /* Build up the CCFLAGS option string */ - assign(&gstr.pCCFLAGS,IVERILOG_VPI_CFLAGS " -I\""); - append(&gstr.pCCFLAGS,gstr.pIVL); + assign(&gstr.pCCFLAGS, IVERILOG_VPI_CFLAGS " -I\""); + append(&gstr.pCCFLAGS, gstr.pIVL); appendBackSlash(&gstr.pCCFLAGS); - append(&gstr.pCCFLAGS,"\\include\\\\iverilog\"" IVERILOG_SUFFIX); + append(&gstr.pCCFLAGS, "include\\iverilog\"" IVERILOG_SUFFIX); /* Build up the CXFLAGS option string */ - assign(&gstr.pCXFLAGS,IVERILOG_VPI_CXXFLAGS " -I\""); - append(&gstr.pCXFLAGS,gstr.pIVL); + assign(&gstr.pCXFLAGS, IVERILOG_VPI_CXXFLAGS " -I\""); + append(&gstr.pCXFLAGS, gstr.pIVL); appendBackSlash(&gstr.pCXFLAGS); - append(&gstr.pCXFLAGS,"\\include\\\\iverilog\"" IVERILOG_SUFFIX); + append(&gstr.pCXFLAGS, "include\\iverilog\"" IVERILOG_SUFFIX); /* Build up the LDFLAGS option string */ - assign(&gstr.pLDLIBS,"-L\""); - append(&gstr.pLDLIBS,gstr.pIVL); + assign(&gstr.pLDLIBS, "-L\""); + append(&gstr.pLDLIBS, gstr.pIVL); appendBackSlash(&gstr.pLDLIBS); - append(&gstr.pLDLIBS,"\\lib\" " IVERILOG_VPI_LDLIBS); + append(&gstr.pLDLIBS, "lib\" " IVERILOG_VPI_LDLIBS); } /* compile source modules */ @@ -609,12 +610,7 @@ static void compile_and_link(void) /* To make the output match iverilog-vpi.sh do not print out the * root directories */ - // printf("MinGW root directory: %s.\n", gstr.pMINGW); - checkMingwDir(gstr.pMINGW); - -// printf("Icarus Verilog root directory: %s.\n", gstr.pIVL); - checkIvlDir(gstr.pIVL); /* compile the C source files (*.c) */ compile(gstr.pCCSRC, gstr.pCCFLAGS, &gstr.pOBJ, &compile_errors, IVERILOG_VPI_CC ); @@ -622,7 +618,7 @@ static void compile_and_link(void) compile(gstr.pCXSRC, gstr.pCXFLAGS, &gstr.pOBJ, &compile_errors, IVERILOG_VPI_CXX); if (compile_errors) { - fprintf(stderr,"iverilog-vpi: %d file(s) failed to compile.\n", + fprintf(stderr, "iverilog-vpi: %d file(s) failed to compile.\n", compile_errors); myExit(2); } @@ -653,7 +649,7 @@ int main(int argc, char *argv[]) { init(); - if (!parse(argc,argv)) usage(); + if (!parse(argc, argv)) usage(); setup_mingw_environment(); setup_ivl_environment(); diff --git a/driver/cflexor.lex b/driver/cflexor.lex index b0ba1a40b..c6e3eb860 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-2017 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. */ @@ -251,7 +252,7 @@ void destroy_lexor(void) { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 -# if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 +# if YY_FLEX_MINOR_VERSION > 5 || defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif diff --git a/driver/iverilog.man.in b/driver/iverilog.man.in index 07b12cea1..56ba6d201 100644 --- a/driver/iverilog.man.in +++ b/driver/iverilog.man.in @@ -1,14 +1,15 @@ -.TH iverilog 1 "December 16th, 2014" "" "Version %M.%m.%n %E" +.TH iverilog 1 "Nov 8th, 2017" "" "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] +[\-ESuVv] [\-Bpath] [\-ccmdfile|\-fcmdfile] [\-Dmacro[=defn]] +[\-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 @@ -30,7 +31,7 @@ different set of programs. The path given is used to locate \fIivlpp\fP, \fIivl\fP, code generators and the VPI modules. .TP 8 .B -c\fIfile\fP -f\fIfile\fP -These flags specifies an input file that contains a list of Verilog +These flags specify an input file that contains a list of Verilog source files. This is similar to the \fIcommand file\fP of other Verilog simulators, in that it is a file that contains the file names instead of taking them on the command line. See \fBCommand Files\fP below. @@ -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 (default) or disable 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. @@ -204,6 +222,12 @@ will suppress the warning that the compiler is making a choice. Use this switch to specify the target output format. See the \fBTARGETS\fP section below for a list of valid output formats. .TP 8 +.B -u +Treat each source file as a separate compilation unit (as defined in +SystemVerilog). If compiling for an \fIIEEE1364\fP generation, this +will just reset all compiler directives (including macro definitions) +before each new file is processed. +.TP 8 .B -v Turn on verbose messages. This will print the command lines that are executed to perform the actual compilation, along with version @@ -292,8 +316,14 @@ after a \fB\-Wall\fP argument to suppress isolated warning types. .TP 8 .B all -This enables the implicit, portbind, select\-range, timescale, and -sensitivity\-entire\-array warning categories. +This enables the anachronisms, implicit, macro-replacement, portbind, +select\-range, timescale, and sensitivity\-entire\-array warning +categories. + +.TP 8 +.B anachronisms +This enables warnings for use of features that have been deprecated +or removed in the selected generation of the Verilog language. .TP 8 .B implicit @@ -301,6 +331,13 @@ This enables warnings for creation of implicit declarations. For example, if a scalar wire X is used but not declared in the Verilog source, this will print a warning at its first use. +.TP 8 +.B macro-redefinition\fI | \fPmacro-replacement +This enables preprocessor warnings when a macro is being redefined. +The first variant prints a warning any time a macro is redefined. +The second variant only prints a warning if the macro text changes. +Use \fBno-macro-redefinition\fP to turn off all warnings of this type. + .TP 8 .B portbind This enables warnings for ports of module instantiations that are not @@ -379,6 +416,11 @@ The function returns an integer. The function returns a vector with the given width, and is signed or unsigned according to the flag. +.TP 8 +.B vpiSysFuncString +The function returns a string. This is an Icarus-specific extension, not +available in the VPI standard. + .SH "COMMAND FILES" The command file allows the user to place source file names and certain command line switches into a text file instead of on a long @@ -397,6 +439,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, @@ -520,14 +571,14 @@ Steve Williams (steve@icarus.com) .SH SEE ALSO vvp(1), -.BR "" +.BR "" Tips on using, debugging, and developing the compiler can be found at .BR "" .SH COPYRIGHT .nf -Copyright \(co 2002\-2014 Stephen Williams +Copyright \(co 2002\-2017 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 b34c541d5..b723d7ff3 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-2017 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,13 +38,13 @@ const char NOTICE[] = ; const char HELP[] = -"Usage: iverilog [-ESvV] [-B base] [-c cmdfile|-f cmdfile]\n" +"Usage: iverilog [-EiSuvV] [-B base] [-c cmdfile|-f cmdfile]\n" " [-g1995|-g2001|-g2005|-g2005-sv|-g2009|-g2012] [-g]\n" " [-D macro[=defn]] [-I includedir]\n" " [-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 = "shared-loop-index"; const char*gen_verilog_ams = "no-verilog-ams"; /* Boolean: true means use a default include dir, false means don't */ @@ -137,7 +138,12 @@ int gen_std_include = 1; of the include list. */ int gen_relative_include = 0; -char warning_flags[16] = "n"; +char warning_flags[17] = "n"; + +int separate_compilation_flag = 0; + +/* Boolean: true means ignore errors about missing modules */ +int ignore_missing_modules = 0; unsigned integer_width = 32; @@ -340,10 +346,15 @@ static int t_version_only(void) static void build_preprocess_command(int e_flag) { - snprintf(tmp, sizeof tmp, "%s%civlpp %s%s -F\"%s\" -f\"%s\" -p\"%s\" ", - ivlpp_dir, sep, verbose_flag?" -v":"", - e_flag?"":" -L", defines_path, source_path, - compiled_defines_path); + snprintf(tmp, sizeof tmp, "%s%civlpp %s%s%s -F\"%s\" -f\"%s\" -p\"%s\"%s", + ivlpp_dir, sep, + verbose_flag ? " -v" : "", + e_flag ? "" : " -L", + strchr(warning_flags, 'r') ? " -Wredef-all " : + strchr(warning_flags, 'R') ? " -Wredef-chg " : "", + defines_path, source_path, + compiled_defines_path, + e_flag ? "" : " | "); } static int t_preprocess_only(void) @@ -406,8 +417,12 @@ static int t_compile(void) { unsigned rc; - /* Start by building the preprocess command line. */ - build_preprocess_command(0); + /* Start by building the preprocess command line, if required. + This pipes into the main ivl command. */ + if (!separate_compilation_flag) + build_preprocess_command(0); + else + strcpy(tmp, ""); size_t ncmd = strlen(tmp); char*cmd = malloc(ncmd + 1); @@ -417,8 +432,8 @@ static int t_compile(void) int rtn; #endif - /* Build the ivl command and pipe it to the preprocessor. */ - snprintf(tmp, sizeof tmp, " | %s%civl", base, sep); + /* Build the ivl command. */ + snprintf(tmp, sizeof tmp, "%s%civl", base, sep); rc = strlen(tmp); cmd = realloc(cmd, ncmd+rc+1); strcpy(cmd+ncmd, tmp); @@ -446,7 +461,16 @@ static int t_compile(void) strcpy(cmd+ncmd, tmp); ncmd += rc; - snprintf(tmp, sizeof tmp, " -C\"%s\" -- -", iconfig_common_path); + snprintf(tmp, sizeof tmp, " -C\"%s\"", iconfig_common_path); + rc = strlen(tmp); + cmd = realloc(cmd, ncmd+rc+1); + strcpy(cmd+ncmd, tmp); + ncmd += rc; + + if (separate_compilation_flag) + snprintf(tmp, sizeof tmp, " -F\"%s\"", source_path); + else + snprintf(tmp, sizeof tmp, " -- -"); rc = strlen(tmp); cmd = realloc(cmd, ncmd+rc+1); strcpy(cmd+ncmd, tmp); @@ -496,6 +520,8 @@ static void process_warning_switch(const char*name) if (strcmp(name,"all") == 0) { process_warning_switch("anachronisms"); process_warning_switch("implicit"); + process_warning_switch("implicit-dimensions"); + process_warning_switch("macro-replacement"); process_warning_switch("portbind"); process_warning_switch("select-range"); process_warning_switch("timescale"); @@ -503,9 +529,21 @@ static void process_warning_switch(const char*name) } else if (strcmp(name,"anachronisms") == 0) { if (! strchr(warning_flags, 'n')) strcat(warning_flags, "n"); + } else if (strcmp(name,"floating-nets") == 0) { + if (! strchr(warning_flags, 'f')) + strcat(warning_flags, "f"); } else if (strcmp(name,"implicit") == 0) { if (! strchr(warning_flags, 'i')) strcat(warning_flags, "i"); + } else if (strcmp(name,"implicit-dimensions") == 0) { + if (! strchr(warning_flags, 'd')) + strcat(warning_flags, "d"); + } else if (strcmp(name,"macro-redefinition") == 0) { + if (! strchr(warning_flags, 'r')) + strcat(warning_flags, "r"); + } else if (strcmp(name,"macro-replacement") == 0) { + if (! strchr(warning_flags, 'R')) + strcat(warning_flags, "R"); } else if (strcmp(name,"portbind") == 0) { if (! strchr(warning_flags, 'p')) strcat(warning_flags, "p"); @@ -532,12 +570,35 @@ static void process_warning_switch(const char*name) cp[0] = cp[1]; cp += 1; } + } else if (strcmp(name,"no-floating-nets") == 0) { + char*cp = strchr(warning_flags, 'f'); + if (cp) while (*cp) { + cp[0] = cp[1]; + cp += 1; + } } else if (strcmp(name,"no-implicit") == 0) { char*cp = strchr(warning_flags, 'i'); if (cp) while (*cp) { cp[0] = cp[1]; cp += 1; } + } else if (strcmp(name,"no-implicit-dimensions") == 0) { + char*cp = strchr(warning_flags, 'd'); + if (cp) while (*cp) { + cp[0] = cp[1]; + cp += 1; + } + } else if (strcmp(name,"no-macro-redefinition") == 0) { + char*cp = strchr(warning_flags, 'r'); + if (cp) while (*cp) { + cp[0] = cp[1]; + cp += 1; + } + cp = strchr(warning_flags, 'R'); + if (cp) while (*cp) { + cp[0] = cp[1]; + cp += 1; + } } else if (strcmp(name,"no-portbind") == 0) { char*cp = strchr(warning_flags, 'p'); if (cp) while (*cp) { @@ -720,6 +781,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"; @@ -745,7 +812,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; } @@ -798,58 +866,108 @@ static void add_sft_file(const char *module) free(file); } +static void find_ivl_root_failed(const char *reason) +{ + fprintf(stderr, "Cannot locate IVL modules : %s\n", reason); + exit(1); +} + +static void find_ivl_root(void) +{ +#ifdef __MINGW32__ + const char *ivl_lib_prefix = "\\lib"; + const char *ivl_lib_suffix = "\\ivl" IVL_SUFFIX; +#else + const char *ivl_lib_prefix = IVL_LIB; + const char *ivl_lib_suffix = "/ivl" IVL_SUFFIX; +#endif + ssize_t len = 0; + char *s; + +#ifndef __MINGW32__ + /* First try the location specified in the build process. */ + if (access(IVL_ROOT, F_OK) != -1) { + assert(strlen(IVL_ROOT) < sizeof ivl_root); + strcpy(ivl_root, IVL_ROOT); + return; + } +#endif + /* If that fails, calculate the ivl_root from the path to the + command. This is always necessary on Windows because of the + installation process, but may also be necessary on other OSs + if the package has been relocated. + + On Windows we know the command path is formed like this: + + $(prefix)\bin\iverilog.exe + + The module path in a Windows installation is the path: + + $(prefix)\lib\ivl$(suffix) + + so we chop the file name and the last directory by + turning the last two \ characters to null. Then we append + the lib\ivl$(suffix) to finish. + + On other OSs, we expect the command path to be: + + $(prefix)/bin/iverilog + + and the module path to be: + + $(prefix)/$(lib)/ivl$(suffix) + + so we extract the $(prefix) from the command location as for + Windows and the $(lib) from IVL_LIB. This will of course fail + if the user has overridden $(bindir) or $(libdir), but there's + not a lot we can do in that case. + */ +#ifdef __MINGW32__ + char tmppath[MAXSIZE]; + len = GetModuleFileName(NULL, tmppath, sizeof tmppath); + if (len >= (ssize_t) sizeof ivl_root) { + find_ivl_root_failed("command path exceeds size of string buffer."); + } + /* Convert to a short name to remove any embedded spaces. */ + len = GetShortPathName(tmppath, ivl_root, sizeof ivl_root); +#else + len = readlink("/proc/self/exe", ivl_root, sizeof ivl_root); +#endif + if (len >= (ssize_t) sizeof ivl_root) { + find_ivl_root_failed("command path exceeds size of string buffer."); + } + if (len <= 0) { + find_ivl_root_failed("couldn't get command path from OS."); + } + s = strrchr(ivl_root, sep); + if (s == 0) { + find_ivl_root_failed("missing first separator in command path."); + } + *s = 0; + s = strrchr(ivl_root, sep); + if (s == 0) { + find_ivl_root_failed("missing second separator in command path."); + } + *s = 0; + len = s - ivl_root; + s = strrchr(ivl_lib_prefix, sep); + assert(s); + if (len + strlen(s) + strlen(ivl_lib_suffix) >= (ssize_t) sizeof ivl_root) { + find_ivl_root_failed("module path exceeds size of string buffer."); + } + strcat(ivl_root, s); + strcat(ivl_root, ivl_lib_suffix); +} + int main(int argc, char **argv) { int e_flag = 0; int version_flag = 0; int opt; -#ifdef __MINGW32__ - /* Calculate the ivl_root from the path to the command. This - is necessary because of the installation process on - Windows. Mostly, it is those darn drive letters, but oh - well. We know the command path is formed like this: - - D:\iverilog\bin\iverilog.exe - - The module path in a Windows installation is the path: - - D:\iverilog\lib\ivl$(suffix) - - so we chop the file name and the last directory by - turning the last two \ characters to null. Then we append - the lib\ivl$(suffix) to finish. */ - char *s; - char tmppath[MAXSIZE]; - GetModuleFileName(NULL, tmppath, sizeof tmppath); - /* Convert to a short name to remove any embedded spaces. */ - GetShortPathName(tmppath, ivl_root, sizeof ivl_root); - s = strrchr(ivl_root, sep); - if (s) *s = 0; - else { - fprintf(stderr, "%s: Missing first %c in exe path!\n", - argv[0], sep); - exit(1); - } - s = strrchr(ivl_root, sep); - if (s) *s = 0; - else { - fprintf(stderr, "%s: Missing second %c in exe path!\n", - argv[0], sep); - exit(1); - } - strcat(ivl_root, "\\lib\\ivl" IVL_SUFFIX); - + find_ivl_root(); base = ivl_root; -#else - /* In a UNIX environment, the IVL_ROOT from the Makefile is - dependable. It points to the $prefix/lib/ivl directory, - where the sub-parts are installed. */ - strcpy(ivl_root, IVL_ROOT); - base = ivl_root; -#endif - /* Create a temporary file for communicating input parameters to the preprocessor. */ source_path = strdup(my_tempfile("ivrlg", &source_file)); @@ -910,7 +1028,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:iM:m:N:o:P:p:Ss:T:t:uvVW:y:Y:")) != EOF) { switch (opt) { case 'B': @@ -965,6 +1083,14 @@ int main(int argc, char **argv) process_include_dir(optarg); break; + case 'i': + ignore_missing_modules = 1; + break; + + case 'l': + process_file_name(optarg, 1); + break; + case 'M': if (process_depfile(optarg) != 0) return -1; @@ -1005,6 +1131,9 @@ int main(int argc, char **argv) case 't': targ = optarg; break; + case 'u': + separate_compilation_flag = 1; + break; case 'v': verbose_flag = 1; break; @@ -1047,19 +1176,10 @@ int main(int argc, char **argv) if (version_flag || verbose_flag) { printf("Icarus Verilog version " VERSION " (" VERSION_TAG ")\n\n"); - printf("Copyright 1998-2013 Stephen Williams\n\n"); + printf("Copyright 1998-2017 Stephen Williams\n\n"); puts(NOTICE); } - if (synth_flag) { - fprintf(stderr, "Warning: Synthesis is not currently being " - "maintained and may not\n"); - fprintf(stderr, " function correctly. V0.8 was the " - "last release branch to\n"); - fprintf(stderr, " have active synthesis development " - "and support!\n"); - } - /* Make a common conf file path to reflect the target. */ snprintf(iconfig_common_path, sizeof iconfig_common_path, "%s%c%s%s.conf", base, sep, targ, synth_flag? "-s" : ""); @@ -1072,6 +1192,7 @@ int main(int argc, char **argv) how to handle them. */ fprintf(iconfig_file, "sys_func:%s%csystem.sft\n", base, sep); fprintf(iconfig_file, "sys_func:%s%cvhdl_sys.sft\n", base, sep); + fprintf(iconfig_file, "sys_func:%s%cvhdl_textio.sft\n", base, sep); /* If verilog-2005/09/12 is enabled or icarus-misc or verilog-ams, * then include the v2005_math library. */ @@ -1107,9 +1228,11 @@ 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); + fprintf(iconfig_file, "ignore_missing_modules:%s\n", ignore_missing_modules ? "true" : "false"); fprintf(iconfig_file, "out:%s\n", opath); if (depfile) { fprintf(iconfig_file, "depfile:%s\n", depfile); @@ -1186,8 +1309,12 @@ int main(int argc, char **argv) will append to the file, so this is necessary to make sure it starts out empty. */ if (depfile) { - FILE*fd = fopen(depfile, "w"); - fclose(fd); + FILE *fd = fopen(depfile, "w"); + if (!fd) { + fprintf(stderr, "%s: can't open %s file.\n\n%s\n", argv[0], depfile, HELP); + return 1; + } + fclose(fd); } if (source_count == 0 && !version_flag) { @@ -1202,8 +1329,12 @@ int main(int argc, char **argv) /* Write the preprocessor command needed to preprocess a single file. This may be used to preprocess library files. */ - fprintf(iconfig_file, "ivlpp:%s%civlpp -L -F\"%s\" -P\"%s\"\n", - ivlpp_dir, sep, defines_path, compiled_defines_path); + fprintf(iconfig_file, "ivlpp:%s%civlpp %s -L -F\"%s\" -P\"%s\"\n", + ivlpp_dir, sep, + strchr(warning_flags, 'r') ? "-Wredef-all" : + strchr(warning_flags, 'R') ? "-Wredef-chg" : "", + defines_path, compiled_defines_path + ); /* Done writing to the iconfig file. Close it now. */ fclose(iconfig_file); diff --git a/elab_expr.cc b/elab_expr.cc index e309f6162..68b49f645 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-2018 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); } /* @@ -329,10 +329,12 @@ unsigned PEBinary::test_width(Design*des, NetScope*scope, width_mode_t&mode) case '>': // > Should be handled by PEBComp case 'e': // == Should be handled by PEBComp case 'E': // === Should be handled by PEBComp + case 'w': // ==? Should be handled by PEBComp case 'L': // <= Should be handled by PEBComp case 'G': // >= Should be handled by PEBComp case 'n': // != Should be handled by PEBComp case 'N': // !== Should be handled by PEBComp + case 'W': // !=? Should be handled by PEBComp case 'p': // ** should be handled by PEBPower ivl_assert(*this, 0); default: @@ -668,6 +670,18 @@ NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, return 0; } break; + case 'w': /* ==? */ + case 'W': /* !=? */ + if ((lp->expr_type() != IVL_VT_BOOL && lp->expr_type() != IVL_VT_LOGIC) || + (rp->expr_type() != IVL_VT_BOOL && rp->expr_type() != IVL_VT_LOGIC)) { + cerr << get_fileline() << ": error: " + << human_readable_op(op_) + << " operator may only have INTEGRAL operands." + << endl; + des->errors += 1; + return 0; + } + break; default: break; } @@ -675,10 +689,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 +727,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 +1043,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; @@ -1277,7 +1284,6 @@ unsigned PECallFunction::test_width_method_(Design*des, NetScope*scope, return 0; perm_string member_name; - ivl_type_t member_type = 0; pform_name_t use_path = path_; perm_string method_name = peek_tail_name(use_path); use_path.pop_back(); @@ -1317,11 +1323,8 @@ unsigned PECallFunction::test_width_method_(Design*des, NetScope*scope, const netclass_t* class_type = net->class_type(); int midx = class_type->property_idx_from_name(member_name); - if (midx >= 0) - member_type = class_type->get_prop_type(midx); - else - member_type = 0; - use_path = tmp_path; + ivl_type_t member_type = 0; + if (midx >= 0) member_type = class_type->get_prop_type(midx); use_darray = dynamic_cast (member_type); @@ -1335,7 +1338,7 @@ unsigned PECallFunction::test_width_method_(Design*des, NetScope*scope, if (net == 0) return 0; - // Look fonr built in string attributes. + // Look for built in string attributes. if (net->data_type()==IVL_VT_STRING) { if (method_name == "len") { @@ -1410,27 +1413,11 @@ NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, unsigned wid) const if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::cast_to_width_: " << "cast to " << wid - << " bits " << (signed_flag_?"signed":"unsigned") + << " bits " << (signed_flag_ ? "signed" : "unsigned") << " 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); } /* @@ -1469,6 +1456,13 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, return 0; } + if (!type_is_vectorable(expr_type_)) { + cerr << get_fileline() << ": error: The argument to " + << name << " must be a vector type." << endl; + des->errors += 1; + return 0; + } + if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::elaborate_sfunc_: " << name << " expression is the argument cast to expr_wid=" << expr_wid << endl; @@ -1535,7 +1529,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, PExpr*expr = parms_[0]; - verinum val (expr->has_sign()? verinum::V1 : verinum::V0, 1); + verinum val (expr->has_sign() ? verinum::V1 : verinum::V0, 1); NetEConst*sub = new NetEConst(val); sub->set_line(*this); @@ -1576,6 +1570,18 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, bool need_const = NEED_CONST & flags; + /* These functions can work in a constant context with a signal expression. */ + if ((nparms == 1) && (dynamic_cast(parms_[0]))) { + if (strcmp(name, "$dimensions") == 0) need_const = false; + else if (strcmp(name, "$high") == 0) need_const = false; + else if (strcmp(name, "$increment") == 0) need_const = false; + else if (strcmp(name, "$left") == 0) need_const = false; + else if (strcmp(name, "$low") == 0) need_const = false; + else if (strcmp(name, "$right") == 0) need_const = false; + else if (strcmp(name, "$size") == 0) need_const = false; + else if (strcmp(name, "$unpacked_dimensions") == 0) need_const = false; + } + unsigned parm_errors = 0; unsigned missing_parms = 0; for (unsigned idx = 0 ; idx < nparms ; idx += 1) { @@ -1597,7 +1603,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, if (missing_parms > 0) { cerr << get_fileline() << ": error: The function " << name - << " has been called with empty parameters." << endl; + << " has been called with missing/empty parameters." << endl; cerr << get_fileline() << ": : Verilog doesn't allow " << "passing empty parameters to functions." << endl; des->errors += 1; @@ -1606,10 +1612,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 +1669,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); } /* @@ -1678,7 +1678,7 @@ NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope, static NetExpr* check_for_enum_methods(const LineInfo*li, Design*des, NetScope*scope, const netenum_t*netenum, - pform_name_t use_path, + const pform_name_t&use_path, perm_string method_name, NetExpr*expr, unsigned rtn_wid, @@ -2017,7 +2017,7 @@ static NetExpr* check_for_struct_members(const LineInfo*li, } NetESignal*sig = new NetESignal(net); - NetExpr *base = packed_base? packed_base : make_const_val(off); + NetExpr *base = packed_base ? packed_base : make_const_val(off); if (debug_elaborate) { cerr << li->get_fileline() << ": debug: check_for_struct_members: " @@ -2281,15 +2281,12 @@ 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 " - "function return value for " << path_ - << " in " << dscope->basename() << "." << endl; + "function return value for " << path_ + << " in " << dscope->basename() << "." << endl; des->errors += 1; return 0; } @@ -2313,6 +2310,21 @@ unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, const unsigned parm_count = parms.size() - parm_off; const unsigned actual_count = parms_.size(); + /* The parser can't distinguish between a function call with + no arguments and a function call with one empty argument, + and always supplies one empty argument. Handle the no + argument case here. */ + if ((parm_count == 0) && (actual_count == 1) && (parms_[0] == 0)) + return 0; + + if (actual_count > parm_count) { + cerr << get_fileline() << ": error: " + << "Too many arguments (" << actual_count + << ", expecting " << parm_count << ")" + << " in call to function." << endl; + des->errors += 1; + } + for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { unsigned pidx = idx + parm_off; PExpr*tmp = (idx < actual_count) ? parms_[idx] : 0; @@ -2343,7 +2355,7 @@ unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, } else if (def->port_defe(pidx)) { if (! gn_system_verilog()) { cerr << get_fileline() << ": internal error: " - <<"Found (and using) default function argument " + << "Found (and using) default function argument " << "requires SystemVerilog." << endl; des->errors += 1; } @@ -2357,7 +2369,7 @@ unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, if (missing_parms > 0) { cerr << get_fileline() << ": error: The function " << path_ - << " has been called with empty parameters." << endl; + << " has been called with missing/empty parameters." << endl; cerr << get_fileline() << ": : Verilog doesn't allow " << "passing empty parameters to functions." << endl; parm_errors += 1; @@ -2506,22 +2518,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) @@ -2567,7 +2611,7 @@ NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope, // Find rounded up length that can fit the whole casted array of vectors int len = base->expr_width() + vector->packed_width() - 1; - if(base->expr_width() > vector->packed_width()) { + if(base->expr_width() > (unsigned)vector->packed_width()) { len /= vector->packed_width(); } else { len /= base->expr_width(); @@ -2625,17 +2669,15 @@ NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope, if((base_->expr_type() != IVL_VT_BOOL) && (base_->expr_type() != IVL_VT_LOGIC)) { - cerr << get_fileline() << ": cannot be casted to string." << endl; + cerr << get_fileline() << ": cannot be cast to a string." << endl; ivl_assert(*this, false); } return expr; } - cerr << get_fileline() << ": sorry: I don't know how to cast expression." << endl; - ivl_assert(*this, false); - - return expr; + cerr << get_fileline() << ": sorry: This cast operation is not yet supported." << endl; + return 0; } unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&) @@ -2666,7 +2708,7 @@ unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&) expr_is_string = NO; } - expr_type_ = (expr_is_string==YES)? IVL_VT_STRING : IVL_VT_LOGIC; + expr_type_ = (expr_is_string==YES) ? IVL_VT_STRING : IVL_VT_LOGIC; signed_flag_ = false; // If there is a repeat expression, then evaluate the constant @@ -2733,6 +2775,7 @@ NetExpr* PEConcat::elaborate_expr(Design*, NetScope*, tmp->set_line(*this); return tmp; } + // fallthrough default: cerr << get_fileline() << ": internal error: " << "I don't know how to elaborate(ivl_type_t)" @@ -2827,17 +2870,16 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, concat->set(idx, parms[off+idx]); } - if (wid_sum == 0 && concat_depth < 2) { - cerr << get_fileline() << ": error: Concatenation may not " - << "have zero width in this context." << endl; + if (wid_sum == 0 && expr_type_ != IVL_VT_STRING) { + cerr << get_fileline() << ": error: Concatenation/replication " + << "may not have zero width in this context." << endl; des->errors += 1; concat_depth -= 1; delete concat; 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; @@ -2924,7 +2966,7 @@ bool PEIdent::calculate_bits_(Design*des, NetScope*scope, NetEConst*msb_c = dynamic_cast(msb_ex); if (msb_c == 0) { cerr << index_tail.msb->get_fileline() << ": error: " - "Bit select expressionsmust be constant." + "Bit select expressions must be constant." << endl; cerr << index_tail.msb->get_fileline() << ": : " "This msb expression violates the rule: " @@ -3020,7 +3062,7 @@ bool PEIdent::calculate_up_do_width_(Design*des, NetScope*scope, NetExpr*wid_ex = elab_and_eval(des, scope, index_tail.lsb, -1, true); NetEConst*wid_c = dynamic_cast(wid_ex); - wid = wid_c? wid_c->value().as_ulong() : 0; + wid = wid_c ? wid_c->value().as_ulong() : 0; if (wid == 0) { cerr << index_tail.lsb->get_fileline() << ": error: " "Indexed part widths must be constant and greater than zero." @@ -3121,6 +3163,25 @@ unsigned PEIdent::test_width_method_(Design*des, NetScope*scope, width_mode_t&) } } + // Look for the enumeration attributes. + if (const netenum_t*netenum = net->enumeration()) { + if (member_name == "num") { + expr_type_ = IVL_VT_BOOL; + expr_width_ = 32; + min_width_ = 32; + signed_flag_= true; + return 32; + } + if ((member_name == "first") || (member_name == "last") || + (member_name == "next") || (member_name == "prev")) { + expr_type_ = netenum->base_type(); + expr_width_ = netenum->packed_width();; + min_width_ = expr_width_; + signed_flag_ = netenum->get_signed(); + return expr_width_; + } + } + return 0; } @@ -3169,7 +3230,7 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) bool parts_defined; calculate_parts_(des, scope, msb, lsb, parts_defined); if (parts_defined) - use_width = 1 + ((msb>lsb)? (msb-lsb) : (lsb-msb)); + use_width = 1 + ((msb>lsb) ? (msb-lsb) : (lsb-msb)); else use_width = UINT_MAX; break; @@ -3203,7 +3264,7 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) ivl_assert(*this, 0); } - if (const netdarray_t*darray = net? net->darray_type() : 0) { + if (const netdarray_t*darray = net ? net->darray_type() : 0) { switch (use_sel) { case index_component_t::SEL_BIT: case index_component_t::SEL_BIT_LAST: @@ -3257,7 +3318,7 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) << net->name() << " is a net, " << "type=" << expr_type_ << ", width=" << expr_width_ - << ", signed_=" << (signed_flag_?"true":"false") + << ", signed_=" << (signed_flag_ ? "true" : "false") << ", use_depth=" << use_depth << ", packed_dimensions=" << net->packed_dimensions() << ", unpacked_dimensions=" << net->unpacked_dimensions() @@ -3428,29 +3489,41 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, } if (net == 0) { - cerr << get_fileline() << ": internal error: " - << "Expecting idents with ntype to be signals." << endl; + cerr << get_fileline() << ": error: Unable to bind variable `" + << path_ << "' in `" << scope_path(use_scope) << "'" << endl; des->errors += 1; return 0; } - if (! ntype->type_compatible(net->net_type())) { - cerr << get_fileline() << ": internal_error: " - << "net type doesn't match context type." << endl; + if (const netdarray_t*array_type = dynamic_cast (ntype)) { + if (array_type->type_compatible(net->net_type())) { + NetESignal*tmp = new NetESignal(net); + tmp->set_line(*this); + return tmp; + } - cerr << get_fileline() << ": : " - << "net type="; + // Icarus allows a dynamic array to be initialised with a + // single elementary value, so try that next. + ntype = array_type->element_type(); + } + + if (! ntype->type_compatible(net->net_type())) { + cerr << get_fileline() << ": error: the type of the variable '" + << path_ << "' doesn't match the context type." << endl; + + cerr << get_fileline() << ": : " << "variable type="; if (net->net_type()) net->net_type()->debug_dump(cerr); else cerr << ""; cerr << endl; - cerr << get_fileline() << ": : " - << "context type="; + cerr << get_fileline() << ": : " << "context type="; ivl_assert(*this, ntype); ntype->debug_dump(cerr); cerr << endl; + des->errors += 1; + return 0; } ivl_assert(*this, ntype->type_compatible(net->net_type())); @@ -3490,7 +3563,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, indices_flags idx_flags; indices_to_expressions(des, scope, this, use_comp.index, net->unpacked_dimensions(), - need_const, net->unpacked_count(), + need_const, idx_flags, unpacked_indices, unpacked_indices_const); @@ -3754,10 +3827,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) @@ -3793,10 +3863,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 @@ -3843,7 +3910,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, } // Maybe this is a method attached to an enumeration name? If - // this is system verilog, then test to see if the name is + // this is SystemVerilog, then test to see if the name is // really a method attached to an object. if (gn_system_verilog() && found_in==0 && path_.size() >= 2) { pform_name_t use_path = path_; @@ -3896,9 +3963,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) { @@ -3921,7 +3986,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, if ( !(SYS_TASK_ARG & flags) ) { // I cannot interpret this identifier. Error message. cerr << get_fileline() << ": error: Unable to bind " - << (NEED_CONST & flags ? "parameter" : "wire/reg/memory") + << ((NEED_CONST & flags) ? "parameter" : "wire/reg/memory") << " `" << path_ << "' in `" << scope_path(scope) << "'" << endl; if (scope->need_const_func()) { @@ -4015,7 +4080,7 @@ static verinum param_part_select_bits(const verinum&par_val, long wid, // If the input is a string, and the part select is working on // byte boundaries, then make the result into a string. if (par_val.is_string() && (labs(lsv)%8 == 0) && (wid%8 == 0)) - return result.as_string(); + return verinum(result.as_string()); return result; } @@ -4477,7 +4542,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_); @@ -4563,7 +4628,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, indices_flags idx_flags; indices_to_expressions(des, scope, this, name_tail.index, net->unpacked_dimensions(), - need_const, net->unpacked_count(), + need_const, idx_flags, unpacked_indices, unpacked_indices_const); @@ -4996,6 +5061,8 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, ivl_assert(*this, index_tail.lsb == 0); NetExpr*mux = elab_and_eval(des, scope, index_tail.msb, -1, need_const); + if (!mux) + return 0; if (const netdarray_t*darray = net->sig()->darray_type()) { // Special case: This is a select of a dynamic @@ -5312,9 +5379,8 @@ NetExpr* PENewArray::elaborate_expr(Design*des, NetScope*scope, // expression. Elaborate the expression as an element // type. The run-time will assign this value to each element. const netarray_t*array_type = dynamic_cast (ntype); - ivl_type_t elem_type = array_type->element_type(); - init_val = init_->elaborate_expr(des, scope, elem_type, flags); + init_val = init_->elaborate_expr(des, scope, array_type, flags); } NetENew*tmp = new NetENew(ntype, size, init_val); @@ -5565,6 +5631,10 @@ unsigned PENumber::test_width(Design*, NetScope*, width_mode_t&mode) NetExpr* PENumber::elaborate_expr(Design*des, NetScope*, ivl_type_t ntype, unsigned) const { + // Icarus allows dynamic arrays to be initialised with a single value. + if (const netdarray_t*array_type = dynamic_cast (ntype)) + ntype = array_type->element_type(); + const netvector_t*use_type = dynamic_cast (ntype); if (use_type == 0) { cerr << get_fileline() << ": internal error: " @@ -5611,7 +5681,7 @@ NetEConst* PENumber::elaborate_expr(Design*, NetScope*, unsigned PEString::test_width(Design*, NetScope*, width_mode_t&) { expr_type_ = IVL_VT_BOOL; - expr_width_ = text_? verinum(text_).len() : 0; + expr_width_ = text_ ? verinum(text_).len() : 0; min_width_ = expr_width_; signed_flag_ = false; @@ -5671,7 +5741,6 @@ unsigned PETernary::test_width(Design*des, NetScope*scope, width_mode_t&mode) } else if (tru_type == IVL_VT_LOGIC || fal_type == IVL_VT_LOGIC) { expr_type_ = IVL_VT_LOGIC; } else { - ivl_assert(*this, tru_type == fal_type); expr_type_ = tru_type; } if (expr_type_ == IVL_VT_REAL) { @@ -6067,7 +6136,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 @@ -6085,7 +6154,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 7677921b1..d1ee58fb7 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 @@ -75,15 +75,8 @@ */ NetAssign_* PExpr::elaborate_lval(Design*, NetScope*, bool, bool) const { - NetNet*ll = 0; - if (ll == 0) { - cerr << get_fileline() << ": Assignment l-value too complex." - << endl; - return 0; - } - - NetAssign_*lv = new NetAssign_(ll); - return lv; + cerr << get_fileline() << ": Assignment l-value too complex." << endl; + return 0; } /* @@ -138,6 +131,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 +283,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 +395,7 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, /* No select expressions. */ NetAssign_*lv = new NetAssign_(reg); + lv->set_signed(reg->get_signed()); return lv; } @@ -485,7 +482,7 @@ NetAssign_* PEIdent::elaborate_lval_method_class_member_(Design*des, if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: " - << "Found initialzers for property " << class_type->get_prop_name(pidx) << endl; + << "Found initializers for property " << class_type->get_prop_name(pidx) << endl; } } } @@ -543,8 +540,6 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, return 0; } - unsigned array_need_words = reg->unpacked_count(); - // Make sure there are enough indices to address an array element. const index_component_t&index_head = name_tail.index.front(); if (index_head.sel == index_component_t::SEL_PART) { @@ -562,7 +557,7 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, indices_flags flags; indices_to_expressions(des, scope, this, name_tail.index, reg->unpacked_dimensions(), - false, array_need_words, + false, flags, unpacked_indices, unpacked_indices_const); @@ -689,20 +684,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; } @@ -799,12 +798,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) { @@ -870,10 +867,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); @@ -936,11 +936,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; } @@ -630,7 +657,7 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, indices_flags flags; indices_to_expressions(des, scope, this, path_tail.index, sig->unpacked_dimensions(), - true, sig->unpacked_count(), + true, flags, unpacked_indices, unpacked_indices_const); diff --git a/elab_scope.cc b/elab_scope.cc index 6a0e00075..cfbc2a50d 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 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->has_explicit_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, @@ -158,8 +167,7 @@ static void collect_scope_specparams_(Design*des, NetScope*scope, } /* - * Elaborate the enumeration into the given scope. If scope==0, then - * the enumeration goes into $root instead of a scope. + * Elaborate the enumeration into the given scope. */ static void elaborate_scope_enumeration(Design*des, NetScope*scope, enum_type_t*enum_type) @@ -184,10 +192,7 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope, enum_type); use_enum->set_line(enum_type->li); - if (scope) - scope->add_enumeration_set(enum_type, use_enum); - else - des->add_enumeration_set(enum_type, use_enum); + scope->add_enumeration_set(enum_type, use_enum); size_t name_idx = 0; // Find the enumeration width. @@ -355,10 +360,7 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope, } rc_flag = use_enum->insert_name(name_idx, cur->name, cur_value); - if (scope) - rc_flag &= scope->add_enumeration_name(use_enum, cur->name); - else - rc_flag &= des->add_enumeration_name(use_enum, cur->name); + rc_flag &= scope->add_enumeration_name(use_enum, cur->name); if (! rc_flag) { cerr << use_enum->get_fileline() @@ -388,15 +390,6 @@ static void elaborate_scope_enumerations(Design*des, NetScope*scope, } } -void elaborate_rootscope_enumerations(Design*des) -{ - for (set::const_iterator cur = pform_enum_sets.begin() - ; cur != pform_enum_sets.end() ; ++ cur) { - enum_type_t*curp = *cur; - elaborate_scope_enumeration(des, 0, curp); - } -} - /* * If the pclass includes an implicit and explicit constructor, then * merge the implicit constructor into the explicit constructor as @@ -500,7 +493,6 @@ static void elaborate_scope_class(Design*des, NetScope*scope, PClass*pclass) netclass_t*use_base_class = 0; if (base_class) { - ivl_assert(*pclass, scope); use_base_class = scope->find_class(base_class->name); if (use_base_class == 0) { cerr << pclass->get_fileline() << ": error: " @@ -516,13 +508,15 @@ static void elaborate_scope_class(Design*des, NetScope*scope, PClass*pclass) use_type->save_elaborated_type = use_class; // Class scopes have no parent scope, because references are - // not allowed to escape a class method. + // not allowed to escape a class method. But they are allowed + // to reference the compilation unit scope. NetScope*class_scope = new NetScope(0, hname_t(pclass->pscope_name()), - NetScope::CLASS); + NetScope::CLASS, scope->unit()); class_scope->set_line(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. @@ -576,12 +570,7 @@ static void elaborate_scope_class(Design*des, NetScope*scope, PClass*pclass) cur->second->elaborate_scope(des, method_scope); } - if (scope) { - scope->add_class(use_class); - - } else { - des->add_class(use_class, pclass); - } + scope->add_class(use_class); } static void elaborate_scope_classes(Design*des, NetScope*scope, @@ -593,18 +582,6 @@ static void elaborate_scope_classes(Design*des, NetScope*scope, } } -void elaborate_rootscope_classes(Design*des) -{ - if (pform_classes.empty()) - return; - - for (map::iterator cur = pform_classes.begin() - ; cur != pform_classes.end() ; ++ cur) { - blend_class_constructors(cur->second); - elaborate_scope_class(des, 0, cur->second); - } -} - static void replace_scope_parameters_(NetScope*scope, const LineInfo&loc, const Module::replace_t&replacements) { @@ -654,9 +631,6 @@ 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) - des->add_root_task(task_scope, task); - if (debug_scopes) { cerr << task->get_fileline() << ": elaborate_scope_task: " << "Elaborate task scope " << scope_path(task_scope) << endl; @@ -719,9 +693,6 @@ 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) - des->add_root_task(task_scope, task); - if (debug_scopes) { cerr << task->get_fileline() << ": elaborate_scope_func: " << "Elaborate task scope " << scope_path(task_scope) << endl; @@ -777,28 +748,6 @@ static void elaborate_scope_funcs(Design*des, NetScope*scope, } -void elaborate_rootscope_tasks(Design*des) -{ - for (map::iterator cur = pform_tasks.begin() - ; cur != pform_tasks.end() ; ++ cur) { - - if (PTask*task = dynamic_cast (cur->second)) { - elaborate_scope_task(des, 0, task); - continue; - } - - if (PFunction*func = dynamic_cast(cur->second)) { - elaborate_scope_func(des, 0, func); - continue; - } - - cerr << cur->second->get_fileline() << ": internal error: " - << "elabortae_rootscope_tasks does not understand " - << "this object," << endl; - des->errors += 1; - } -} - class generate_schemes_work_item_t : public elaborator_work_item_t { public: generate_schemes_work_item_t(Design*des__, NetScope*scope, Module*mod) @@ -1155,7 +1104,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) container->genvar_tmp_val = genvar; delete step; delete test_ex; - test_ex = elab_and_eval(des, container, loop_test, -1); + test_ex = elab_and_eval(des, container, loop_test, -1, true); test = dynamic_cast(test_ex); assert(test); } @@ -1714,6 +1663,10 @@ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*s << "." << endl; } + struct attrib_list_t*attrib_list; + unsigned attrib_list_n = 0; + attrib_list = evaluate_attributes(attributes, attrib_list_n, des, sc); + // Run through the module instances, and make scopes out of // them. Also do parameter overrides that are done on the // instantiation line. @@ -1740,7 +1693,7 @@ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*s // Create the new scope as a MODULE with my name. Note // that if this is a nested module, mark it thus so that // scope searches will continue into the parent scope. - NetScope*my_scope = new NetScope(sc, use_name, NetScope::MODULE, + NetScope*my_scope = new NetScope(sc, use_name, NetScope::MODULE, 0, bound_type_? true : false, mod->program_block, mod->is_interface); @@ -1748,13 +1701,12 @@ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*s get_lineno(), mod->get_lineno()); my_scope->set_module_name(mod->mod_name()); + for (unsigned adx = 0 ; adx < attrib_list_n ; adx += 1) + my_scope->attribute(attrib_list[adx].key, attrib_list[adx].val); + 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 @@ -1793,8 +1745,12 @@ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*s // so the mapping into the replace list is much easier. if (parms_) { assert(overrides_ == 0); - for (unsigned jdx = 0 ; jdx < nparms_ ; jdx += 1) - replace[parms_[jdx].name] = parms_[jdx].parm; + for (unsigned jdx = 0 ; jdx < nparms_ ; jdx += 1) { + // No expression means that the parameter is not + // replaced. + if (parms_[jdx].parm) + replace[parms_[jdx].name] = parms_[jdx].parm; + } } @@ -1806,6 +1762,7 @@ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*s mod->elaborate_scope(des, my_scope, replace); } + delete[]attrib_list; /* Stash the instance array of scopes into the parent scope. Later elaboration passes will use this vector to diff --git a/elab_sig.cc b/elab_sig.cc index b33e993b9..efe1997a4 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-2017 Stephen Williams (steve@icarus.com) * Copyright CERN 2012 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -715,16 +715,23 @@ void PTaskFunc::elaborate_sig_ports_(Design*des, NetScope*scope, continue; } - // If the port has a default expression that can be used - // as a value when the caller doesn't bind, then - // elaborate that expression here. This expression - // should evaluate down do a constant. + // If the port has a default expression, elaborate + // that expression here. if (ports_->at(idx).defe != 0) { - tmp_def = elab_and_eval(des, scope, ports_->at(idx).defe, -1, true); - if (tmp_def==0) { - cerr << get_fileline() << ": error: Unable to evaluate " - << *ports_->at(idx).defe - << " as a port default (constant) expression." << endl; + if (tmp->port_type() == NetNet::PINPUT) { + tmp_def = elab_and_eval(des, scope, ports_->at(idx).defe, + -1, scope->need_const_func()); + if (tmp_def == 0) { + cerr << get_fileline() + << ": error: Unable to evaluate " + << *ports_->at(idx).defe + << " as a port default expression." << endl; + des->errors += 1; + } + } else { + cerr << get_fileline() << ": sorry: Default arguments " + "for subroutine output or inout ports are not " + "yet supported." << endl; des->errors += 1; } } @@ -969,6 +976,23 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const } if (port_set_ || net_set_) { + + if (warn_implicit_dimensions + && port_set_ && net_set_ + && net_.empty() && !port_.empty()) { + cerr << get_fileline() << ": warning: " + << "var/net declaration of " << basename() + << " inherits dimensions from port declaration." << endl; + } + + if (warn_implicit_dimensions + && port_set_ && net_set_ + && port_.empty() && !net_.empty()) { + cerr << get_fileline() << ": warning: " + << "Port declaration of " << basename() + << " inherits dimensions from var/net." << endl; + } + bool bad_range = false; vector plist, nlist; /* If they exist get the port definition MSB and LSB */ @@ -1161,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; @@ -1171,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; - } } @@ -1309,27 +1333,3 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const return sig; } - - -void Design::root_elaborate_sig(void) -{ - for (map::const_iterator cur = classes_.begin() - ; cur != classes_.end() ; ++ cur) { - - netclass_t*cur_class = cur->second; - PClass*cur_pclass = class_to_pclass_[cur_class]; - - cur_class->elaborate_sig(this, cur_pclass); - } - - for (map::iterator cur = root_tasks_.begin() - ; cur != root_tasks_.end() ; ++ cur) { - - if (debug_elaborate) { - cerr << cur->second->get_fileline() << ": root_elaborate_sig: " - << "Elaborate_sig for root task/func " << scope_path(cur->first) << endl; - } - - cur->second->elaborate_sig(this, cur->first); - } -} diff --git a/elab_type.cc b/elab_type.cc index 3ff85a9d8..73d7842dc 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-2017 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" @@ -76,13 +78,12 @@ static void elaborate_array_ranges(Design*des, NetScope*scope, */ ivl_type_s* data_type_t::elaborate_type(Design*des, NetScope*scope) { + ivl_assert(*this, scope); Definitions*use_definitions = scope; - if (use_definitions == 0) - use_definitions = des; map::iterator pos = cache_type_elaborate_.lower_bound(use_definitions); - if (pos->first == use_definitions) - return pos->second; + if (pos != cache_type_elaborate_.end() && pos->first == use_definitions) + return pos->second; ivl_type_s*tmp = elaborate_type_raw(des, scope); cache_type_elaborate_.insert(pos, pair(scope, tmp)); @@ -145,13 +146,13 @@ ivl_type_s* class_type_t::elaborate_type_raw(Design*, NetScope*) const * available at the right time. At that time, the netenum_t* object is * stashed in the scope so that I can retrieve it here. */ -ivl_type_s* enum_type_t::elaborate_type_raw(Design*des, NetScope*scope) const +ivl_type_s* enum_type_t::elaborate_type_raw(Design*, NetScope*scope) const { ivl_assert(*this, scope); ivl_type_s*tmp = scope->enumeration_for_key(this); - if (tmp) return tmp; - - tmp = des->enumeration_for_key(this); + if (tmp == 0 && scope->unit()) { + tmp = scope->unit()->enumeration_for_key(this); +} return tmp; } @@ -245,6 +246,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 1693b3215..588ccf11a 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2018 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? " << @@ -105,24 +109,6 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const return; } -#if 0 - // MTW, 01-Mar-2013. The expression elaboration rework should have - // ensured that this can no longer occur. Leaving this here for the - // moment, but it should be safe to remove it. - if (type_is_vectorable(rval_expr->expr_type()) - && type_is_vectorable(lval->data_type()) - && rval_expr->expr_width() < lval->vector_width()) { - if (debug_elaborate) { - cerr << get_fileline() << ": debug: " - << "r-value expressions width "<expr_width() - << " of " << (rval_expr->has_sign()? "signed":"unsigned") - << " expression is to small for l-value width " - << lval->vector_width() << "." << endl; - } - rval_expr = pad_to_width(rval_expr, lval->vector_width(), *this); - } -#endif - NetNet*rval = rval_expr->synthesize(des, scope, rval_expr); if (rval == 0) { @@ -787,7 +773,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; @@ -1287,15 +1273,18 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const for (unsigned idx = 0 ; idx < pins.size() ; idx += 1) { bool unconnected_port = false; + perm_string port_name = rmod->get_port_name(idx); + // Skip unconnected module ports. This happens when a // null parameter is passed in. if (pins[idx] == 0) { if (pins_fromwc[idx]) { - cerr << get_fileline() << ": error: Wildcard named port " << - "connection (.*) did not find a matching identifier " << - "for port '" << rmod->ports[idx]->name << "'." << endl; + cerr << get_fileline() << ": error: Wildcard named " + "port connection (.*) did not find a matching " + "identifier for port " << (idx+1) << " (" + << port_name << ")." << endl; des->errors += 1; return; } @@ -1330,22 +1319,22 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const break; } - // Print a waring for an unconnected input. + // Print a warning for an unconnected input. if (warn_portbinding) { cerr << get_fileline() << ": warning: " << "Instantiating module " << rmod->mod_name() - << " with dangling input port '" - << rmod->ports[idx]->name; + << " with dangling input port " + << (idx+1) << " (" << port_name; switch (rmod->uc_drive) { case Module::UCD_PULL0: - cerr << "' (pulled low)." << endl; + cerr << ") pulled low." << endl; break; case Module::UCD_PULL1: - cerr << "' (pulled high)." << endl; + cerr << ") pulled high." << endl; break; case Module::UCD_NONE: - cerr << "' (floating)." << endl; + cerr << ") floating." << endl; break; } } @@ -1361,8 +1350,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const if (debug_elaborate) { cerr << get_fileline() << ": debug: " << get_name() - << ": Port " << (idx+1) << " has " << prts.size() - << " sub-ports." << endl; + << ": Port " << (idx+1) << " (" << port_name + << ") has " << prts.size() << " sub-ports." << endl; } // Count the internal vector bits of the port. @@ -1394,7 +1383,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const prt_vector_width += port_width; ptype = PortType::merged(netnet->port_type(), ptype); } - inst_scope->add_module_port_info(idx, rmod->get_port_name(idx), ptype, prt_vector_width ); + inst_scope->add_module_port_info(idx, port_name, ptype, prt_vector_width ); } // If I find that the port is unconnected inside the @@ -1418,12 +1407,23 @@ 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. NetNet*sig = 0; - if (prts.empty() || (prts[0]->port_type() == NetNet::PINPUT)) { + NetNet::PortType ptype = prts[0]->port_type(); + if (prts.empty() || (ptype == NetNet::PINPUT)) { // Special case: If the input port is an unpacked // array, then there should be no sub-ports and @@ -1454,8 +1454,9 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const NetExpr*tmp_expr = elab_and_eval(des, scope, pins[idx], -1); if (tmp_expr == 0) { cerr << pins[idx]->get_fileline() - << ": internal error: Port expression " - << "too complicated for elaboration." << endl; + << ": error: Failed to elaborate port expression." + << endl; + des->errors += 1; continue; } @@ -1515,7 +1516,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const sig->vector_width()); } - } else if (prts[0]->port_type() == NetNet::PINOUT) { + } else if (ptype == NetNet::PINOUT) { // For now, do not support unpacked array outputs. ivl_assert(*this, prts[0]->unpacked_dimensions()==0); @@ -1541,8 +1542,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const cerr << pins[idx]->get_fileline() << ": error: " << "Inout port expression must support " << "continuous assignment." << endl; - cerr << pins[idx]->get_fileline() << ": : " - << "Port " << rmod->ports[idx]->name << " of " + cerr << pins[idx]->get_fileline() << ": : Port " + << (idx+1) << " (" << port_name << ") of " << rmod->mod_name() << " is connected to " << *pins[idx] << endl; des->errors += 1; @@ -1555,9 +1556,9 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const !prts.empty() && (prts[0]->data_type() != IVL_VT_REAL )) { cerr << pins[idx]->get_fileline() << ": error: " << "Cannot automatically connect bit based " - "inout port " << rmod->ports[idx]->name - << " of module " << rmod->mod_name() << " to real " - "signal " << sig->name() << "." << endl; + "inout port " << (idx+1) << " (" << port_name + << ") of module " << rmod->mod_name() + << " to real signal " << sig->name() << "." << endl; des->errors += 1; continue; } @@ -1566,9 +1567,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const if (!prts.empty() && (prts[0]->data_type() == IVL_VT_REAL )) { cerr << pins[idx]->get_fileline() << ": error: " << "No support for connecting real inout ports (" - "port " - << rmod->ports[idx]->name << " of module " - << rmod->mod_name() << ")." << endl; + "port " << (idx+1) << " (" << port_name + << ") of module " << rmod->mod_name() << ")." << endl; des->errors += 1; continue; } @@ -1577,11 +1577,11 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const } else { /* Port type must be OUTPUT here. */ - ivl_assert(*this, prts[0]->port_type() == NetNet::POUTPUT); + ivl_assert(*this, ptype == NetNet::POUTPUT); // Special case: If the output port is an unpacked // array, then there should be no sub-ports and - // the passed pexxpression is processed + // the passed port expression is processed // differently. Note that we are calling it the // "r-value" expression, but since this is an // output port, we assign to it from the internal object. @@ -1611,8 +1611,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const cerr << pins[idx]->get_fileline() << ": error: " << "Output port expression must support " << "continuous assignment." << endl; - cerr << pins[idx]->get_fileline() << ": : " - << "Port " << rmod->ports[idx]->name << " of " + cerr << pins[idx]->get_fileline() << ": : Port " + << (idx+1) << " (" << port_name << ") of " << rmod->mod_name() << " is connected to " << *pins[idx] << endl; des->errors += 1; @@ -1640,11 +1640,9 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const } prts_vector_width = sig->vector_width(); for (unsigned pidx = 0; pidx < prts.size(); pidx += 1) { - prts[pidx]->port_type(NetNet::NOT_A_PORT); prts[pidx] = cast_to_int4(des, scope, prts[pidx], prts_vector_width / instance.size()); - prts[pidx]->port_type(NetNet::POUTPUT); } } @@ -1653,9 +1651,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const if ((sig->data_type() == IVL_VT_REAL ) && !prts.empty() && (prts[0]->data_type() != IVL_VT_REAL )) { prts_vector_width -= prts[0]->vector_width() - 1; - prts[0]->port_type(NetNet::NOT_A_PORT); prts[0] = cast_to_real(des, scope, prts[0]); - prts[0]->port_type(NetNet::POUTPUT); // No support for multiple real drivers. if (instance.size() != 1) { cerr << pins[idx]->get_fileline() << ": error: " @@ -1673,10 +1669,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const if ((sig->data_type() == IVL_VT_BOOL ) && !prts.empty() && (prts[0]->data_type() == IVL_VT_LOGIC )) { for (unsigned pidx = 0; pidx < prts.size(); pidx += 1) { - prts[pidx]->port_type(NetNet::NOT_A_PORT); prts[pidx] = cast_to_int2(des, scope, prts[pidx], prts[pidx]->vector_width()); - prts[pidx]->port_type(NetNet::POUTPUT); } } @@ -1687,8 +1681,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const instance.size() != 1) { cerr << pins[idx]->get_fileline() << ": error: " << "An arrayed instance of " << rmod->mod_name() - << " cannot have a real port (" - << rmod->ports[idx]->name << ") connected to a " + << " cannot have a real port (port " << (idx+1) + << " : " << port_name << ") connected to a " "real signal (" << sig->name() << ")." << endl; des->errors += 1; continue; @@ -1700,7 +1694,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const #ifndef NDEBUG if ((! prts.empty()) - && (prts[0]->port_type() != NetNet::PINPUT)) { + && (ptype != NetNet::PINPUT)) { assert(sig->type() != NetNet::REG); } #endif @@ -1721,8 +1715,9 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const if (debug_elaborate) { cerr << get_fileline() << ": debug: " << get_name() - << ": Port " << (idx+1) << " has vector width of " - << prts_vector_width << "." << endl; + << ": Port " << (idx+1) << " (" << port_name + << ") has vector width of " << prts_vector_width + << "." << endl; } // Check that the parts have matching pin counts. If @@ -1730,12 +1725,9 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const // based, but users count parameter positions from 1. if ((instance.size() == 1) && (prts_vector_width != sig->vector_width())) { - const char *tmp3 = rmod->ports[idx]->name.str(); bool as_signed = false; - if (tmp3 == 0) tmp3 = "???"; - - switch (prts[0]->port_type()) { + switch (ptype) { case NetNet::POUTPUT: as_signed = prts[0]->get_signed(); break; @@ -1754,12 +1746,12 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const } cerr << get_fileline() << ": warning: Port " << (idx+1) - << " (" << tmp3 << ") of " + << " (" << port_name << ") of " << type_ << " expects " << prts_vector_width << " bits, got " << sig->vector_width() << "." << endl; // Delete this when inout ports pad correctly. - if (prts[0]->port_type() == NetNet::PINOUT) { + if (ptype == NetNet::PINOUT) { if (prts_vector_width > sig->vector_width()) { cerr << get_fileline() << ": : Leaving " << (prts_vector_width-sig->vector_width()) @@ -1779,7 +1771,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const << " high bits of the port." << endl; } else { - if (prts[0]->port_type() == NetNet::PINPUT) { + if (ptype == NetNet::PINPUT) { cerr << get_fileline() << ": : Pruning "; } else { cerr << get_fileline() << ": : Padding "; @@ -1791,7 +1783,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const } sig = resize_net_to_port_(des, scope, sig, prts_vector_width, - prts[0]->port_type(), as_signed); + ptype, as_signed); } // Connect the sig expression that is the context of the @@ -1842,7 +1834,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const for (unsigned ldx = 0 ; ldx < prts.size() ; ldx += 1) connect(prts[ldx]->pin(0), sig->pin(0)); - } else switch (prts[0]->port_type()) { + } else switch (ptype) { case NetNet::POUTPUT: ctmp = new NetConcat(scope, scope->local_symbol(), prts_vector_width, prts.size()); @@ -1999,7 +1991,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); } } @@ -2212,8 +2204,10 @@ void PGModule::elaborate(Design*des, NetScope*scope) const return; } - cerr << get_fileline() << ": internal error: Unknown module type: " << - type_ << endl; + if (!ignore_missing_modules) { + cerr << get_fileline() << ": internal error: Unknown module type: " << + type_ << endl; + } } void PGModule::elaborate_scope(Design*des, NetScope*sc) const @@ -2257,9 +2251,11 @@ void PGModule::elaborate_scope(Design*des, NetScope*sc) const // Not a module or primitive that I know about or can find by // any means, so give up. - cerr << get_fileline() << ": error: Unknown module type: " << type_ << endl; - missing_modules[type_] += 1; - des->errors += 1; + if (!ignore_missing_modules) { + cerr << get_fileline() << ": error: Unknown module type: " << type_ << endl; + missing_modules[type_] += 1; + des->errors += 1; + } } @@ -2324,7 +2320,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_); @@ -2333,7 +2330,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; @@ -2358,19 +2355,7 @@ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) { NetExpr*dex = elab_and_eval(des, scope, expr, -1); - /* Print a warning if we find default and `timescale based - * delays in the design, since this is likely an error. */ - if (scope->time_from_timescale()) dly_used_timescale = true; - else dly_used_no_timescale = true; - - if (display_ts_dly_warning && - dly_used_no_timescale && dly_used_timescale) { - cerr << "warning: Found both default and " - "`timescale based delays. Use" << endl; - cerr << " -Wtimescale to find the " - "module(s) with no `timescale." << endl; - display_ts_dly_warning = false; - } + check_for_inconsistent_delays(scope); /* If the delay expression is a real constant or vector constant, then evaluate it, scale it to the local time @@ -2451,10 +2436,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; @@ -2676,11 +2686,16 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const return bl; } - if (lv->enumeration() && - ! lv->enumeration()->matches(rv->enumeration())) { - cerr << get_fileline() << ": error: " - << "Enumeration type mismatch in assignment." << endl; - des->errors += 1; + if (lv->enumeration()) { + if (! rv->enumeration()) { + cerr << get_fileline() << ": error: " + "This assignment requires an explicit cast." << endl; + des->errors += 1; + } else if (! lv->enumeration()->matches(rv->enumeration())) { + cerr << get_fileline() << ": error: " + "Enumeration type mismatch in assignment." << endl; + des->errors += 1; + } } NetAssign*cur = new NetAssign(lv, rv); @@ -2867,22 +2882,36 @@ 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; - // Handle the special case that the block contains only one - // statement. There is no need to keep the block node. Also, - // don't elide named blocks, because they might be referenced - // elsewhere. - if ((list_.size() == 1) && (pscope_name() == 0)) { + // Handle the special case that the sequential block contains + // only one statement. There is no need to keep the block node. + // Also, don't elide named blocks, because they might be + // referenced elsewhere. + if ((type == NetBlock::SEQU) && (list_.size() == 1) && + (pscope_name() == 0)) { assert(list_[0]); NetProc*tmp = list_[0]->elaborate(des, nscope); return tmp; @@ -3727,7 +3756,7 @@ NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope, NetExpr*rv = 0; - if (parms_idxnet_type(), lv_type, wid, parms_ [parms_idx]); if (NetEEvent*evt = dynamic_cast (rv)) { @@ -3742,7 +3771,7 @@ NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope, if (! gn_system_verilog()) { cerr << get_fileline() << ": internal error: " << "Found (and using) default task expression " - << " requires SystemVerilog." << endl; + "requires SystemVerilog." << endl; des->errors += 1; } rv = def->port_defe(idx); @@ -3777,7 +3806,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); @@ -3789,20 +3820,24 @@ NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope, /* Elaborate an l-value version of the port expression for output and inout ports. If the expression does - not exist then quietly skip it, but if the expression - is not a valid l-value print an error message. Note - that the elaborate_lval method already printed a - detailed message. */ - NetAssign_*lv; - if (parms_[idx]) { - lv = parms_[idx]->elaborate_lval(des, scope, false, false); + not exist or is not a valid l-value print an error + message. Note that the elaborate_lval method already + printed a detailed message for the latter case. */ + NetAssign_*lv = 0; + 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 { - lv = 0; + } else if (port->port_type() == NetNet::POUTPUT) { + // Output ports were skipped earlier, so + // report the error now. + cerr << get_fileline() << ": error: " + << "Missing argument " << (idx+1) + << " of call to task." << endl; + des->errors += 1; } if (lv == 0) @@ -4034,9 +4069,18 @@ NetProc* PDisable::elaborate(Design*des, NetScope*scope) const */ NetProc* PDoWhile::elaborate(Design*des, NetScope*scope) const { - NetExpr*tmp = elab_and_eval(des, scope, cond_, -1); - tmp->set_line(*this); - NetDoWhile*loop = new NetDoWhile(tmp, statement_->elaborate(des, scope)); + NetExpr*ce = elab_and_eval(des, scope, cond_, -1); + NetProc*sub; + if (statement_) + sub = statement_->elaborate(des, scope); + else + sub = new NetBlock(NetBlock::SEQU, 0); + if (ce == 0 || sub == 0) { + delete ce; + delete sub; + return 0; + } + NetDoWhile*loop = new NetDoWhile(ce, sub); loop->set_line(*this); return loop; } @@ -4147,6 +4191,7 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope, NetEvent*ev = new NetEvent(scope->local_symbol()); ev->set_line(*this); + ev->local_flag(true); unsigned expr_count = 0; NetEvWait*wa = new NetEvWait(enet); @@ -4157,14 +4202,18 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope, if (expr_.count() == 0) { assert(enet); - /* For synthesis we want just the inputs, but for the rest we - * want inputs and outputs that may cause a value to change. */ + /* For synthesis or always_comb/latch we want just the inputs, + * but for the rest we want inputs and outputs that may cause + * a value to change. */ extern bool synthesis; /* Synthesis flag from main.cc */ bool rem_out = false; - if (synthesis) { + if (synthesis || search_funcs_) { rem_out = true; } - NexusSet*nset = enet->nex_input(rem_out); + // If this is an always_comb/latch then we need an implicit T0 + // trigger of the event expression. + if (search_funcs_) wa->set_t0_trigger(); + NexusSet*nset = enet->nex_input(rem_out, search_funcs_); if (nset == 0) { cerr << get_fileline() << ": error: Unable to elaborate:" << endl; @@ -4423,6 +4472,8 @@ NetProc* PEventStatement::elaborate_wait(Design*des, NetScope*scope, /* Create an event wait and an otherwise unreferenced event variable to force a perpetual wait. */ NetEvent*wait_event = new NetEvent(scope->local_symbol()); + wait_event->set_line(*this); + wait_event->local_flag(true); scope->add_event(wait_event); NetEvWait*wait = new NetEvWait(0); @@ -4443,6 +4494,8 @@ NetProc* PEventStatement::elaborate_wait(Design*des, NetScope*scope, eval_expr(expr); NetEvent*wait_event = new NetEvent(scope->local_symbol()); + wait_event->set_line(*this); + wait_event->local_flag(true); scope->add_event(wait_event); NetEvWait*wait = new NetEvWait(0 /* noop */); @@ -4567,7 +4620,11 @@ NetProc* PEventStatement::elaborate(Design*des, NetScope*scope) const */ NetProc* PForever::elaborate(Design*des, NetScope*scope) const { - NetProc*stat = statement_->elaborate(des, scope); + NetProc*stat; + if (statement_) + stat = statement_->elaborate(des, scope); + else + stat = new NetBlock(NetBlock::SEQU, 0); if (stat == 0) return 0; NetForever*proc = new NetForever(stat); @@ -4773,7 +4830,11 @@ NetProc* PForeach::elaborate(Design*des, NetScope*scope) const /* Elaborate the statement that is contained in the foreach loop. */ - NetProc*sub = statement_->elaborate(des, scope); + NetProc*sub; + if (statement_) + sub = statement_->elaborate(des, scope); + else + sub = new NetBlock(NetBlock::SEQU, 0); /* Make a step statement: idx += 1 */ NetAssign_*idx_lv = new NetAssign_(idx_sig); @@ -4804,7 +4865,11 @@ NetProc* PForeach::elaborate_static_array_(Design*des, NetScope*scope, ivl_assert(*this, index_vars_.size() > 0); ivl_assert(*this, dims.size() == index_vars_.size()); - NetProc*sub = statement_->elaborate(des, scope); + NetProc*sub; + if (statement_) + sub = statement_->elaborate(des, scope); + else + sub = new NetBlock(NetBlock::SEQU, 0); NetForLoop*stmt = 0; for (int idx_idx = index_vars_.size()-1 ; idx_idx >= 0 ; idx_idx -= 1) { @@ -4898,7 +4963,11 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const loop. If there is an error, this will return 0 and I should skip the append. No need to worry, the error has been reported so it's OK that the netlist is bogus. */ - NetProc*sub = statement_->elaborate(des, scope); + NetProc*sub; + if (statement_) + sub = statement_->elaborate(des, scope); + else + sub = new NetBlock(NetBlock::SEQU, 0); /* Now elaborate the for_step statement. I really should do some error checking here to make sure the step statement @@ -4967,7 +5036,6 @@ void PFunction::elaborate(Design*des, NetScope*scope) const des->errors += 1; return; } - assert(def); ivl_assert(*this, statement_); @@ -4980,6 +5048,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); } @@ -5020,7 +5113,11 @@ NetProc* PRepeat::elaborate(Design*des, NetScope*scope) const if (expr->expr_type() == IVL_VT_REAL) expr = cast_to_int4(expr, 64); - NetProc*stat = statement_->elaborate(des, scope); + NetProc*stat; + if (statement_) + stat = statement_->elaborate(des, scope); + else + stat = new NetBlock(NetBlock::SEQU, 0); if (stat == 0) return 0; // If the expression is a constant, handle certain special @@ -5138,11 +5235,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); @@ -5161,6 +5253,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); } @@ -5200,7 +5317,11 @@ NetProc* PTrigger::elaborate(Design*des, NetScope*scope) const NetProc* PWhile::elaborate(Design*des, NetScope*scope) const { NetExpr*ce = elab_and_eval(des, scope, cond_, -1); - NetProc*sub = statement_->elaborate(des, scope); + NetProc*sub; + if (statement_) + sub = statement_->elaborate(des, scope); + else + sub = new NetBlock(NetBlock::SEQU, 0); if (ce == 0 || sub == 0) { delete ce; delete sub; @@ -5245,7 +5366,10 @@ bool PProcess::elaborate(Design*des, NetScope*scope) const gets into its wait statement before non-combinational code is executed. */ do { - if (top->type() != IVL_PR_ALWAYS) + if ((top->type() != IVL_PR_ALWAYS) && + (top->type() != IVL_PR_ALWAYS_COMB) && + (top->type() != IVL_PR_ALWAYS_FF) && + (top->type() != IVL_PR_ALWAYS_LATCH)) break; NetEvWait*st = dynamic_cast(top->statement()); @@ -5292,19 +5416,7 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const ndelays = delays.size(); if (ndelays > 12) ndelays = 12; - /* Print a warning if we find default and `timescale based - * delays in the design, since this is likely an error. */ - if (scope->time_from_timescale()) dly_used_timescale = true; - else dly_used_no_timescale = true; - - if (display_ts_dly_warning && - dly_used_no_timescale && dly_used_timescale) { - cerr << "warning: Found both default and " - "`timescale based delays. Use" << endl; - cerr << " -Wtimescale to find the " - "module(s) with no `timescale." << endl; - display_ts_dly_warning = false; - } + check_for_inconsistent_delays(scope); /* Elaborate the delay values themselves. Remember to scale them for the timescale/precision of the scope. */ @@ -5572,6 +5684,10 @@ bool PPackage::elaborate(Design*des, NetScope*scope) const // Elaborate class definitions. elaborate_classes(des, scope, classes); + // Elaborate the variable initialization statements, making a + // single initial process out of them. + result_flag &= elaborate_var_inits_(des, scope); + return result_flag; } @@ -5613,6 +5729,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. @@ -5690,6 +5810,9 @@ bool PGenerate::elaborate(Design*des, NetScope*container) const "generate " << scheme_type << " elaborating in scope " << scope_path(container) << "." << endl; + cerr << get_fileline() << ": PGenerate::elaborate: " + "generate scope_name=" << scope_name + << ", id_number=" << id_number << endl; } // Handle the special case that this is a CASE scheme. In this @@ -5724,6 +5847,7 @@ bool PGenerate::elaborate(Design*des, NetScope*container) const // scheme defined in the Verilog-2005 standard. const char*name = scope_name.str(); if (name[0] == '$') { + if (!scope->auto_name("genblk", '0', name + 4)) { cerr << get_fileline() << ": warning: Couldn't build" << " unique name for unnamed generate block" @@ -5799,6 +5923,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); @@ -5834,6 +5960,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) @@ -5889,7 +6053,7 @@ class elaborate_root_scope_t : public elaborator_work_item_t { class top_defparams : public elaborator_work_item_t { public: - top_defparams(Design*des__) + explicit top_defparams(Design*des__) : elaborator_work_item_t(des__) { } @@ -5920,7 +6084,7 @@ class top_defparams : public elaborator_work_item_t { class later_defparams : public elaborator_work_item_t { public: - later_defparams(Design*des__) + explicit later_defparams(Design*des__) : elaborator_work_item_t(des__) { } @@ -5956,14 +6120,17 @@ class later_defparams : public elaborator_work_item_t { bool Design::check_proc_delay() const { - bool result_flag = true; + bool result = false; for (const NetProcTop*pr = procs_ ; pr ; pr = pr->next_) { - /* If this is an always block and we have no or zero delay then - * a runtime infinite loop will happen. If we possible have some + /* If this is an always process and we have no or zero delay then + * a runtime infinite loop will happen. If we possibly have some * delay then print a warning that an infinite loop is possible. */ - if (pr->type() == IVL_PR_ALWAYS) { + if ((pr->type() == IVL_PR_ALWAYS) || + (pr->type() == IVL_PR_ALWAYS_COMB) || + (pr->type() == IVL_PR_ALWAYS_FF) || + (pr->type() == IVL_PR_ALWAYS_LATCH)) { DelayType dly_type = pr->statement()->delay_type(); if (dly_type == NO_DELAY || dly_type == ZERO_DELAY) { @@ -5971,7 +6138,7 @@ bool Design::check_proc_delay() const << " statement does not have any delay." << endl; cerr << pr->get_fileline() << ": : A runtime" << " infinite loop will occur." << endl; - result_flag = false; + result = true; } else if (dly_type == POSSIBLE_DELAY && warn_inf_loop) { cerr << pr->get_fileline() << ": warning: always" @@ -5979,6 +6146,38 @@ bool Design::check_proc_delay() const cerr << pr->get_fileline() << ": : A runtime" << " infinite loop may be possible." << endl; } + + // The always_comb/ff/latch processes also have special + // delay rules that need to be checked. + if ((pr->type() == IVL_PR_ALWAYS_COMB) || + (pr->type() == IVL_PR_ALWAYS_FF) || + (pr->type() == IVL_PR_ALWAYS_LATCH)) { + const NetEvWait *wait = dynamic_cast (pr->statement()); + if (! wait) { + // The always_comb/latch processes have an event + // control added automatically by the compiler. + assert(pr->type() == IVL_PR_ALWAYS_FF); + cerr << pr->get_fileline() << ": error: the " + << "first statement of an always_ff must " + << "be an event control." << endl; + result = true; + } else if (wait->statement()->delay_type(true) != NO_DELAY) { + cerr << pr->get_fileline() << ": error: there " + << "must "; + + if (pr->type() == IVL_PR_ALWAYS_FF) { + cerr << "only be a single event control " + << "and no blocking delays in an " + << "always_ff process."; + } else { + cerr << "be no event controls or blocking " + << "delays in an always_comb/latch " + << "process."; + } + cerr << endl; + result = true; + } + } } /* If this is a final block it must not have a delay, @@ -5988,37 +6187,287 @@ bool Design::check_proc_delay() const if (pr->type() == IVL_PR_FINAL) { DelayType dly_type = pr->statement()->delay_type(); - if (dly_type != NO_DELAY && dly_type != ZERO_DELAY) { + if (dly_type != NO_DELAY) { cerr << pr->get_fileline() << ": error: final" << " statement contains a delay." << endl; - result_flag = false; + result = true; } } } - return result_flag; + return result; } -void Design::root_elaborate(void) +static void print_nexus_name(const Nexus*nex) { - for (map::const_iterator cur = classes_.begin() - ; cur != classes_.end() ; ++ cur) { - netclass_t*cur_class = cur->second; - PClass*cur_pclass = class_to_pclass_[cur_class]; - cur_class->elaborate(this, cur_pclass); - } - - for (map::iterator cur = root_tasks_.begin() - ; cur != root_tasks_.end() ; ++ cur) { - - if (debug_elaborate) { - cerr << cur->second->get_fileline() << ": Design::root_elaborate: " - << "Elaborate for root task/func " << scope_path(cur->first) << endl; + for (const Link*cur = nex->first_nlink(); cur; cur = cur->next_nlink()) { + if (cur->get_dir() != Link::OUTPUT) continue; + const NetPins*obj = cur->get_obj(); + // For a NetNet (signal) just use the name. + if (const NetNet*net = dynamic_cast(obj)) { + cerr << net->name(); + return; + // For a NetPartSelect calculate the name. + } else if (const NetPartSelect*ps = dynamic_cast(obj)) { + assert(ps->pin_count() >= 2); + assert(ps->pin(1).get_dir() == Link::INPUT); + assert(ps->pin(1).is_linked()); + print_nexus_name(ps->pin(1).nexus()); + cerr << "[]"; + return; + // For a NetUReduce calculate the name. + } else if (const NetUReduce*reduce = dynamic_cast(obj)) { + assert(reduce->pin_count() == 2); + assert(reduce->pin(1).get_dir() == Link::INPUT); + assert(reduce->pin(1).is_linked()); + switch (reduce->type()) { + case NetUReduce::AND: + cerr << "&"; + break; + case NetUReduce::OR: + cerr << "|"; + break; + case NetUReduce::XOR: + cerr << "^"; + break; + case NetUReduce::NAND: + cerr << "~&"; + break; + case NetUReduce::NOR: + cerr << "~|"; + break; + case NetUReduce::XNOR: + cerr << "~^"; + break; + case NetUReduce::NONE: + assert(0); + } + print_nexus_name(reduce->pin(1).nexus()); + return; + } else if (const NetLogic*logic = dynamic_cast(obj)) { + assert(logic->pin_count() >= 2); + assert(logic->pin(1).get_dir() == Link::INPUT); + assert(logic->pin(1).is_linked()); + switch (logic->type()) { + case NetLogic::NOT: + cerr << "~"; + break; + default: + // The other operators should never be used here, + // so just return the nexus name. + cerr << nex->name(); + return; + } + print_nexus_name(logic->pin(1).nexus()); + return; } + // Use the following to find the type of anything that may be missing: + // cerr << "(" << typeid(*obj).name() << ") "; + } + // Otherwise just use the nexus name so somthing is printed. + cerr << nex->name(); +} - cur->second->elaborate(this, cur->first); +static void print_event_probe_name(const NetEvProbe *prb) +{ + assert(prb->pin_count() == 1); + assert(prb->pin(0).get_dir() == Link::INPUT); + assert(prb->pin(0).is_linked()); + print_nexus_name(prb->pin(0).nexus()); +} + +static void check_event_probe_width(const LineInfo *info, const NetEvProbe *prb) +{ + assert(prb->pin_count() == 1); + assert(prb->pin(0).get_dir() == Link::INPUT); + assert(prb->pin(0).is_linked()); + if (prb->edge() == NetEvProbe::ANYEDGE) return; + if (prb->pin(0).nexus()->vector_width() > 1) { + cerr << info->get_fileline() << " Warning: Synthesis wants " + "the sensitivity list expressions for '"; + switch (prb->edge()) { + case NetEvProbe::POSEDGE: + cerr << "posedge "; + break; + case NetEvProbe::NEGEDGE: + cerr << "negedge "; + break; + default: + break; + } + print_nexus_name(prb->pin(0).nexus()); + cerr << "' to be a single bit." << endl; + } +} + +static void check_ff_sensitivity(const NetProc* statement) +{ + const NetEvWait *evwt = dynamic_cast (statement); + // We have already checked for and reported if the first statmemnt is + // not a wait. + if (! evwt) return; + + for (unsigned cevt = 0; cevt < evwt->nevents(); cevt += 1) { + const NetEvent *evt = evwt->event(cevt); + for (unsigned cprb = 0; cprb < evt->nprobe(); cprb += 1) { + const NetEvProbe *prb = evt->probe(cprb); + check_event_probe_width(evwt, prb); + if (prb->edge() == NetEvProbe::ANYEDGE) { + cerr << evwt->get_fileline() << " Warning: Synthesis " + "requires the sensitivity list of an " + "always_ff process to only be edge " + "sensitive. "; + print_event_probe_name(prb); + cerr << " is missing a pos/negedge." << endl; + } + } + } +} + +/* + * Check to see if the always_* processes only contain synthesizable + * constructs. + */ +bool Design::check_proc_synth() const +{ + bool result = false; + for (const NetProcTop*pr = procs_ ; pr ; pr = pr->next_) { + if ((pr->type() == IVL_PR_ALWAYS_COMB) || + (pr->type() == IVL_PR_ALWAYS_FF) || + (pr->type() == IVL_PR_ALWAYS_LATCH)) { + result |= pr->statement()->check_synth(pr->type(), + pr->scope()); + if (pr->type() == IVL_PR_ALWAYS_FF) { + check_ff_sensitivity(pr->statement()); + } + } + } + return result; +} + +/* + * Check whether all design elements have an explicit timescale or all + * design elements use the default timescale. If a mixture of explicit + * and default timescales is found, a warning message is output. Note + * that we only need to check the top level design elements - nested + * design elements will always inherit the timescale from their parent + * if they don't have any local timescale declarations. + * + * NOTE: Strictly speaking, this should be an error for SystemVerilog + * (1800-2012 section 3.14.2). + */ +static void check_timescales(bool&some_explicit, bool&some_implicit, + const PScope*scope) +{ + if (scope->time_unit_is_default) + some_implicit = true; + else + some_explicit = true; + if (scope->time_prec_is_default) + some_implicit = true; + else + some_explicit = true; +} + +static void check_timescales() +{ + bool some_explicit = false; + bool some_implicit = false; + map::iterator mod; + for (mod = pform_modules.begin(); mod != pform_modules.end(); ++mod) { + const Module*mp = (*mod).second; + check_timescales(some_explicit, some_implicit, mp); + if (some_explicit && some_implicit) + break; + } + map::iterator pkg; + if (gn_system_verilog() && !(some_explicit && some_implicit)) { + for (pkg = pform_packages.begin(); pkg != pform_packages.end(); ++pkg) { + const PPackage*pp = (*pkg).second; + check_timescales(some_explicit, some_implicit, pp); + if (some_explicit && some_implicit) + break; + } + } + if (gn_system_verilog() && !(some_explicit && some_implicit)) { + for (unsigned idx = 0; idx < pform_units.size(); idx += 1) { + const PPackage*pp = pform_units[idx]; + // We don't need a timescale if the compilation unit + // contains no items outside a design element. + if (pp->parameters.empty() && + pp->localparams.empty() && + pp->wires.empty() && + pp->tasks.empty() && + pp->funcs.empty() && + pp->classes.empty()) + continue; + + check_timescales(some_explicit, some_implicit, pp); + if (some_explicit && some_implicit) + break; + } } + if (!(some_explicit && some_implicit)) + return; + + if (gn_system_verilog()) { + cerr << "warning: " + << "Some design elements have no explicit time unit and/or" + << endl; + cerr << " : " + << "time precision. This may cause confusing timing results." + << endl; + cerr << " : " + << "Affected design elements are:" + << endl; + } else { + cerr << "warning: " + << "Some modules have no timescale. This may cause" + << endl; + cerr << " : " + << "confusing timing results. Affected modules are:" + << endl; + } + + for (mod = pform_modules.begin(); mod != pform_modules.end(); ++mod) { + Module*mp = (*mod).second; + if (mp->has_explicit_timescale()) + continue; + cerr << " : -- module " << (*mod).first + << " declared here: " << mp->get_fileline() << endl; + } + + if (!gn_system_verilog()) + return; + + for (pkg = pform_packages.begin(); pkg != pform_packages.end(); ++pkg) { + PPackage*pp = (*pkg).second; + if (pp->has_explicit_timescale()) + continue; + cerr << " : -- package " << (*pkg).first + << " declared here: " << pp->get_fileline() << endl; + } + + for (unsigned idx = 0; idx < pform_units.size(); idx += 1) { + PPackage*pp = pform_units[idx]; + if (pp->has_explicit_timescale()) + continue; + + if (pp->parameters.empty() && + pp->localparams.empty() && + pp->wires.empty() && + pp->tasks.empty() && + pp->funcs.empty() && + pp->classes.empty()) + continue; + + cerr << " : -- compilation unit"; + if (pform_units.size() > 1) { + cerr << " from: " << pp->get_file(); + } + cerr << endl; + } } /* @@ -6040,8 +6489,13 @@ struct root_elem { Design* elaborate(listroots) { + unsigned npackages = pform_packages.size(); + if (gn_system_verilog()) + npackages += pform_units.size(); + vector root_elems(roots.size()); - vector pack_elems(pform_packages.size()); + vector pack_elems(npackages); + map unit_scopes; bool rc = true; unsigned i = 0; @@ -6049,24 +6503,38 @@ Design* elaborate(listroots) // module and elaborate what I find. Design*des = new Design; - // Elaborate enum sets in $root scope. - elaborate_rootscope_enumerations(des); + // Elaborate the compilation unit scopes. From here on, these are + // treated as an additional set of packages. + if (gn_system_verilog()) { + for (i = 0; i < pform_units.size(); i += 1) { + PPackage*unit = pform_units[i]; + NetScope*scope = des->make_package_scope(unit->pscope_name(), 0, true); + scope->set_line(unit); + set_scope_timescale(des, scope, unit); - // Elaborate tasks and functions in $root scope. - elaborate_rootscope_tasks(des); + elaborator_work_item_t*es = new elaborate_package_t(des, scope, unit); + des->elaboration_work_list.push_back(es); - // Elaborate classes in $root scope. - elaborate_rootscope_classes(des); + pack_elems[i].pack = unit; + pack_elems[i].scope = scope; + + unit_scopes[unit] = scope; + } + } // Elaborate the packages. Package elaboration is simpler - // because there are fewer sub-scopes involved. - i = 0; + // because there are fewer sub-scopes involved. Note that + // in SystemVerilog, packages are not allowed to refer to + // the compilation unit scope, but the VHDL preprocessor + // assumes they can. for (map::iterator pac = pform_packages.begin() ; pac != pform_packages.end() ; ++ pac) { ivl_assert(*pac->second, pac->first == pac->second->pscope_name()); - NetScope*scope = des->make_package_scope(pac->first); + NetScope*unit_scope = unit_scopes[pac->second->parent_scope()]; + NetScope*scope = des->make_package_scope(pac->first, unit_scope, false); 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); @@ -6095,19 +6563,19 @@ Design* elaborate(listroots) // Get the module definition for this root instance. Module *rmod = (*mod).second; + // Get the compilation unit scope for this module. + NetScope*unit_scope = unit_scopes[rmod->parent_scope()]; + // Make the root scope. This makes a NetScope object and // pushes it into the list of root scopes in the Design. - NetScope*scope = des->make_root_scope(*root, rmod->program_block, + NetScope*scope = des->make_root_scope(*root, unit_scope, + rmod->program_block, rmod->is_interface); // 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. @@ -6171,6 +6639,10 @@ Design* elaborate(listroots) if (des->errors > 0) return des; + // Now we have the full design, check for timescale inconsistencies. + if (warn_timescale) + check_timescales(); + if (debug_elaborate) { cerr << ": elaborate: " << "Start calling Package elaborate_sig methods." << endl; @@ -6199,8 +6671,6 @@ Design* elaborate(listroots) << "Start calling $root elaborate_sig methods." << endl; } - des->root_elaborate_sig(); - if (debug_elaborate) { cerr << ": elaborate: " << "Start calling root module elaborate_sig methods." << endl; @@ -6262,8 +6732,6 @@ Design* elaborate(listroots) rc &= pkg->elaborate(des, scope); } - des->root_elaborate(); - for (i = 0; i < root_elems.size(); i++) { Module *rmod = root_elems[i].mod; NetScope *scope = root_elems[i].scope; @@ -6278,10 +6746,11 @@ Design* elaborate(listroots) // Now that everything is fully elaborated verify that we do // not have an always block with no delay (an infinite loop), // or a final block with a delay. - if (des->check_proc_delay() == false) { - delete des; - des = 0; - } + bool has_failure = des->check_proc_delay(); + + // Check to see if the always_comb/ff/latch processes only have + // synthesizable constructs + has_failure |= des->check_proc_synth(); if (debug_elaborate) { cerr << "" << ": debug: " @@ -6289,5 +6758,10 @@ Design* elaborate(listroots) << des->find_root_scopes().size() << " root scopes " << endl; } + if (has_failure) { + delete des; + des = 0; + } + return des; } diff --git a/emit.cc b/emit.cc index bd46fc5c4..c5c64c9d3 100644 --- a/emit.cc +++ b/emit.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2017 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 @@ -123,6 +123,12 @@ bool NetFF::emit_node(struct target_t*tgt) const return true; } +bool NetLatch::emit_node(struct target_t*tgt) const +{ + tgt->lpm_latch(this); + return true; +} + bool NetLiteral::emit_node(struct target_t*tgt) const { return tgt->net_literal(this); @@ -504,26 +510,12 @@ int Design::emit(struct target_t*tgt) const if (tgt->start_design(this) == false) return -2; - 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 for (map::const_iterator scope = packages_.begin() ; scope != packages_.end() ; ++ scope) { scope->second->emit_scope(tgt); } - for (map::const_iterator cur = classes_.begin() - ; cur != classes_.end() ; ++cur) { - 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 for (list::const_iterator scope = root_scopes_.begin() ; scope != root_scopes_.end(); ++ scope ) { diff --git a/eval_tree.cc b/eval_tree.cc index 15bbc96d4..372a382cc 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2018 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 @@ -545,7 +545,7 @@ NetEConst* NetEBComp::eval_eqeq_real_(bool ne_flag, const NetExpr*le, const NetE bool flag = get_real_arguments(le, re, lval, rval); if (! flag) return 0; - verinum result(((lval == rval) ^ ne_flag) ? + verinum result(((lval == rval) != ne_flag) ? verinum::V1 : verinum::V0, 1); NetEConst*res = new NetEConst(result); ivl_assert(*this, res); @@ -570,11 +570,11 @@ NetEConst* NetEBComp::eval_eqeq_(bool ne_flag, const NetExpr*le, const NetExpr*r const verinum::V ne_res = ne_flag? verinum::V1 : verinum::V0; verinum::V res = eq_res; - unsigned top = lv.len(); - if (rv.len() < top) - top = rv.len(); - for (unsigned idx = 0 ; idx < top ; idx += 1) { + // The two expressions should already be padded to the same size. + ivl_assert(*this, lv.len() == rv.len()); + + for (unsigned idx = 0 ; idx < lv.len() ; idx += 1) { bool x_bit_present = false; @@ -611,60 +611,6 @@ NetEConst* NetEBComp::eval_eqeq_(bool ne_flag, const NetExpr*le, const NetExpr*r } } - if (res != verinum::Vx) { - verinum::V lpad = verinum::V0; - verinum::V rpad = verinum::V0; - - if (lv.has_sign() && lv.get(lv.len()-1) == verinum::V1) - lpad = verinum::V1; - if (rv.has_sign() && rv.get(rv.len()-1) == verinum::V1) - rpad = verinum::V1; - - for (unsigned idx = top ; idx < lv.len() ; idx += 1) - switch (lv.get(idx)) { - - case verinum::Vx: - case verinum::Vz: - res = verinum::Vx; - break; - - case verinum::V0: - if (res != verinum::Vx && rpad != verinum::V0) - res = ne_res; - break; - - case verinum::V1: - if (res != verinum::Vx && rpad != verinum::V1) - res = ne_res; - break; - - default: - break; - } - - for (unsigned idx = top ; idx < rv.len() ; idx += 1) - switch (rv.get(idx)) { - - case verinum::Vx: - case verinum::Vz: - res = verinum::Vx; - break; - - case verinum::V0: - if (res != verinum::Vx && lpad != verinum::V0) - res = ne_res; - break; - - case verinum::V1: - if (res != verinum::Vx && lpad != verinum::V1) - res = ne_res; - break; - - default: - break; - } - } - NetEConst*result = new NetEConst(verinum(res, 1)); ivl_assert(*this, result); return result; @@ -681,41 +627,15 @@ NetEConst* NetEBComp::eval_eqeqeq_(bool ne_flag, const NetExpr*le, const NetExpr verinum::V res = verinum::V1; - // Find the smallest argument length. - unsigned cnt = lv.len(); - if (cnt > rv.len()) cnt = rv.len(); + // The two expressions should already be padded to the same size. + ivl_assert(*this, lv.len() == rv.len()); - // Check the common bits. - for (unsigned idx = 0 ; idx < cnt ; idx += 1) + for (unsigned idx = 0 ; idx < lv.len() ; idx += 1) if (lv.get(idx) != rv.get(idx)) { res = verinum::V0; break; } - bool is_signed = lv.has_sign() && rv.has_sign(); - - // If the left value is longer check it against the pad bit. - if (res == verinum::V1) { - verinum::V pad = verinum::V0; - if (is_signed) pad = rv.get(rv.len()-1); - for (unsigned idx = cnt ; idx < lv.len() ; idx += 1) - if (lv.get(idx) != pad) { - res = verinum::V0; - break; - } - } - - // If the right value is longer check it against the pad bit. - if (res == verinum::V1) { - verinum::V pad = verinum::V0; - if (is_signed) pad = lv.get(lv.len()-1); - for (unsigned idx = cnt ; idx < rv.len() ; idx += 1) { - if (rv.get(idx) != pad) - res = verinum::V0; - break; - } - } - if (ne_flag) { if (res == verinum::V0) res = verinum::V1; else res = verinum::V0; @@ -726,6 +646,55 @@ NetEConst* NetEBComp::eval_eqeqeq_(bool ne_flag, const NetExpr*le, const NetExpr return result; } +NetEConst* NetEBComp::eval_weqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const +{ + const NetEConst*lc = dynamic_cast(le); + const NetEConst*rc = dynamic_cast(re); + if (lc == 0 || rc == 0) return 0; + + const verinum&lv = lc->value(); + const verinum&rv = rc->value(); + + const verinum::V eq_res = ne_flag ? verinum::V0 : verinum::V1; + const verinum::V ne_res = ne_flag ? verinum::V1 : verinum::V0; + + verinum::V res = eq_res; + + // The two expressions should already be padded to the same size. + ivl_assert(*this, lv.len() == rv.len()); + + for (unsigned idx = 0 ; idx < lv.len() ; idx += 1) { + // An X or Z in the R-value matches any L-value. + switch (rv.get(idx)) { + case verinum::Vx: + case verinum::Vz: + continue; + default: + break; + } + + // An X or Z in the L-value that is not matches by an R-value X/Z returns undefined. + switch (lv.get(idx)) { + case verinum::Vx: + case verinum::Vz: + res = verinum::Vx; + continue; + default: + break; + } + + // A hard (0/1) mismatch gives a not-equal result. + if (rv.get(idx) != lv.get(idx)) { + res = ne_res; + break; + } + } + + NetEConst*result = new NetEConst(verinum(res, 1)); + ivl_assert(*this, result); + return result; +} + NetEConst* NetEBComp::eval_arguments_(const NetExpr*l, const NetExpr*r) const { NetEConst*res = 0; @@ -739,6 +708,10 @@ NetEConst* NetEBComp::eval_arguments_(const NetExpr*l, const NetExpr*r) const res = eval_eqeq_(false, l, r); break; + case 'w': // Wild equality (==?) + res = eval_weqeq_(false, l, r); + break; + case 'G': // >= res = eval_gteq_(l, r); break; @@ -755,6 +728,10 @@ NetEConst* NetEBComp::eval_arguments_(const NetExpr*l, const NetExpr*r) const res = eval_eqeq_(true, l, r); break; + case 'W': // Wild not-equal (!=?) + res = eval_weqeq_(true, l, r); + break; + case '<': // Less than res = eval_less_(l, r); break; @@ -1097,6 +1074,7 @@ NetEConst* NetEBShift::eval_arguments_(const NetExpr*l, const NetExpr*r) const break; case 'r': lv.has_sign(false); + // fallthrough case 'R': val = cast_to_width(lv >> shift, wid); break; @@ -1548,6 +1526,7 @@ NetEConst* NetEUReduce::eval_arguments_(const NetExpr*ex) const case 'A': invert = true; + // fallthrough case '&': { res = verinum::V1; for (unsigned idx = 0 ; idx < val.len() ; idx += 1) @@ -1557,6 +1536,7 @@ NetEConst* NetEUReduce::eval_arguments_(const NetExpr*ex) const case 'N': invert = true; + // fallthrough case '|': { res = verinum::V0; for (unsigned idx = 0 ; idx < val.len() ; idx += 1) @@ -1566,6 +1546,7 @@ NetEConst* NetEUReduce::eval_arguments_(const NetExpr*ex) const case 'X': invert = true; + // fallthrough case '^': { /* Reduction XOR. */ unsigned ones = 0, unknown = 0; @@ -1617,6 +1598,7 @@ NetExpr* NetECast::eval_arguments_(const NetExpr*ex) const res_val = cast_to_width(res_val, expr_width()); res = new NetEConst(res_val); } + // fallthrough case 'v': if (const NetECReal*val = dynamic_cast(ex)) { verinum res_val(val->value().as_double(), false); @@ -1931,15 +1913,111 @@ NetExpr* NetESFunc::evaluate_min_max_(ID id, const NetExpr*arg0_, return res; } -NetEConst* NetESFunc::evaluate_countbits_(const NetExpr* /*arg0*/, - const NetExpr* /*arg1*/) const +static void no_string_arg(const NetESFunc*info, unsigned arg_num) { - return 0; + cerr << info->get_fileline() << ": error: constant function " + << info->name() << "() does not support a string argument (" + << arg_num+1 << ")." << endl; } -NetEConst* NetESFunc::evaluate_countones_(const NetExpr* /*arg*/) const +NetEConst* NetESFunc::evaluate_countbits_() const { - return 0; + const NetEConst*tmpi = dynamic_cast(parms_[0]); + + NetEConst*res = 0; + + if (tmpi) { + verinum value = tmpi->value(); + + if (value.is_string()) { + no_string_arg(this, 0); + return 0; + } + + /* Find which values need to be counted. */ + bool count_0 = false; + bool count_1 = false; + bool count_z = false; + bool count_x = false; + for (unsigned arg=1; arg < parms_.size(); ++arg) { + const NetEConst*argi = dynamic_cast(parms_[arg]); + if (! argi) return 0; + verinum check_for = argi->value(); + if (check_for.is_string()) { + no_string_arg(this, arg); + return 0; + } + switch (check_for[0]) { + case verinum::V0: + count_0 = true; + break; + case verinum::V1: + count_1 = true; + break; + case verinum::Vz: + count_z = true; + break; + case verinum::Vx: + count_x = true; + break; + } + } + + /* Search each bit of the vector looking for the values to + * be counted. */ + int count = 0; + for (unsigned bit=0; bit < value.len(); ++bit) { + switch (value[bit]) { + case verinum::V0: + if (count_0) ++count; + break; + case verinum::V1: + if (count_1) ++count; + break; + case verinum::Vz: + if (count_z) ++count; + break; + case verinum::Vx: + if (count_x) ++count; + break; + } + } + + verinum tmp (count, integer_width); + tmp.has_sign(true); + res = new NetEConst(tmp); + ivl_assert(*this, res); + } + + return res; +} + +NetEConst* NetESFunc::evaluate_countones_(const NetExpr* arg) const +{ + const NetEConst*tmpi = dynamic_cast(arg); + + NetEConst*res = 0; + + if (tmpi) { + verinum value = tmpi->value(); + int count = 0; + + if (value.is_string()) { + no_string_arg(this, 0); + return 0; + } + + for (unsigned bit=0; bit < value.len(); ++bit) { + if (value[bit] == verinum::V1) ++count; + } + + verinum tmp (count, integer_width); + tmp.has_sign(true); + res = new NetEConst(tmp); + ivl_assert(*this, res); + } + + return res; } /* Get the total number of dimensions for the given expression. */ @@ -1962,19 +2040,92 @@ NetEConst* NetESFunc::evaluate_dimensions_(const NetExpr*arg) const return new NetEConst(verinum(verinum(res), integer_width)); } -NetEConst* NetESFunc::evaluate_isunknown_(const NetExpr* /*arg*/) const +NetEConst* NetESFunc::evaluate_isunknown_(const NetExpr* arg) const { - return 0; + const NetEConst*tmpi = dynamic_cast(arg); + + NetEConst*res = 0; + + if (tmpi) { + verinum value = tmpi->value(); + unsigned is_unknown = 1; + + if (value.is_string()) { + no_string_arg(this, 0); + return 0; + } + + if (value.is_defined()) is_unknown = 0; + + verinum tmp (is_unknown, 1U); + tmp.has_sign(false); + res = new NetEConst(tmp); + ivl_assert(*this, res); + } + + return res; } -NetEConst* NetESFunc::evaluate_onehot_(const NetExpr* /*arg*/) const +static bool is_onehot(verinum&value, bool zero_is_okay) { - return 0; + bool found_a_one = false; + + for (unsigned bit=0; bit < value.len(); ++bit) { + if (value[bit] == verinum::V1) { + if (found_a_one) return false; + found_a_one = true; + } + } + + /* If no one bit was found return true if zero is okay. */ + if (zero_is_okay) found_a_one = true; + return found_a_one; } -NetEConst* NetESFunc::evaluate_onehot0_(const NetExpr* /*arg*/) const +NetEConst* NetESFunc::evaluate_onehot_(const NetExpr* arg) const { - return 0; + const NetEConst*tmpi = dynamic_cast(arg); + + NetEConst*res = 0; + + if (tmpi) { + verinum value = tmpi->value(); + + if (value.is_string()) { + no_string_arg(this, 0); + return 0; + } + + verinum tmp (is_onehot(value, false), 1U); + tmp.has_sign(false); + res = new NetEConst(tmp); + ivl_assert(*this, res); + } + + return res; +} + +NetEConst* NetESFunc::evaluate_onehot0_(const NetExpr* arg) const +{ + const NetEConst*tmpi = dynamic_cast(arg); + + NetEConst*res = 0; + + if (tmpi) { + verinum value = tmpi->value(); + + if (value.is_string()) { + no_string_arg(this, 0); + return 0; + } + + verinum tmp (is_onehot(value, true), 1U); + tmp.has_sign(false); + res = new NetEConst(tmp); + ivl_assert(*this, res); + } + + return res; } /* Get the number of unpacked dimensions for the given expression. */ @@ -2009,6 +2160,12 @@ static bool check_dimension(const NetExpr*dim_expr, long &dim) static bool get_array_info(const NetExpr*arg, long dim, long &left, long &right, bool&defer) { + if (const NetEConstParam*param = dynamic_cast(arg)) { + assert(dim == 1); + left = param->expr_width() - 1; + right = 0; + return false; + } /* The argument must be a signal that has enough dimensions. */ const NetESignal*esig = dynamic_cast(arg); if (esig == 0) return true; @@ -2140,7 +2297,7 @@ NetExpr* NetESFunc::evaluate_two_arg_(ID id, const NetExpr*arg0, { switch (id) { case CTBITS: - return evaluate_countbits_(arg0, arg1); + return evaluate_countbits_(); /* The array functions are handled together. */ case HIGH: case INCR: @@ -2211,12 +2368,12 @@ NetESFunc::ID NetESFunc::built_in_id_() const built_in_func["$unpacked_dimensions" ] = UPDIMS; } - /* These are available in 1800-2009 and later. */ + /* This is available in 1800-2009 and later. */ if (funcs_need_init && (generation_flag >= GN_VER2009)) { built_in_func["$countones" ] = CTONES; } - /* These are available in 1800-2012 and later. */ + /* This is available in 1800-2012 and later. */ if (funcs_need_init && (generation_flag >= GN_VER2012)) { built_in_func["$countbits" ] = CTBITS; } @@ -2243,7 +2400,7 @@ NetESFunc::ID NetESFunc::built_in_id_() const NetExpr* NetESFunc::eval_tree() { - /* Get the ID for this system function if it is can be used as a + /* Get the ID for this system function if it can be used as a * constant function. */ ID id = built_in_id_(); if (id == NOT_BUILT_IN) return 0; @@ -2251,8 +2408,9 @@ NetExpr* NetESFunc::eval_tree() switch (parms_.size()) { case 1: if (! takes_nargs_(id, 1)) { - cerr << get_fileline() << ": error: " << name_ - << "() does not support a single argument." << endl; + cerr << get_fileline() << ": error: constant function " + << name_ << "() does not support a single argument." + << endl; return 0; } eval_expr(parms_[0]); @@ -2260,8 +2418,9 @@ NetExpr* NetESFunc::eval_tree() case 2: if (! takes_nargs_(id, 2)) { - cerr << get_fileline() << ": error: " << name_ - << "() does not support two arguments." << endl; + cerr << get_fileline() << ": error: constant function " + << name_ << "() does not support two arguments." + << endl; return 0; } eval_expr(parms_[0]); @@ -2271,15 +2430,21 @@ NetExpr* NetESFunc::eval_tree() default: /* Check to see if the function was called correctly. */ if (! takes_nargs_(id, parms_.size())) { - cerr << get_fileline() << ": error: " << name_ - << "() does not support " << parms_.size() + cerr << get_fileline() << ": error: constant function " + << name_ << "() does not support " << parms_.size() << " arguments." << endl; return 0; } -// HERE: Need to add support for a multi argument $countbits(). - cerr << get_fileline() << ": sorry: functions with " - << parms_.size() << " arguments are not supported: " - << name_ << "()." << endl; + if (id == CTBITS) { + for (unsigned bit = 0; bit < parms_.size(); ++bit) { + eval_expr(parms_[bit]); + } + return evaluate_countbits_(); + } else { + cerr << get_fileline() << ": sorry: constant functions with " + << parms_.size() << " arguments are not supported: " + << name_ << "()." << endl; + } return 0; } } diff --git a/exposenodes.cc b/exposenodes.cc new file mode 100644 index 000000000..dc989b7f2 --- /dev/null +++ b/exposenodes.cc @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2016 Martin Whitaker (icarus@martin-whitaker.me.uk) + * + * 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 "config.h" + +# include +# include +# include "netlist.h" +# include "functor.h" +# include "compiler.h" +# include "ivl_assert.h" + + +/* + * The exposenodes functor is primarily provided for use by the vlog95 + * target. To implement some LPM objects, it needs to take a bit or part + * of one of the LPM inputs. If that input is not connected to a real + * net in the design, we need to create a net at that point so that + * there is something to which we can apply a bit or part select. This + * has the effect of splitting the synthesised structure at that point. + * Rather than creating a new net, we just look for a temporary net + * created by the synthesis process (there should be at least one) and + * reset its "local" flag. We also prepend another '_' to the synthetic + * name to avoid name collisions when we recompile the vlog95 output + * (because NetScope::local_symbol() doesn't actually check that the + * name it generates is unique). + */ + +struct exposenodes_functor : public functor_t { + + unsigned count; + + virtual void lpm_mux(Design*des, NetMux*obj); + virtual void lpm_part_select(Design*des, NetPartSelect*obj); + virtual void lpm_substitute(Design*des, NetSubstitute*obj); +}; + +static bool expose_nexus(Nexus*nex) +{ + NetNet*sig = 0; + for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) { + + // Don't expose nodes that are attached to constants + if (dynamic_cast (cur->get_obj())) + return false; + if (dynamic_cast (cur->get_obj())) + return false; + + NetNet*cur_sig = dynamic_cast (cur->get_obj()); + if (cur_sig == 0) + continue; + + if (!cur_sig->local_flag()) + return false; + + sig = cur_sig; + } + assert(sig); + ostringstream res; + res << "_" << sig->name(); + sig->rename(lex_strings.make(res.str())); + sig->local_flag(false); + return true; +} + +/* + * The vlog95 target implements a wide mux as a hierarchy of 2:1 muxes, + * picking off one bit of the select input at each level of the hierarchy. + */ +void exposenodes_functor::lpm_mux(Design*, NetMux*obj) +{ + if (obj->sel_width() == 1) + return; + + if (expose_nexus(obj->pin_Sel().nexus())) + count += 1; +} + +/* + * A VP part select is going to select a part from its input. + */ +void exposenodes_functor::lpm_part_select(Design*, NetPartSelect*obj) +{ + if (obj->dir() != NetPartSelect::VP) + return; + + if (expose_nexus(obj->pin(1).nexus())) + count += 1; +} + +/* + * A substitute is going to select one or two parts from the wider input signal. + */ +void exposenodes_functor::lpm_substitute(Design*, NetSubstitute*obj) +{ + if (expose_nexus(obj->pin(1).nexus())) + count += 1; +} + +void exposenodes(Design*des) +{ + exposenodes_functor exposenodes; + exposenodes.count = 0; + if (verbose_flag) { + cout << " ... Look for intermediate nodes" << endl << flush; + } + des->functor(&exposenodes); + if (verbose_flag) { + cout << " ... Exposed " << exposenodes.count + << " intermediate signals." << endl << flush; + } +} diff --git a/expr_synth.cc b/expr_synth.cc index b1a2a8165..86dd78dd4 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2018 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 @@ -274,7 +274,18 @@ NetNet* NetEBComp::synthesize(Design*des, NetScope*scope, NetExpr*root) if (op_ == 'E' || op_ == 'N') { NetCaseCmp*gate = new NetCaseCmp(scope, scope->local_symbol(), - width, op_=='E'?NetCaseCmp::EEQ:NetCaseCmp::NEQ); + width, op_=='E' ? NetCaseCmp::EEQ : NetCaseCmp::NEQ); + gate->set_line(*this); + connect(gate->pin(0), osig->pin(0)); + connect(gate->pin(1), lsig->pin(0)); + connect(gate->pin(2), rsig->pin(0)); + des->add_node(gate); + return osig; + } + + if (op_ == 'w' || op_ == 'W') { + NetCaseCmp*gate = new NetCaseCmp(scope, scope->local_symbol(), + width, op_=='w' ? NetCaseCmp::WEQ : NetCaseCmp::WNE); gate->set_line(*this); connect(gate->pin(0), osig->pin(0)); connect(gate->pin(1), lsig->pin(0)); @@ -335,6 +346,7 @@ NetNet* NetEBComp::synthesize(Design*des, NetScope*scope, NetExpr*root) des->errors += 1; return 0; } + // fallthrough case 'e': // == connect(dev->pin_AEB(), osig->pin(0)); break; @@ -353,6 +365,7 @@ NetNet* NetEBComp::synthesize(Design*des, NetScope*scope, NetExpr*root) des->errors += 1; return 0; } + // fallthrough case 'n': // != connect(dev->pin_ANEB(), osig->pin(0)); break; @@ -733,12 +746,18 @@ NetNet* NetEConcat::synthesize(Design*des, NetScope*scope, NetExpr*root) } } - if (flag == false) return 0; + if (flag == false) { + delete[]tmp; + return 0; + } ivl_assert(*this, data_type != IVL_VT_NO_TYPE); /* If this is a replication of zero just return 0. */ - if (expr_width() == 0) return 0; + if (expr_width() == 0) { + delete[]tmp; + return 0; + } /* Make a NetNet object to carry the output vector. */ perm_string path = scope->local_symbol(); @@ -1341,6 +1360,7 @@ static NetEvWait* make_func_trigger(Design*des, NetScope*scope, NetExpr*root) if (nset && (nset->size() > 0)) { NetEvent*ev = new NetEvent(scope->local_symbol()); ev->set_line(*root); + ev->local_flag(true); NetEvProbe*pr = new NetEvProbe(scope, scope->local_symbol(), ev, NetEvProbe::ANYEDGE, diff --git a/functor.cc b/functor.cc index 14a72c613..280a6f9aa 100644 --- a/functor.cc +++ b/functor.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2012 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 @@ -78,6 +78,10 @@ void functor_t::lpm_ff(Design*, NetFF*) { } +void functor_t::lpm_latch(Design*, NetLatch*) +{ +} + void functor_t::lpm_logic(Design*, NetLogic*) { } @@ -102,6 +106,10 @@ void functor_t::sign_extend(Design*, NetSignExtend*) { } +void functor_t::lpm_substitute(Design*, NetSubstitute*) +{ +} + void functor_t::lpm_ureduce(Design*, NetUReduce*) { } @@ -215,6 +223,11 @@ void NetFF::functor_node(Design*des, functor_t*fun) fun->lpm_ff(des, this); } +void NetLatch::functor_node(Design*des, functor_t*fun) +{ + fun->lpm_latch(des, this); +} + void NetLiteral::functor_node(Design*des, functor_t*fun) { fun->lpm_literal(des, this); @@ -255,6 +268,11 @@ void NetSignExtend::functor_node(Design*des, functor_t*fun) fun->sign_extend(des, this); } +void NetSubstitute::functor_node(Design*des, functor_t*fun) +{ + fun->lpm_substitute(des, this); +} + void NetUReduce::functor_node(Design*des, functor_t*fun) { fun->lpm_ureduce(des, this); diff --git a/functor.h b/functor.h index b72e8efd3..716ceda64 100644 --- a/functor.h +++ b/functor.h @@ -1,7 +1,7 @@ #ifndef IVL_functor_H #define IVL_functor_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 @@ -75,6 +75,9 @@ struct functor_t { /* This method is called for each FF in the design. */ virtual void lpm_ff(class Design*des, class NetFF*); + /* This method is called for each LATCH in the design. */ + virtual void lpm_latch(class Design*des, class NetLatch*); + /* Handle LPM combinational logic devices. */ virtual void lpm_logic(class Design*des, class NetLogic*); @@ -89,6 +92,9 @@ struct functor_t { /* This method is called for each power. */ virtual void lpm_pow(class Design*des, class NetPow*); + /* This method is called for each part substitute. */ + virtual void lpm_substitute(class Design*des, class NetSubstitute*); + /* This method is called for each unary reduction gate. */ virtual void lpm_ureduce(class Design*des, class NetUReduce*); diff --git a/iverilog-vpi.man.in b/iverilog-vpi.man.in index b1708bddf..bd8f7a930 100644 --- a/iverilog-vpi.man.in +++ b/iverilog-vpi.man.in @@ -1,10 +1,10 @@ -.TH iverilog-vpi 1 "April 17th, 2009" "" "Version %M.%m.%n %E" +.TH iverilog-vpi 1 "Jan 29th, 2017" "" "Version %M.%n%E" .SH NAME iverilog-vpi - Compile front end for VPI modules .SH SYNOPSIS .B iverilog-vpi -[\-\-name=\fIname\fP] +[options] \fIsourcefile\fP... .SH DESCRIPTION @@ -15,9 +15,9 @@ list of C or C++ source files, and generates as output a linked VPI module. See the \fBvvp\fP(1) man page for a description of how the linked module is loaded by a simulation. -The output is named after the first source file. For example, if the -first source file is named \fIfoo.c\fP, the output becomes -\fIfoo.vpi\fP. +By default the output is named after the first source file. For +example, if the first source file is named \fIfoo.c\fP, the output +becomes \fIfoo.vpi\fP. .SH OPTIONS \fIiverilog\-vpi\fP accepts the following options: @@ -26,10 +26,15 @@ first source file is named \fIfoo.c\fP, the output becomes Include the named library in the link of the VPI module. This allows VPI modules to further reference external libraries. +.TP 8 +.B -L\fIdirectory\fP +Add \fIdirectory\fP to the list of directories that will be searched +for library files. + .TP 8 .B -I\fIdirectory\fP -Add \fIdirectory\fP to the list of directories that will be search for -header files. +Add \fIdirectory\fP to the list of directories that will be searched +for header files. .TP 8 .B -D\fIdefine\fP @@ -41,46 +46,38 @@ Normally, the output VPI module will be named after the first source file passed to the command. This flag sets the name (without the .vpi suffix) of the output vpi module. -.TP 8 -.B --install-dir -This flag causes the program to print the install directory for VPI -modules, then exit. It is a convenience for makefiles or automated -plug-in installers. - -.TP 8 -.B --cflags, --ldflags and --ldlibs -These flags provide compile time information. - .SH "PC-ONLY OPTIONS" -The PC port of \fIiverilog\-vpi\fP includes two special flags needed to -support the more intractable development environment. These flags help -the program locate parts that it needs. +When built as a native Windows program (using the MinGW toolchain), +by default \fIiverilog\-vpi\fP will attempt to locate the MinGW tools +needed to compile a VPI module on the system path (as set by the PATH +environment variable). As an alternative, the user may specify the +location of the MinGW tools via the following option. .TP 8 .B -mingw=\fIpath\fP -Tell the program the root of the Mingw compiler tool suite. The +Tell the program the root of the MinGW compiler tool suite. The \fBvvp\fP runtime is compiled with this compiler, and this is the -compiler that \fIiverilog\-vpi\fP expects to use to compile your source -code. This is normally not needed, and if you do use it, it is only -needed once. The compiler will save the \fIpath\fP in the registry for -use later. +compiler that \fIiverilog\-vpi\fP expects to use to compile your +source code. If this option accompanies a list of files, it will +apply to the current build only. If this option is provided on its +own, \fIiverilog\-vpi\fP will save the \fIpath\fP in the registry +and use that path in preference to the system path for subsequent +operations, avoiding the need to specify it on the command line +every time. + +.SH "INFORMATIONAL OPTIONS" + +\fIiverilog\-vpi\fP includes additional flags to let Makefile gurus +peek at the configuration of the \fIiverilog\fP installation. This way, +Makefiles can be written that handle complex VPI builds natively, and +without hard-coding values that depend on the system and installation. +If used at all, these options must be used one at a time, and without +any other options or directives. .TP 8 -.B -ivl=\fIpath\fP -Set for the use during compilation the root if the Icarus Verilog -install. This is the place where you installed Icarus Verilog when you -ran the installer. This flag is also only needed once, and the path is -stored in the registry for future use. - -.SH "UNIX-ONLY OPTIONS" - -The UNIX version of \fIiverilog\-vpi\fP includes additional flags to -let Makefile gurus peek at the configuration of the \fIiverilog\fP -installation. This way, Makefiles can be written that handle complex VPI -builds natively, and without hard-coding values that depend on the -system and installation. If used at all, these options must be -used one at a time, and without any other options or directives. +.B --install-dir +Print the install directory for VPI modules. .TP 8 .B --cflags @@ -95,11 +92,6 @@ Print the linker flags (LDFLAGS) needed to link a VPI module. .B --ldlibs Print the libraries (LDLIBS) needed to link a VPI module. -.TP 8 -.B -m32 -On 64bit systems that support it (and support vvp32) this flag -requests a 32bit vpi binary instead of the default 64bit binary. - .P Example GNU makefile that takes advantage of these flags: .IP "" 4 @@ -123,12 +115,12 @@ Steve Williams (steve@icarus.com) .SH SEE ALSO iverilog(1), vvp(1), -.BR "", -.BR "", +.BR "", +.BR "", .SH COPYRIGHT .nf -Copyright \(co 2002\-2009 Stephen Williams +Copyright \(co 2002\-2017 Stephen Williams This document can be freely redistributed according to the terms of the GNU General Public License version 2.0 diff --git a/iverilog-vpi.sh b/iverilog-vpi.sh index d1554d420..026e3e6ea 100644 --- a/iverilog-vpi.sh +++ b/iverilog-vpi.sh @@ -35,6 +35,7 @@ CCSRC= CXSRC= OBJ= LIB= +LIBDIR= OUT= INCOPT= DEFS= @@ -81,6 +82,9 @@ do -l*) LIB="$LIB $parm" ;; + -L*) LIBDIR="$LIBDIR $parm" + ;; + -I*) INCOPT="$INCOPT $parm" ;; @@ -92,6 +96,11 @@ do exit; ;; + --ccflags) + echo "$CXXFLAGS" + exit; + ;; + --ldflags) echo "$LDFLAGS" exit; @@ -148,4 +157,4 @@ then fi echo "Making $OUT from $OBJ..." -exec $LD -o $OUT $LDFLAGS $OBJ $LIB $LDLIBS +exec $LD -o $OUT $LDFLAGS $LIBDIR $OBJ $LIB $LDLIBS diff --git a/ivl.def b/ivl.def index a49849ba7..39682eaf5 100644 --- a/ivl.def +++ b/ivl.def @@ -125,6 +125,7 @@ ivl_lpm_enable ivl_lpm_file ivl_lpm_lineno ivl_lpm_name +ivl_lpm_negedge ivl_lpm_q ivl_lpm_scope ivl_lpm_select @@ -210,6 +211,9 @@ ivl_scope_enumerates ivl_scope_event ivl_scope_events ivl_scope_file +ivl_scope_func_type +ivl_scope_func_signed +ivl_scope_func_width ivl_scope_is_auto ivl_scope_is_cell ivl_scope_lineno @@ -290,6 +294,7 @@ ivl_stmt_lval ivl_stmt_lvals ivl_stmt_lwidth ivl_stmt_name +ivl_stmt_needs_t0_trigger ivl_stmt_nevent ivl_stmt_opcode ivl_stmt_parm diff --git a/ivl_alloc.h b/ivl_alloc.h index 76f3d09ca..181a2544a 100644 --- a/ivl_alloc.h +++ b/ivl_alloc.h @@ -26,6 +26,7 @@ # include #endif +#if defined(__GNUC__) /* * Define a safer version of malloc(). */ @@ -82,5 +83,6 @@ } \ __ivl_rtn; \ }) +#endif #endif /* IVL_ivl_alloc_H */ diff --git a/ivl_target.h b/ivl_target.h index 0198eec09..f3fed5669 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -1,7 +1,7 @@ #ifndef IVL_ivl_target_H #define IVL_ivl_target_H /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 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 @@ -41,6 +41,12 @@ # define __attribute__(x) #endif +#if defined(__cplusplus) && defined(_MSC_VER) +# define ENUM_UNSIGNED_INT : unsigned int +#else +# define ENUM_UNSIGNED_INT +#endif + _BEGIN_DECL /* @@ -201,7 +207,7 @@ typedef enum ivl_dis_domain_e { IVL_DIS_CONTINUOUS = 2 } ivl_dis_domain_t; -typedef enum ivl_drive_e { +typedef enum ivl_drive_e ENUM_UNSIGNED_INT { IVL_DR_HiZ = 0, IVL_DR_SMALL = 1, IVL_DR_MEDIUM = 2, @@ -243,7 +249,7 @@ typedef enum ivl_expr_type_e { IVL_EX_UNARY = 14 } ivl_expr_type_t; -typedef enum ivl_select_type_e { +typedef enum ivl_select_type_e ENUM_UNSIGNED_INT { IVL_SEL_OTHER = 0, IVL_SEL_IDX_UP = 1, IVL_SEL_IDX_DOWN = 2 @@ -300,8 +306,10 @@ typedef enum ivl_lpm_type_e { IVL_LPM_CONCAT = 16, IVL_LPM_CONCATZ = 36, /* Transparent concat */ IVL_LPM_CMP_EEQ= 18, /* Case EQ (===) */ - IVL_LPM_CMP_EQX= 37, /* Windcard EQ (==?) */ + IVL_LPM_CMP_EQX= 37, /* Wildcard EQ (casex) */ IVL_LPM_CMP_EQZ= 38, /* casez EQ */ + IVL_LPM_CMP_WEQ= 41, + IVL_LPM_CMP_WNE= 42, IVL_LPM_CMP_EQ = 10, IVL_LPM_CMP_GE = 1, IVL_LPM_CMP_GT = 2, @@ -309,6 +317,7 @@ typedef enum ivl_lpm_type_e { IVL_LPM_CMP_NEE= 19, /* Case NE (!==) */ IVL_LPM_DIVIDE = 12, IVL_LPM_FF = 3, + IVL_LPM_LATCH = 40, IVL_LPM_MOD = 13, IVL_LPM_MULT = 4, IVL_LPM_MUX = 5, @@ -345,10 +354,13 @@ typedef enum ivl_path_edge_e { /* Processes are initial, always, or final blocks with a statement. This is the type of the ivl_process_t object. */ -typedef enum ivl_process_type_e { - IVL_PR_INITIAL = 0, - IVL_PR_ALWAYS = 1, - IVL_PR_FINAL = 2 +typedef enum ivl_process_type_e ENUM_UNSIGNED_INT { + IVL_PR_INITIAL = 0, + IVL_PR_ALWAYS = 1, + IVL_PR_ALWAYS_COMB = 3, + IVL_PR_ALWAYS_FF = 4, + IVL_PR_ALWAYS_LATCH = 5, + IVL_PR_FINAL = 2 } ivl_process_type_t; /* These are the sorts of reasons a scope may come to be. These types @@ -431,7 +443,7 @@ typedef enum ivl_sfunc_as_task_e { /* This is the type of a variable, and also used as the type for an expression. */ -typedef enum ivl_variable_type_e { +typedef enum ivl_variable_type_e ENUM_UNSIGNED_INT { IVL_VT_VOID = 0, /* Not used */ IVL_VT_NO_TYPE = 1, /* Place holder for missing/unknown type. */ IVL_VT_REAL = 2, @@ -1285,8 +1297,8 @@ extern unsigned ivl_lpm_lineno(ivl_lpm_t net); * magnitude compare, the signedness does matter. In any case, the * result of the compare is always unsigned. * - * The EQX and EQZ nodes are windcard compares, where xz bits (EQX) or - * z bits (EQZ) in the data(1) operand are treated as windcards. no + * The EQX and EQZ nodes are wildcard compares, where xz bits (EQX) or + * z bits (EQZ) in the data(1) operand are treated as wildcards. no * bits in the data(0) operand are wild. This matches the * SystemVerilog convention for the ==? operator. * @@ -1303,8 +1315,13 @@ extern unsigned ivl_lpm_lineno(ivl_lpm_t net); * inputs and the Q. All the types must be exactly the same. * * - D-FlipFlop (IVL_LPM_FF) - * This data is an edge sensitive register. The ivl_lpm_q output and - * single ivl_lpm_data input are the same with, ivl_lpm_width. This + * This device is an edge sensitive register. The ivl_lpm_q output and + * single ivl_lpm_data input are the same width, ivl_lpm_width. This + * device carries a vector like other LPM devices. + * + * - Latch (IVL_LPM_LATCH) + * This device is an asynchronous latch. The ivl_lpm_q output and + * single ivl_lpm_data input are the same width, ivl_lpm_width. This * device carries a vector like other LPM devices. * * - Memory port (IVL_LPM_RAM) (deprecated in favor of IVL_LPM_ARRAY) @@ -1418,21 +1435,22 @@ extern ivl_signal_t ivl_lpm_array(ivl_lpm_t net); /* IVL_LPM_PART IVL_LPM_SUBSTITUTE */ extern unsigned ivl_lpm_base(ivl_lpm_t net); /* IVL_LPM_FF */ +extern unsigned ivl_lpm_negedge(ivl_lpm_t net); extern ivl_nexus_t ivl_lpm_clk(ivl_lpm_t net); /* IVL_LPM_UFUNC */ extern ivl_scope_t ivl_lpm_define(ivl_lpm_t net); - /* IVL_LPM_FF */ + /* IVL_LPM_FF IVL_LPM_LATCH*/ extern ivl_nexus_t ivl_lpm_enable(ivl_lpm_t net); /* IVL_LPM_ADD IVL_LPM_CONCAT IVL_LPM_FF IVL_LPM_PART IVL_LPM_MULT IVL_LPM_MUX IVL_LPM_POW IVL_LPM_SHIFTL IVL_LPM_SHIFTR IVL_LPM_SUB - IVL_LPM_UFUNC IVL_LPM_SUBSTITUTE */ + IVL_LPM_UFUNC IVL_LPM_SUBSTITUTE IVL_LPM_LATCH */ extern ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx); /* IVL_LPM_ADD IVL_LPM_MULT IVL_LPM_POW IVL_LPM_SUB IVL_LPM_CMP_EQ IVL_LPM_CMP_EEQ IVL_LPM_CMP_EQX IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE */ extern ivl_nexus_t ivl_lpm_datab(ivl_lpm_t net, unsigned idx); /* IVL_LPM_ADD IVL_LPM_FF IVL_LPM_MULT IVL_LPM_PART IVL_LPM_POW IVL_LPM_SUB IVL_LPM_UFUNC IVL_LPM_CMP_EEQ IVL_LPM_CMP_EQX - IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE IVL_LPM_SUBSTITUTE */ + IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE IVL_LPM_SUBSTITUTE IVL_LPM_LATCH */ extern ivl_nexus_t ivl_lpm_q(ivl_lpm_t net); extern ivl_drive_t ivl_lpm_drive0(ivl_lpm_t net); extern ivl_drive_t ivl_lpm_drive1(ivl_lpm_t net); @@ -1738,6 +1756,13 @@ extern unsigned ivl_parameter_lineno(ivl_parameter_t net); * ivl_scope_lineno * Returns the instantiation file and line for this scope. * + * ivl_scope_func_type + * ivl_scope_func_signed + * ivl_scope_func_width + * + * If the scope is a function, these function can be used to get + * the type of the return value. + * * ivl_scope_is_auto * Is the task or function declared to be automatic? * @@ -1856,6 +1881,9 @@ extern const char* ivl_scope_tname(ivl_scope_t net); extern int ivl_scope_time_precision(ivl_scope_t net); extern int ivl_scope_time_units(ivl_scope_t net); +extern ivl_variable_type_t ivl_scope_func_type(ivl_scope_t net); +extern int ivl_scope_func_signed(ivl_scope_t net); +extern unsigned ivl_scope_func_width(ivl_scope_t net); /* SIGNALS * Signals are named things in the Verilog source, like wires and @@ -2077,6 +2105,7 @@ extern unsigned ivl_stmt_lineno(ivl_statement_t net); * handle disable statements. * * ivl_stmt_events + * ivl_stmt_needs_t0_trigger * ivl_stmt_nevent * Statements that have event arguments (TRIGGER and WAIT) make * those event objects available through these methods. @@ -2204,6 +2233,7 @@ extern ivl_expr_t ivl_stmt_delay_expr(ivl_statement_t net); /* IVL_ST_DELAY */ extern uint64_t ivl_stmt_delay_val(ivl_statement_t net); /* IVL_ST_WAIT IVL_ST_TRIGGER */ +extern unsigned ivl_stmt_needs_t0_trigger(ivl_statement_t net); extern unsigned ivl_stmt_nevent(ivl_statement_t net); extern ivl_event_t ivl_stmt_events(ivl_statement_t net, unsigned idx); /* IVL_ST_CONTRIB */ @@ -2342,4 +2372,6 @@ typedef const char* (*target_query_f) (const char*key); _END_DECL +#undef ENUM_UNSIGNED_INT + #endif /* IVL_ivl_target_H */ diff --git a/ivl_target_priv.h b/ivl_target_priv.h index 489d801eb..3ae882b63 100644 --- a/ivl_target_priv.h +++ b/ivl_target_priv.h @@ -1,7 +1,7 @@ #ifndef IVL_ivl_target_priv_H #define IVL_ivl_target_priv_H /* - * Copyright (c) 2008-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2008-2017 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 @@ -52,7 +52,6 @@ struct ivl_design_s { // Keep arrays of root scopes. std::map classes; - std::map root_tasks; std::vector packages; std::vector roots; diff --git a/ivlpp/globals.h b/ivlpp/globals.h index 90b4632b9..a1f77e95e 100644 --- a/ivlpp/globals.h +++ b/ivlpp/globals.h @@ -1,7 +1,7 @@ #ifndef IVL_globals_H #define IVL_globals_H /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2017 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 @@ -53,6 +53,9 @@ extern char dep_mode; extern int verbose_flag; +extern int warn_redef; +extern int warn_redef_all; + /* This is the entry to the lexer. */ extern int yylex(void); diff --git a/ivlpp/ivlpp.txt b/ivlpp/ivlpp.txt index fe6071f53..a95d05126 100644 --- a/ivlpp/ivlpp.txt +++ b/ivlpp/ivlpp.txt @@ -95,7 +95,7 @@ keys, with their corresponding values, are: This is exactly the same as the "-Dname=" described above. I: - This is exctly the same as "-I". + This is exactly the same as "-I". relative include: The can be "true" or "false". This enables "relative diff --git a/ivlpp/lexor.lex b/ivlpp/lexor.lex index f7d76422d..8a4355264 100644 --- a/ivlpp/lexor.lex +++ b/ivlpp/lexor.lex @@ -1,7 +1,7 @@ %option prefix="yy" %{ /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2017 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 @@ -39,6 +39,7 @@ static void def_finish(void); static void def_undefine(void); static void do_define(void); static int def_is_done(void); +static void def_continue(void); static int is_defined(const char*name); static int macro_needs_args(const char*name); @@ -147,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; @@ -241,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. @@ -358,6 +373,8 @@ keywords (include|define|undef|ifdef|ifndef|else|elseif|endif) if (def_is_done()) { def_finish(); yy_pop_state(); + } else { + def_continue(); } istack->lineno += 1; @@ -511,25 +528,17 @@ 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); } \( { BEGIN(MA_ADD); macro_start_args(); } @@ -568,8 +577,12 @@ keywords (include|define|undef|ifdef|ifndef|else|elseif|endif) "(" { macro_add_to_arg(0); ma_parenthesis_level++; } -"," { if (ma_parenthesis_level > 0) macro_add_to_arg(0); - else macro_finish_arg(); } +"," { + if (ma_parenthesis_level > 0) + macro_add_to_arg(0); + else + macro_finish_arg(); +} ")" { if (ma_parenthesis_level > 0) { @@ -729,7 +742,7 @@ static int is_defined(const char*name) * particular, keep the names and name lengths in a compact stretch of * memory. Note that we do not keep the argument names once the * definition is fully processed, because arguments are always - * positional and the definition string hs replaced with position + * positional and the definition string is replaced with position * tokens. */ static char* def_buf = 0; @@ -811,7 +824,7 @@ static void def_add_arg(void) val[val_length] = 0; } - /* Strip white space from betwen arg and "=". */ + /* Strip white space from between arg and "=". */ length = strlen(arg); while (length>0 && isspace(arg[length-1])) { length -= 1; @@ -845,6 +858,25 @@ void define_macro(const char* name, const char* value, int keyword, int argc) { int idx; struct define_t* def; + struct define_t* prev; + + /* Verilog has a very nasty system of macros jumping from + * file to file, resulting in a global macro scope. Here + * we optionally warn about any redefinitions. + * + * If istack is empty, we are processing a configuration + * or precompiled macro file, so don't want to check for + * redefinitions - when a precompiled macro file is used, + * it will contain copies of any predefined macros. + */ + if (warn_redef && istack) { + prev = def_lookup(name); + if (prev && (warn_redef_all || (strcmp(prev->value, value) != 0))) { + emit_pathline(istack); + fprintf(stderr, "warning: redefinition of macro %s from value '%s' to '%s'\n", + name, prev->value, value); + } + } def = malloc(sizeof(struct define_t)); def->name = strdup(name); @@ -950,6 +982,11 @@ static size_t magic_cnt = 0; #define _STR1(x) #x #define _STR2(x) _STR1(x) +static int is_id_char(char c) +{ + return isalnum((int)c) || c == '_' || c == '$'; +} + /* * Find an argument, but only if it is not directly preceded by something * that would make it part of another simple identifier ([a-zA-Z0-9_$]). @@ -964,12 +1001,8 @@ static char *find_arg(char*ptr, char*head, char*arg) cp = strstr(cp, arg); if (!cp) break; - /* If we are not at the start of the string verify that this - * match is not in the middle of another identifier. - */ - if (cp != head && - (isalnum((int)*(cp-1)) || *(cp-1) == '_' || *(cp-1) == '$' || - isalnum((int)*(cp+len)) || *(cp+len) == '_' || *(cp+len) == '$')) { + /* Verify that this match is not in the middle of another identifier. */ + if ((cp != head && is_id_char(cp[-1])) || is_id_char(cp[len])) { cp++; continue; } @@ -1123,6 +1156,14 @@ static int def_is_done(void) return !define_continue_flag; } +/* + * Reset the define_continue_flag. + */ +static void def_continue(void) +{ + define_continue_flag = 0; +} + /* * After some number of calls to do_define, this function is called to * assigned value to the parsed name. If there is no value, then @@ -1299,14 +1340,30 @@ static void macro_add_to_arg(int is_white_space) static void macro_finish_arg(void) { - char* tail = &def_buf[def_buf_size - def_buf_free]; + int offs; + char* head; + char* tail; check_for_max_args(); + offs = def_argo[def_argc-1] + def_argl[def_argc-1] + 1; + head = &def_buf[offs]; + tail = &def_buf[def_buf_size - def_buf_free]; + + /* Eat any leading and trailing white space. */ + if ((head < tail) && (*head == ' ')) { + offs++; + head++; + } + if ((tail > head) && (*(tail-1) == ' ')) { + def_buf_free++; + tail--; + } + *tail = 0; - def_argo[def_argc] = def_argo[def_argc-1] + def_argl[def_argc-1] + 1; - def_argl[def_argc] = tail - def_argv(def_argc); + def_argo[def_argc] = offs; + def_argl[def_argc] = tail - head; def_buf_free -= 1; def_argc++; @@ -1345,11 +1402,22 @@ static void expand_using_args(void) int arg; int length; - if (def_argc != cur_macro->argc) { + if (def_argc > cur_macro->argc) { emit_pathline(istack); - fprintf(stderr, "error: wrong number of arguments for `%s\n", cur_macro->name); + fprintf(stderr, "error: too many arguments for `%s\n", cur_macro->name); return; } + while (def_argc < cur_macro->argc) { + if (cur_macro->defaults[def_argc]) { + def_argl[def_argc] = 0; + def_argc += 1; + continue; + } + emit_pathline(istack); + fprintf(stderr, "error: too few arguments for `%s\n", cur_macro->name); + return; + } + assert(def_argc == cur_macro->argc); head = cur_macro->value; tail = head; @@ -1898,11 +1966,7 @@ static int load_next_input(void) static void do_dump_precompiled_defines(FILE* out, struct define_t* table) { if (!table->keyword) -#ifdef __MINGW32__ /* MinGW does not know about z. */ - fprintf(out, "%s:%d:%d:%s\n", table->name, table->argc, strlen(table->value), table->value); -#else fprintf(out, "%s:%d:%zd:%s\n", table->name, table->argc, strlen(table->value), table->value); -#endif if (table->left) do_dump_precompiled_defines(out, table->left); @@ -2055,7 +2119,7 @@ void destroy_lexor(void) { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 -# if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 +# if YY_FLEX_MINOR_VERSION > 5 || defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif diff --git a/ivlpp/main.c b/ivlpp/main.c index bc8014d38..19ef09bde 100644 --- a/ivlpp/main.c +++ b/ivlpp/main.c @@ -1,5 +1,5 @@ const char COPYRIGHT[] = - "Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com)"; + "Copyright (c) 1999-2017 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 @@ -98,6 +98,10 @@ int line_direct_flag = 0; unsigned error_count = 0; FILE *depend_file = NULL; +/* Should we warn about macro redefinitions? */ +int warn_redef = 0; +int warn_redef_all = 0; + static int flist_read_flags(const char*path) { char line_buf[2048]; @@ -282,7 +286,7 @@ int main(int argc, char*argv[]) include_dir[0] = 0; /* 0 is reserved for the current files path. */ include_dir[1] = strdup("."); - while ((opt=getopt(argc, argv, "F:f:K:Lo:p:P:vV")) != EOF) switch (opt) { + while ((opt=getopt(argc, argv, "F:f:K:Lo:p:P:vVW:")) != EOF) switch (opt) { case 'F': flist_read_flags(optarg); @@ -336,6 +340,15 @@ int main(int argc, char*argv[]) break; } + case 'W': + if (strcmp(optarg, "redef-all") == 0) { + warn_redef_all = 1; + warn_redef = 1; + } else if (strcmp(optarg, "redef-chg") == 0) { + warn_redef = 1; + } + break; + case 'v': fprintf(stderr, "Icarus Verilog Preprocessor version " VERSION " (" VERSION_TAG ")\n\n"); @@ -366,7 +379,10 @@ int main(int argc, char*argv[]) " -p - Write precompiled defines to \n" " -P - Read precompiled defines from \n" " -v - Verbose\n" - " -V - Print version information and quit\n", + " -V - Print version information and quit\n" + " -W - Enable extra ivlpp warning category:\n" + " o redef-all - all macro redefinitions\n" + " o redef-chg - macro definition changes\n", argv[0]); return flag_errors; } diff --git a/lexor.lex b/lexor.lex index d14e38694..37c3e0207 100644 --- a/lexor.lex +++ b/lexor.lex @@ -4,7 +4,7 @@ %{ /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2017 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,7 @@ static const char* set_file_name(char*text) void reset_lexor(); static void line_directive(); static void line_directive2(); +static void reset_all(); verinum*make_unsized_binary(const char*txt); verinum*make_undef_highz_dec(const char*txt); @@ -187,6 +188,8 @@ TU [munpf] "!=" { return K_NE; } "===" { return K_CEQ; } "!==" { return K_CNE; } +"==?" { return K_WEQ; } +"!=?" { return K_WNE; } "||" { return K_LOR; } "&&" { return K_LAND; } "&&&" { return K_TAND; } @@ -310,15 +313,6 @@ TU [munpf] BEGIN(UDPTABLE); break; - /* Translate these to checks if we already have or are - * outside the declaration region. */ - case K_timeunit: - if (have_timeunit_decl) rc = K_timeunit_check; - break; - case K_timeprecision: - if (have_timeprec_decl) rc = K_timeprecision_check; - break; - default: yylval.text = 0; break; @@ -428,22 +422,38 @@ TU [munpf] if (strcmp(yytext,"$attribute") == 0) return KK_attribute; + + if (gn_system_verilog() && strcmp(yytext,"$unit") == 0) { + yylval.package = pform_units.back(); + return PACKAGE_IDENTIFIER; + } + yylval.text = strdupnew(yytext); return SYSTEM_IDENTIFIER; } -\'[sS]?[dD][ \t]*[0-9][0-9_]* { yylval.number = make_unsized_dec(yytext); - return BASED_NUMBER; } -\'[sS]?[dD][ \t]*[xzXZ?]_* { yylval.number = make_undef_highz_dec(yytext); - return BASED_NUMBER; } -\'[sS]?[bB][ \t]*[0-1xzXZ_\?]+ { yylval.number = make_unsized_binary(yytext); - return BASED_NUMBER; } -\'[sS]?[oO][ \t]*[0-7xzXZ_\?]+ { yylval.number = make_unsized_octal(yytext); - return BASED_NUMBER; } -\'[sS]?[hH][ \t]*[0-9a-fA-FxzXZ_\?]+ { yylval.number = make_unsized_hex(yytext); - return BASED_NUMBER; } +\'[sS]?[dD][ \t]*[0-9][0-9_]* { + yylval.number = make_unsized_dec(yytext); + return BASED_NUMBER; +} +\'[sS]?[dD][ \t]*[xzXZ?]_* { + yylval.number = make_undef_highz_dec(yytext); + return BASED_NUMBER; +} +\'[sS]?[bB][ \t]*[0-1xzXZ?][0-1xzXZ?_]* { + yylval.number = make_unsized_binary(yytext); + return BASED_NUMBER; +} +\'[sS]?[oO][ \t]*[0-7xzXZ?][0-7xzXZ?_]* { + yylval.number = make_unsized_octal(yytext); + return BASED_NUMBER; +} +\'[sS]?[hH][ \t]*[0-9a-fA-FxzXZ?][0-9a-fA-FxzXZ?_]* { + yylval.number = make_unsized_hex(yytext); + 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; @@ -469,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; } @@ -556,11 +566,7 @@ TU [munpf] "definition." << endl; error_count += 1; } else { - pform_set_default_nettype(NetNet::WIRE, yylloc.text, - yylloc.first_line); - in_celldefine = false; - uc_drive = UCD_NONE; - pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); + reset_all(); } } /* Notice and handle the `unconnected_drive directive. */ @@ -847,7 +853,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 { @@ -862,6 +868,14 @@ verinum*make_unsized_binary(const char*txt) for (const char*idx = ptr ; *idx ; idx += 1) if (*idx != '_') size += 1; + if (size == 0) { + VLerror(yylloc, "Numeric literal has no digits in it."); + verinum*out = new verinum(); + out->has_sign(sign_flag); + out->is_single(single_flag); + return out; + } + if ((based_size > 0) && (size > based_size)) yywarn(yylloc, "extra digits given for sized binary constant."); @@ -1579,6 +1593,18 @@ static void line_directive2() yylloc.first_line = lineno; } +/* + * Reset all compiler directives. This will be called when a `resetall + * directive is encountered or when a new compilation unit is started. + */ +static void reset_all() +{ + pform_set_default_nettype(NetNet::WIRE, yylloc.text, yylloc.first_line); + in_celldefine = false; + uc_drive = UCD_NONE; + pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); +} + extern FILE*vl_input; void reset_lexor() { @@ -1587,6 +1613,14 @@ void reset_lexor() /* Announce the first file name. */ yylloc.text = set_file_name(strdupnew(vl_file.c_str())); + + if (separate_compilation) { + reset_all(); + if (!keyword_mask_stack.empty()) { + lexor_keyword_mask = keyword_mask_stack.back(); + keyword_mask_stack.clear(); + } + } } /* @@ -1596,7 +1630,7 @@ void destroy_lexor() { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 -# if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 +# if YY_FLEX_MINOR_VERSION > 5 || defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif diff --git a/libmisc/LineInfo.cc b/libmisc/LineInfo.cc index 753b24970..c0daaff14 100644 --- a/libmisc/LineInfo.cc +++ b/libmisc/LineInfo.cc @@ -27,6 +27,11 @@ LineInfo::LineInfo() { } +LineInfo::LineInfo(const LineInfo&that) : + file_(that.file_), lineno_(that.lineno_) +{ +} + LineInfo::~LineInfo() { } diff --git a/libmisc/LineInfo.h b/libmisc/LineInfo.h index b212bc959..4d4c6beda 100644 --- a/libmisc/LineInfo.h +++ b/libmisc/LineInfo.h @@ -36,6 +36,7 @@ using namespace std; class LineInfo { public: LineInfo(); + LineInfo(const LineInfo&that); virtual ~LineInfo(); // Get a fully formatted file/lineno diff --git a/libmisc/StringHeap.cc b/libmisc/StringHeap.cc index f87c4fcf8..10259dc34 100644 --- a/libmisc/StringHeap.cc +++ b/libmisc/StringHeap.cc @@ -32,8 +32,8 @@ static unsigned string_pool_count = 0; StringHeap::StringHeap() { cell_base_ = 0; - cell_ptr_ = HEAPCELL; - cell_count_ = 0; + cell_size_ = 0; + cell_ptr_ = 0; } StringHeap::~StringHeap() @@ -45,20 +45,24 @@ StringHeap::~StringHeap() const char* StringHeap::add(const char*text) { unsigned len = strlen(text); - assert((len+1) <= HEAPCELL); - - unsigned rem = HEAPCELL - cell_ptr_; + unsigned rem = cell_size_ - cell_ptr_; if (rem < (len+1)) { - cell_base_ = (char*)malloc(HEAPCELL); + // release any unused memory + if (rem > 0) { + cell_base_ = (char*)realloc(cell_base_, cell_ptr_); + assert(cell_base_ != 0); + } + // start new cell + cell_size_ = (len+1) > DEFAULT_CELL_SIZE ? len+1 : DEFAULT_CELL_SIZE; + cell_base_ = (char*)malloc(cell_size_); + cell_ptr_ = 0; + assert(cell_base_ != 0); #ifdef CHECK_WITH_VALGRIND string_pool_count += 1; string_pool = (char **) realloc(string_pool, string_pool_count*sizeof(char **)); string_pool[string_pool_count-1] = cell_base_; #endif - cell_ptr_ = 0; - cell_count_ += 1; - assert(cell_base_ != 0); } char*res = cell_base_ + cell_ptr_; @@ -66,7 +70,7 @@ const char* StringHeap::add(const char*text) cell_ptr_ += len; cell_base_[cell_ptr_++] = 0; - assert(cell_ptr_ <= HEAPCELL); + assert(cell_ptr_ <= cell_size_); return res; } diff --git a/libmisc/StringHeap.h b/libmisc/StringHeap.h index 5c791e104..2d4b97d43 100644 --- a/libmisc/StringHeap.h +++ b/libmisc/StringHeap.h @@ -46,7 +46,7 @@ class perm_string { private: friend class StringHeap; friend class StringHeapLex; - perm_string(const char*t) : text_(t) { }; + explicit perm_string(const char*t) : text_(t) { }; private: const char*text_; @@ -78,11 +78,11 @@ class StringHeap { perm_string make(const char*); private: - enum { HEAPCELL = 0x10000 }; + static const unsigned DEFAULT_CELL_SIZE = 0x10000; char*cell_base_; + unsigned cell_size_; unsigned cell_ptr_; - unsigned cell_count_; private: // not implemented StringHeap(const StringHeap&); diff --git a/libveriuser/Makefile.in b/libveriuser/Makefile.in index 868f3e257..989d7c980 100644 --- a/libveriuser/Makefile.in +++ b/libveriuser/Makefile.in @@ -47,7 +47,7 @@ LDRELOCFLAGS = @LDRELOCFLAGS@ LDTARGETFLAGS = @LDTARGETFLAGS@ -CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ +CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ -DICARUS_VPI_CONST=const @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ A = a_close.o a_compare_handles.o a_configure.o a_fetch_argc.o \ @@ -76,7 +76,8 @@ distclean: clean rm -f config.h stamp-config-h cppcheck: $(O:.o=.c) - cppcheck --enable=all -f $(INCLUDE_PATH) $^ + cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in cd ..; ./config.status --file=libveriuser/$@ diff --git a/libveriuser/a_fetch_param.c b/libveriuser/a_fetch_param.c index 5993a47da..bf7b5ba8c 100644 --- a/libveriuser/a_fetch_param.c +++ b/libveriuser/a_fetch_param.c @@ -36,7 +36,7 @@ double acc_fetch_paramval(handle object) fprintf(pli_trace, "acc_fetch_paramval(%s) --> \"%s\"\n", vpi_get_str(vpiName, object), val.value.str); } - return (double) (long)val.value.str; + return (double) (intptr_t)val.value.str; default: vpi_printf("XXXX: parameter %s has type %d\n", diff --git a/libveriuser/cppcheck.sup b/libveriuser/cppcheck.sup new file mode 100644 index 000000000..3fd3b2bb5 --- /dev/null +++ b/libveriuser/cppcheck.sup @@ -0,0 +1,187 @@ +// These are the functions that the runtime exports. + +// The ACC functions. + +// acc_close() +unusedFunction:a_close.c:23 +// acc_compare_handles() +unusedFunction:a_compare_handles.c:23 +// acc_configure() +unusedFunction:a_configure.c:25 + +// acc_fetch_argc() +unusedFunction:a_fetch_argc.c:27 +// acc_fetch_argv() +unusedFunction:a_fetch_argv.c:27 +// acc_fetch_defname() +unusedFunction:a_fetch_fullname.c:37 +// acc_fetch_direction() +unusedFunction:a_fetch_dir.c:26 +// acc_fetch_fulltype() +unusedFunction:a_fetch_type.c:66 +// acc_fetch_itfarg() +unusedFunction:a_fetch_tfarg.c:27 +// acc_fetch_location() +unusedFunction:a_fetch_location.c:23 +// acc_fetch_name() +unusedFunction:a_fetch_fullname.c:32 +// acc_fetch_paramtype() +unusedFunction:a_fetch_type_str.c:47 +// acc_fetch_paramval() +unusedFunction:a_fetch_param.c:25 +// acc_fetch_range() +unusedFunction:a_fetch_range.c:26 +// acc_fetch_size() +unusedFunction:a_fetch_type.c:24 +// acc_fetch_tfarg() +unusedFunction:a_fetch_tfarg.c:56 +// acc_fetch_tfarg_int() +unusedFunction:a_fetch_tfarg.c:91 +// acc_fetch_timescale_info() +unusedFunction:a_fetch_time.c:24 +// acc_fetch_type() +unusedFunction:a_fetch_type.c:29 +// acc_fetch_type_str() +unusedFunction:a_fetch_type_str.c:24 +// acc_fetch_value() +unusedFunction:a_fetch_value.c:115 + +// acc_handle_by_name() +unusedFunction:a_handle_by_name.c:29 +// acc_handle_hiconn() +unusedFunction:a_handle_hiconn.c:26 +// acc_handle_object() +unusedFunction:a_handle_object.c:26 +// acc_handle_parent() +unusedFunction:a_handle_parent.c:24 +// acc_handle_scope() +unusedFunction:a_handle_parent.c:34 +// acc_handle_simulated_net() +unusedFunction:a_handle_simulated_net.c:26 +// acc_handle_tfarg() +unusedFunction:a_handle_tfarg.c:26 +// acc_handle_tfinst() +unusedFunction:a_handle_tfarg.c:53 + +// acc_initialize() +unusedFunction:a_initialize.c:24 + +// acc_next_bit() +unusedFunction:a_next_bit.c:26 +// acc_next_port() +unusedFunction:a_next_port.c:26 +// acc_next_scope() +unusedFunction:a_next.c:86 +// acc_next_topmod() +unusedFunction:a_next_topmod.c:30 + +// acc_product_version() +unusedFunction:a_product_version.c:23 + +// acc_set_scope() +unusedFunction:a_handle_object.c:40 + +// acc_set_value() +unusedFunction:a_set_value.c:27 + +// acc_vcl_add() +unusedFunction:a_vcl.c:157 +// acc_vcl_delete() +unusedFunction:a_vcl.c:196 + +// acc_version() +unusedFunction:a_version.c:23 + +// These are the TF routines. + +// io_printf() +unusedFunction:io_print.c:26 + +// mc_scan_plusargs() +unusedFunction:mc_scan_plusargs.c:27 + +// tf_asynchoff() +unusedFunction:asynch.c:34 +// tf_asynchon() +unusedFunction:asynch.c:28 +// tf_dofinish() +unusedFunction:finish.c:26 +// tf_dostop() +unusedFunction:finish.c:32 +// tf_error() +unusedFunction:io_print.c:45 +// tf_exprinfo() +unusedFunction:exprinfo.c:26 +// tf_getcstringp() +unusedFunction:getcstringp.c:26 +// tf_getlongp() +unusedFunction:getlongp.c:29 +// tf_getlongtime() +unusedFunction:getsimtime.c:112 +// tf_getp() +unusedFunction:getp.c:73 +// tf_getrealp() +unusedFunction:getp.c:120 +// tf_gettime() +unusedFunction:getsimtime.c:77 +// tf_getworkarea() +unusedFunction:workarea.c:62 +// tf_igettimeprecision() +unusedFunction:getsimtime.c:198 +// tf_igettimeunit() +unusedFunction:getsimtime.c:228 +// tf_long_to_real() +unusedFunction:math.c:47 +// tf_message() +unusedFunction:io_print.c:56 +// tf_mipname() +unusedFunction:spname.c:47 +// tf_multiply_long() +unusedFunction:math.c:27 +// tf_nodeinfo() +unusedFunction:nodeinfo.c:27 +// tf_nump() +unusedFunction:nump.c:42 +// tf_putlongp() +unusedFunction:putlongp.c:28 +// tf_putp() +unusedFunction:putp.c:88 +// tf_putrealp() +unusedFunction:putp.c:137 +// tf_real_to_long() +unusedFunction:math.c:40 +// tf_rosynchronize() +unusedFunction:veriusertfs.c:391 +// tf_scale_longdelay() +unusedFunction:getsimtime.c:136 +// tf_scale_realdelay() +unusedFunction:getsimtime.c:161 +// tf_setdelay() +unusedFunction:delay.c:75 +// tf_setrealdelay() +unusedFunction:veriusertfs.c:426 +// tf_setworkarea() +unusedFunction:workarea.c:36 +// tf_spname() +unusedFunction:spname.c:25 +// tf_strgetp() +unusedFunction:getp.c:177 +// tf_strgettime() +unusedFunction:getsimtime.c:85 +// tf_synchronize() +unusedFunction:veriusertfs.c:364 +// tf_typep() +unusedFunction:typep.c:24 +// tf_unscale_longdelay() +unusedFunction:getsimtime.c:144 +// tf_unscale_realdelay() +unusedFunction:getsimtime.c:171 +// tf_warning() +unusedFunction:io_print.c:34 + +// Non-standard TF routines the Icarus provides. + +// tf_getlongsimtime() +unusedFunction:getsimtime.c:126 +// veriusertfs_register_table() +unusedFunction:veriusertfs.c:76 diff --git a/libveriuser/getsimtime.c b/libveriuser/getsimtime.c index 0a7ad914f..a8812bd0c 100644 --- a/libveriuser/getsimtime.c +++ b/libveriuser/getsimtime.c @@ -115,7 +115,7 @@ PLI_INT32 tf_getlongtime(PLI_INT32 *high) } /* - * This function is not defined in the IEE standard, but is provided for + * This function is not defined in the IEEE standard, but is provided for * compatibility with other simulators. On platforms that support this, * make it a weak symbol just in case the user has defined their own * function for this. diff --git a/libveriuser/veriusertfs.c b/libveriuser/veriusertfs.c index 979b2b5d3..8807f6649 100644 --- a/libveriuser/veriusertfs.c +++ b/libveriuser/veriusertfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2014 Michael Ruff (mruff at chiaro.com) + * Copyright (c) 2002-2018 Michael Ruff (mruff at chiaro.com) * Michael Runyan (mrunyan at chiaro.com) * * This source code is free software; you can redistribute it @@ -42,8 +42,9 @@ typedef struct t_pli_data { int paramvc; /* parameter number for misctf */ } s_pli_data, *p_pli_data; -static PLI_INT32 compiletf(char *); -static PLI_INT32 calltf(char *); +static PLI_INT32 compiletf(ICARUS_VPI_CONST PLI_BYTE8 *); +static PLI_INT32 calltf(ICARUS_VPI_CONST PLI_BYTE8 *); +static PLI_INT32 sizetf(ICARUS_VPI_CONST PLI_BYTE8 *); static PLI_INT32 callback(p_cb_data); /* @@ -150,7 +151,7 @@ void veriusertfs_register_table(p_tfcell vtable) tf_data.tfname = tf->tfname; tf_data.compiletf = compiletf; tf_data.calltf = calltf; - tf_data.sizetf = (PLI_INT32 (*)(PLI_BYTE8 *))tf->sizetf; + tf_data.sizetf = sizetf; tf_data.user_data = (char *)data; if (pli_trace) { @@ -180,7 +181,7 @@ void veriusertfs_register_table(p_tfcell vtable) * This function calls the veriusertfs checktf and sets up all the * callbacks misctf requires. */ -static PLI_INT32 compiletf(char *data) +static PLI_INT32 compiletf(ICARUS_VPI_CONST PLI_BYTE8*data) { p_pli_data pli; p_tfcell tf; @@ -260,7 +261,7 @@ static PLI_INT32 compiletf(char *data) /* * This function is the wrapper for the veriusertfs calltf routine. */ -static PLI_INT32 calltf(char *data) +static PLI_INT32 calltf(ICARUS_VPI_CONST PLI_BYTE8*data) { int rc = 0; p_pli_data pli; @@ -283,6 +284,32 @@ static PLI_INT32 calltf(char *data) return rc; } +/* + * This function is the wrapper for the veriusertfs sizetf routine. + */ +static PLI_INT32 sizetf(ICARUS_VPI_CONST PLI_BYTE8*data) +{ + int rc = 32; + p_pli_data pli; + p_tfcell tf; + + /* cast back from opaque */ + pli = (p_pli_data)data; + tf = pli->tf; + + /* execute sizetf */ + if (tf->sizetf) { + if (pli_trace) { + fprintf(pli_trace, "Call %s->sizetf(%d, %d)\n", + tf->tfname, tf->data, reason_sizetf); + } + + rc = tf->sizetf(tf->data, reason_sizetf); + } + + return rc; +} + /* * This function is the wrapper for all the misctf callbacks */ diff --git a/link_const.cc b/link_const.cc index e28388bbf..d5b77a6a8 100644 --- a/link_const.cc +++ b/link_const.cc @@ -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 @@ -102,6 +102,17 @@ bool Nexus::drivers_constant() const break; } + const NetSubstitute*ps = dynamic_cast(cur->get_obj()); + if (ps) { + if (ps->pin(1).nexus()->drivers_constant() && + ps->pin(2).nexus()->drivers_constant() ) { + constant_drivers += 1; + continue; + } + driven_ = VAR; + return false; + } + if (! dynamic_cast(cur->get_obj())) { driven_ = VAR; return false; @@ -162,13 +173,13 @@ verinum::V Nexus::driven_value() const if ((sig->type() == NetNet::SUPPLY0) || (sig->type() == NetNet::TRI0)) { // Multiple drivers are not currently supported. - ivl_assert(*obj, val == verinum::Vz); + ivl_assert(*sig, val == verinum::Vz); val = verinum::V0; } if ((sig->type() == NetNet::SUPPLY1) || (sig->type() == NetNet::TRI1)) { // Multiple drivers are not currently supported. - ivl_assert(*obj, val == verinum::Vz); + ivl_assert(*sig, val == verinum::Vz); val = verinum::V1; } } @@ -199,10 +210,12 @@ verinum Nexus::driven_vector() const const Link*cur = list_; verinum val; + verinum pval; unsigned width = 0; for (cur = first_nlink() ; cur ; cur = cur->next_nlink()) { + const NetSubstitute*ps; const NetConst*obj; const NetNet*sig; if ((obj = dynamic_cast(cur->get_obj()))) { @@ -212,6 +225,18 @@ verinum Nexus::driven_vector() const val = obj->value(); width = val.len(); + } else if ((ps = dynamic_cast(cur->get_obj()))) { + if (cur->get_pin() != 0) + continue; + + // Multiple drivers are not currently supported. + ivl_assert(*ps, val.len() == 0); + val = ps->pin(1).nexus()->driven_vector(); + pval = ps->pin(2).nexus()->driven_vector(); + for (unsigned idx = 0; idx < pval.len(); idx += 1) + val.set(ps->base() + idx, pval.get(idx)); + width = val.len(); + } else if ((sig = dynamic_cast(cur->get_obj()))) { width = sig->vector_width(); @@ -222,13 +247,13 @@ verinum Nexus::driven_vector() const if ((sig->type() == NetNet::SUPPLY0) || (sig->type() == NetNet::TRI0)) { // Multiple drivers are not currently supported. - ivl_assert(*obj, val.len() == 0); + ivl_assert(*sig, val.len() == 0); val = verinum(verinum::V0, width); } if ((sig->type() == NetNet::SUPPLY1) || (sig->type() == NetNet::TRI1)) { // Multiple drivers are not currently supported. - ivl_assert(*obj, val.len() == 0); + ivl_assert(*sig, val.len() == 0); val = verinum(verinum::V1, width); } } diff --git a/load_module.cc b/load_module.cc index 665b865e9..b285302f2 100644 --- a/load_module.cc +++ b/load_module.cc @@ -82,36 +82,10 @@ bool load_module(const char*type) fflush(depend_file); } - if (ivlpp_string) { - char*cmdline = (char*)malloc(strlen(ivlpp_string) + - strlen(path) + 4); - strcpy(cmdline, ivlpp_string); - strcat(cmdline, " \""); - strcat(cmdline, path); - strcat(cmdline, "\""); + if (verbose_flag) + cerr << "Loading library file " << path << "." << endl; - if (verbose_flag) - cerr << "Executing: " << cmdline << endl<< flush; - - FILE*file = popen(cmdline, "r"); - - if (verbose_flag) - cerr << "...parsing output from preprocessor..." << endl << flush; - - pform_parse(path, file); - pclose(file); - free(cmdline); - - } else { - if (verbose_flag) - cerr << "Loading library file " - << path << "." << endl; - - FILE*file = fopen(path, "r"); - assert(file); - pform_parse(path, file); - fclose(file); - } + pform_parse(path); if (verbose_flag) cerr << "... Load module complete." << endl << flush; @@ -119,7 +93,6 @@ bool load_module(const char*type) return true; } - return false; } diff --git a/main.cc b/main.cc index 377dfc55a..290f766a7 100644 --- a/main.cc +++ b/main.cc @@ -1,5 +1,5 @@ const char COPYRIGHT[] = - "Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com)"; + "Copyright (c) 1998-2017 Stephen Williams (steve@icarus.com)"; /* * This source code is free software; you can redistribute it @@ -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 = true; bool gn_verilog_ams_flag = false; /* @@ -139,6 +140,8 @@ void add_vpi_module(const char*name) map missing_modules; map library_file_map; +vector source_files; + list library_suff; list roots; @@ -153,6 +156,7 @@ FILE *depend_file = NULL; * These are the warning enable flags. */ bool warn_implicit = false; +bool warn_implicit_dimensions = false; bool warn_timescale = false; bool warn_portbinding = false; bool warn_inf_loop = false; @@ -160,6 +164,12 @@ bool warn_ob_select = false; bool warn_sens_entire_vec = false; bool warn_sens_entire_arr = false; bool warn_anachronisms = false; +bool warn_floating_nets = false; + +/* + * Ignore errors about missing modules + */ +bool ignore_missing_modules = false; /* * Debug message class flags. @@ -171,6 +181,11 @@ bool debug_emit = false; bool debug_synth2 = false; bool debug_optimizer = false; +/* + * Compilation control flags. + */ +bool separate_compilation = false; + /* * Optimization control flags. */ @@ -224,6 +239,7 @@ const bool CASE_SENSITIVE = true; bool synthesis = false; extern void cprop(Design*des); +extern void exposenodes(Design*des); extern void synth(Design*des); extern void synth2(Design*des); extern void syn_rules(Design*des); @@ -234,10 +250,11 @@ static struct net_func_map { const char*name; void (*func)(Design*); } func_table[] = { - { "cprop", &cprop }, - { "nodangle",&nodangle }, - { "synth", &synth }, - { "synth2", &synth2 }, + { "cprop", &cprop }, + { "exposenodes", &exposenodes }, + { "nodangle", &nodangle }, + { "synth", &synth }, + { "synth2", &synth2 }, { "syn-rules", &syn_rules }, { 0, 0 } }; @@ -343,6 +360,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 { } } @@ -559,6 +582,9 @@ static bool set_default_timescale(const char*ts_string) * * warnings: * Warning flag letters. + * + * ignore_missing_modules: + * true to ignore errors about missing modules */ bool had_timescale = false; static void read_iconfig_file(const char*ipath) @@ -675,9 +701,15 @@ static void read_iconfig_file(const char*ipath) } else if (strcmp(buf,"warnings") == 0) { /* Scan the warnings enable string for warning flags. */ for ( ; *cp ; cp += 1) switch (*cp) { + case 'f': + warn_floating_nets = true; + break; case 'i': warn_implicit = true; break; + case 'd': + warn_implicit_dimensions = true; + break; case 'l': warn_inf_loop = true; break; @@ -703,6 +735,10 @@ static void read_iconfig_file(const char*ipath) break; } + } else if (strcmp(buf, "ignore_missing_modules") == 0) { + if (strcmp(cp, "true") == 0) + ignore_missing_modules = true; + } else if (strcmp(buf, "-y") == 0) { build_library_index(cp, CASE_SENSITIVE); @@ -748,6 +784,38 @@ static void read_iconfig_file(const char*ipath) fclose(ifile); } +/* + * This function reads a list of source file names. Each name starts + * with the first non-space character, and ends with the last non-space + * character. Spaces in the middle are OK. + */ +static void read_sources_file(const char*path) +{ + char line_buf[2048]; + + FILE*fd = fopen(path, "r"); + if (fd == 0) { + cerr << "ERROR: Unable to read source file list: " << path << endl; + return; + } + + while (fgets(line_buf, sizeof line_buf, fd) != 0) { + char*cp = line_buf + strspn(line_buf, " \t\r\b\f"); + char*tail = cp + strlen(cp); + while (tail > cp) { + if (! isspace((int)tail[-1])) + break; + tail -= 1; + tail[0] = 0; + } + + if (cp < tail) + source_files.push_back(filename_strings.make(cp)); + } + + fclose(fd); +} + extern Design* elaborate(list root); #if defined(HAVE_TIMES) @@ -828,17 +896,20 @@ int main(int argc, char*argv[]) // Start the module list with the base system module. add_vpi_module("system"); add_vpi_module("vhdl_sys"); + add_vpi_module("vhdl_textio"); flags["-o"] = strdup("a.out"); min_typ_max_flag = TYP; min_typ_max_warn = 10; - while ((opt = getopt(argc, argv, "C:f:hN:P:p:Vv")) != EOF) switch (opt) { + while ((opt = getopt(argc, argv, "C:F:f:hN:P:p:Vv")) != EOF) switch (opt) { case 'C': read_iconfig_file(optarg); break; - + case 'F': + read_sources_file(optarg); + break; case 'f': parm_to_flagmap(optarg); break; @@ -891,6 +962,7 @@ int main(int argc, char*argv[]) "usage: ivl \n" "options:\n" "\t-C Config file from driver.\n" +"\t-F List of source files from driver.\n" "\t-h Print usage information, and exit.\n" "\t-N Dump the elaborated netlist to .\n" "\t-P Write the parsed input to .\n" @@ -906,11 +978,19 @@ int main(int argc, char*argv[]) return 0; } - if (optind == argc) { + int arg = optind; + while (arg < argc) { + perm_string path = filename_strings.make(argv[arg++]); + source_files.push_back(path); + } + + if (source_files.empty()) { cerr << "No input files." << endl; return 1; } + separate_compilation = source_files.size() > 1; + if( depfile_name ) { depend_file = fopen(depfile_name, "a"); if(! depend_file) { @@ -922,16 +1002,22 @@ int main(int argc, char*argv[]) switch (generation_flag) { case GN_VER2012: lexor_keyword_mask |= GN_KEYWORDS_1800_2012; + // fallthrough case GN_VER2009: lexor_keyword_mask |= GN_KEYWORDS_1800_2009; + // fallthrough case GN_VER2005_SV: lexor_keyword_mask |= GN_KEYWORDS_1800_2005; + // fallthrough case GN_VER2005: lexor_keyword_mask |= GN_KEYWORDS_1364_2005; + // fallthrough case GN_VER2001: lexor_keyword_mask |= GN_KEYWORDS_1364_2001_CONFIG; + // fallthrough case GN_VER2001_NOCONFIG: lexor_keyword_mask |= GN_KEYWORDS_1364_2001; + // fallthrough case GN_VER1995: lexor_keyword_mask |= GN_KEYWORDS_1364_1995; } @@ -1005,8 +1091,10 @@ int main(int argc, char*argv[]) if (flag_tmp) disable_concatz_generation = strcmp(flag_tmp,"true")==0; /* Parse the input. Make the pform. */ - pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); - int rc = pform_parse(argv[optind]); + int rc = 0; + for (unsigned idx = 0; idx < source_files.size(); idx += 1) { + rc += pform_parse(source_files[idx]); + } if (pf_path) { ofstream out (pf_path); @@ -1020,22 +1108,16 @@ int main(int argc, char*argv[]) ; cur != disciplines.end() ; ++ cur ) { pform_dump(out, (*cur).second); } - out << "PFORM DUMP $ROOT TASKS/FUNCTIONS:" << endl; - for (map::iterator cur = pform_tasks.begin() - ; cur != pform_tasks.end() ; ++ cur) { - pform_dump(out, cur->second); - } - out << "PFORM DUMP $ROOT CLASSES:" << endl; - for (map::iterator cur = pform_classes.begin() - ; cur != pform_classes.end() ; ++ cur) { - pform_dump(out, cur->second); + out << "PFORM DUMP COMPILATION UNITS:" << endl; + for (vector::iterator pac = pform_units.begin() + ; pac != pform_units.end() ; ++ pac) { + pform_dump(out, *pac); } out << "PFORM DUMP PACKAGES:" << endl; for (map::iterator pac = pform_packages.begin() ; pac != pform_packages.end() ; ++ pac) { pform_dump(out, pac->second); } - out << "PFORM DUMP MODULES:" << endl; for (map::iterator mod = pform_modules.begin() ; mod != pform_modules.end() ; ++ mod ) { @@ -1107,7 +1189,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; } @@ -1153,12 +1235,6 @@ int main(int argc, char*argv[]) (*idx).second = 0; } - for(map::iterator it = pform_typedefs.begin() - ; it != pform_typedefs.end() ; ++it) { - delete (*it).second; - (*it).second = 0; - } - if (verbose_flag) { if (times_flag) { times(cycles+2); diff --git a/mingw.txt b/mingw.txt index b5dc25307..5f65922a6 100644 --- a/mingw.txt +++ b/mingw.txt @@ -1,282 +1,4 @@ -Please NOTE: +Please see the Icarus Verilog Wiki for instruction on building and installing +Icarus Verilog as a native Windows application using the MinGW tools: -These directions are slightly outdated and need to be updated. -The plan is to rewrite all this using the latest MinGW at -some time in the not too distant future (CR Aug. 2009) - - -MINGW PORT OF ICARUS VERILOG - - Copyright 2006 Stephen Williams - - -Icarus Verilog source can be compiled with the mingw C/C++ compilers -to get a Windows binary that does not require the POSIX compatibility -cruft of the Cygwin.dll library. The resulting program can be run with -or without Cygwin, so this is the preferred Windows distribution form. -The configure scripts automatically detect that the compilers in use -are the mingw compilers and will configure the Makefiles appropriately. - -The mingw patch doesn't contain tools beyond the compiler, but there -is the "msys" package that the makers of Mingw publish that has enough -extra tools to get most everything else working. There are a few extra -packages needed beyond mingw and msys, and the following instructions -explain how to get them and install them. - -* Some Preliminary Comments -- PLEASE READ ME -- - -The Windows port of Icarus Verilog is the most difficult of all the -ports. The Windows system off the shelf is completely stripped, devoid -of any support for software development. Everything needed to compile -Icarus Verilog must be collected from various sources and stitched -together by hand. Normal human beings with a basic understanding of -software development can do this, but some patience (and access to the -Internet) is required. You may choose to print these instructions. FOR -BEST RESULTS, FOLLOW THESE INSTRUCTIONS CAREFULLY. - -NOTE that if you have Cygwin installed, it is best to not use a cygwin -window to do the build, as the Cygwin tools will intermix with the -mingw tools such that it is hard to be sure you are using the right -compiler. Thus, it is recommended that these steps be *not* done in a -Cygwin window. Use an MSYS window instead, and be careful that your -msys/mingw tools are not masked by paths that point to Cygwin binaries. - -I have no plans to intentionally support MSVC++ compilation. Don't ask. - -* Summary of Prerequisites - -This is a brief list of prerequisite packages, along with the URL -where each can be found. In most cases, the specific version is not -critical, but these are the versions I use. - - msys-1.0 - msysDTK-1.0 - Mingw32-5.x - ... including the sub-packages binutils, gcc-core and gcc-g++ - if you are given the option. - readline-5.0-1 - bzip2-1.0.3 - zlib-1.2.3 - gperf-3.0.1 - bison-2.1 - flex-2.5.4a - -The above table lists the packages required. It is convenient to -install them in the above order. Many of these packages are also -collected into the directory: - - - -Incidentally, besides Mingw32, none of these packages are needed after -installation of Icarus Verilog is complete. These are only needed to -build the compiler. The Mingw32 package can be used to compile VPI -modules if you choose. - -* Install MSYS and msysDTK - -The msys package is available from the mingw download site. This is -not the compiler but a collection of *nix tools ported to Windows and -wrapped in a convenient installer. The msys package is all the various -basic tools (shells, file utils, etc) and the msysDTK is extra -developer tools other than the compiler. - -Download the msys-1.x.x.exe and msysdtc-1.x.x.exe binaries. These are -self-installing packages. Install msys first, and then msysDTC. Most -likely, you want to install them in c:/msys. (The msysDTK is installed -in the same location, as it is an add-on.) - -This install should be easy and reliable. - -The installation will leave an "msys" icon on your desktop and in the -mingw sub-menu of your Start menu. This icon brings up a shell window -(a command line) that has paths all set up for executing msys and -mingw commands. This is what you will want to use while executing -commands below. - -* Install Mingw32 - -The obvious step 2, then, is install the mingw compilers. These can be -found at the web page . The Mingw-5.x.x package -is a convenient remote installer. Download this program and run -it. The installer will ask which components you want to install. You -need only the base C compiler and the C++ compiler. (You may install -other languages if you wish.) - -When I install Mingw32 (using the installer) I typically set a -destination directory of d:\mingw or the like. You will be using -that path later. - - NOTES: - If you intend to compile VPI modules for Icarus Verilog, you - need Mingw32, even if you are using a precompiled binary. VPI - modules only require Mingw32, and none of the other libraries. - -Finally, as part of installing the mingw32 compilers, remember to add -the mingw/bin directory to your path. You will need that to be able to -find the compilers later. - -* Install GnuWin32 Packages - -The GnuWin32 project is a collections of open source programs and -libraries ported to Windows. These also work well with the Mingw -compiler, and in fact Icarus Verilog uses a few pieces from this -collection. - -You will need these gnuwin32 packages to compile Icarus Verilog: - - - bzip2-1.0.3.exe - zlib-1.2.3.exe - gperf-3.0.1.exe - bison-2.1.exe - flex-2.5.4a.exe - readline-5.0-1.exe - -I suggest creating a common directory for holding all your gnuwin32 -packages. I use C:\gnuwin32. The download page at the gnuwin32 site -has a "setup" link for each of these packages. Click the setup to -download the installer for each of the desired programs, then execute -the downloaded .exe files to invoke the installer. Install into the -c:\gunwin32 directory. - - NOTES: - You need the binaries and the developer files, but you do not - need the source to these packages. The installer gives you the - choice. - -After you are done installing the gnuwin32 tools, you should add the -c:\gnuwin32\bin directory (assuming you installed in c:\gnuwin32) to -your Windows path. The msys shell will pick up your Windows path. - -* Unpack Icarus Verilog source - -Unpack the compressed tar file (.tar.gz) of the source with a command -like this: - - $ gunzip -d verilog-xxxxxxxx.tar.gz | tar xvf - - -This will create a directory "verilog-xxxxxxxx" that contains all the -source for Icarus Verilog. Descend into that directory, as that is -where we will work from now on. - - $ cd verilog-xxxxxxxx - - NOTES: - The exact name of the file will vary according to the - snapshot. The 20030303 name is only an example. - - Unpack the source into a directory that has no spaces. The - makefiles included in the source get confused by white space in - directory names. - -* Preconfigure Icarus Verilog (Not normally needed) - -Under certain cases, you may need to "preconfigure" the Icarus Verilog -source tree. You should only need to do this if you are getting the -Icarus Verilog source tree from git, or you are using an existing -source tree that you've patched to cause configure.in files to change. - - NOTE: If you are building from a fresh, bundled source tree that - you downloaded from an FTP site, then SKIP THIS STEP. Go on to - the "Configure Icarus Verilog" step below. - -First, remove any autom4te.cache directories that may exist in your -source tree. These can make a mess of autoconf runs. Then, generate -configure scripts with this command: - - $ sh autoconf.sh - -This script will run the "autoconf" command (part of the msysDTK) to -generate all the necessary "configure" scripts. This will take a few -minutes. This should go smoothly. - -* Configure Icarus Verilog - -Now we are all set to configure and compile Icarus Verilog. Choose a -destination path where you will want to install the binaries. I chose -on my system the directory "D:\iverilog". This choice is not -permanent, so don't get too much angst over it. Just choose a name -without white space. - -Now, configure the source to make the makefiles and configuration -details. Run these commands: - - $ CPPFLAGS="-Ic:/gnuwin32/include - $ LDFLAGS="-Lc:/gnuwin32/lib - $ export CPPFLAGS LDFLAGS - $ ./configure --prefix=c:/iverilog - - NOTES: - The CPPFLAGS and LDFLAGS variables tell configure where - the gnuwin32 packages are. The configure program will - write these values into the Makefiles, so you only need to - keep these variables long enough for the configure program - to work. - - Your PATH variable was set in the previous step. - - Use forward slashes as directory characters. All the various - tools prefer the forward slash. - -Substitute your chosen directory for the prefix. This will cause the -makefiles to build and the source code to configure. The configure -program will detect that this is a mingw environment and set things up -to build properly. - -(For a prefix, use the drive letter notation; the mingw compiled parts -require it, and the Cygwin tools should be able to handle it. You may -need to check or upgrade your Cygwin installation if this causes -problems for you.) - - -* Compile Icarus Verilog - -This, believe it or not, should be the easy part: - - $ make - -It could take a while. Now is a good time to go get some coffee or -take a tea break. - - -* Install Icarus Verilog - -If the compile ran OK, then next you install Icarus Verilog in the -directory you have chosen. When you are ready, install like this: - - $ make install - -This is part of what the configure program did for you. The Makefiles -now know to put the files under the D:\iverilog directory (or whatever -directory you chose) and away you go. - -You may find that you need to put some of the prerequisite DLLs into -the d:\iverilog\bin directory. These include: - - c:\mingw\bin\mingw10.dll - c:\gnuwin32\bin\readline.dll - c:\gnuwin32\bin\history.dll - c:\gnuwin32\bin\bzip2.dll - c:\gnuwin32\bin\zlib.dll - -If you already have these in your Windows path (i.e. your system32 -directory) then you do not need to copy them into the iverilog -directory. However, prepackaged Icarus Verilog binaries include these -files. - -* Running Icarus Verilog - -Finally, put the C:\iverilog\bin directory in your Windows path, and -you should be able to run the iverilog and vvp commands to your -heart's content. - -Currently, the iverilog.exe uses the path to itself to locate the -libraries and modules associated with itself. In other words, if you -execute the C:\iverilog\bin\iverilog.exe program, it will locate its -subparts in the C:\iverilog directory and subdirectories below -that. This means you can move the Icarus Verilog installation by -simply moving the root directory and all its contents. - -The vvp.pdf and iverilog.pdf files document the main commands. View -these with Acrobat reader, or any other viewer capable of displaying -PDF format files. + http://iverilog.wikia.com/wiki/Installation_Guide#Compiling_on_MS_Windows_.28MinGW.29 diff --git a/net_assign.cc b/net_assign.cc index 1467ee961..29005f885 100644 --- a/net_assign.cc +++ b/net_assign.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2015 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 @@ -42,7 +42,10 @@ unsigned count_lval_width(const NetAssign_*idx) NetAssign_::NetAssign_(NetAssign_*n) : nest_(n), sig_(0), word_(0), base_(0), sel_type_(IVL_SEL_OTHER) { + lwid_ = 0; more = 0; + signed_ = false; + turn_sig_to_wire_on_release_ = false; } NetAssign_::NetAssign_(NetNet*s) @@ -51,6 +54,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_design.cc b/net_design.cc index dfaf51d56..b071469e2 100644 --- a/net_design.cc +++ b/net_design.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 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 @@ -101,11 +101,11 @@ uint64_t Design::scale_to_precision(uint64_t val, return val; } -NetScope* Design::make_root_scope(perm_string root, bool program_block, - bool is_interface) +NetScope* Design::make_root_scope(perm_string root, NetScope*unit_scope, + bool program_block, bool is_interface) { NetScope *root_scope_; - root_scope_ = new NetScope(0, hname_t(root), NetScope::MODULE, + root_scope_ = new NetScope(0, hname_t(root), NetScope::MODULE, unit_scope, false, program_block, is_interface); /* This relies on the fact that the basename return value is permallocated. */ @@ -125,31 +125,18 @@ list Design::find_root_scopes() const return root_scopes_; } -NetScope* Design::make_package_scope(perm_string name) +NetScope* Design::make_package_scope(perm_string name, NetScope*unit_scope, + bool is_unit) { NetScope*scope; - scope = new NetScope(0, hname_t(name), NetScope::PACKAGE, false, false); + scope = new NetScope(0, hname_t(name), NetScope::PACKAGE, unit_scope, + false, false, false, is_unit); scope->set_module_name(scope->basename()); packages_[name] = scope; return scope; } -void Design::add_class(netclass_t*cl, PClass*pclass) -{ - Definitions::add_class(cl); - class_to_pclass_[cl] = pclass; -} - -netclass_t* Design::find_class(perm_string name) const -{ - map::const_iterator cur = classes_.find(name); - if (cur != classes_.end()) - return cur->second; - - return 0; -} - NetScope* Design::find_package(perm_string name) const { map::const_iterator cur = packages_.find(name); @@ -170,17 +157,6 @@ list Design::find_package_scopes() const return res; } -list Design::find_roottask_scopes() const -{ - listres; - for (map::const_iterator cur = root_tasks_.begin() - ; cur != root_tasks_.end() ; ++ cur) { - res.push_back (cur->first); - } - - return res; -} - /* * This method locates a scope in the design, given its rooted * hierarchical name. Each component of the key is used to scan one @@ -211,25 +187,6 @@ NetScope* Design::find_scope(const std::list&path) const } } - for (map::const_iterator root = root_tasks_.begin() - ; root != root_tasks_.end() ; ++ root) { - - NetScope*cur = root->first; - if (path.front() != cur->fullname()) - continue; - - std::list tmp = path; - tmp.pop_front(); - - while (cur) { - if (tmp.empty()) return cur; - - cur = cur->child( tmp.front() ); - - tmp.pop_front(); - } - } - return 0; } @@ -253,6 +210,49 @@ NetScope* Design::find_scope(const hname_t&path) const return 0; } +static bool is_design_unit(NetScope*scope) +{ + return (scope->type() == NetScope::MODULE && !scope->nested_module()) + || (scope->type() == NetScope::PACKAGE); +} + +static bool is_subroutine(NetScope::TYPE type) +{ + return type == NetScope::TASK || type == NetScope::FUNC; +} + +/* + * This method locates a scope within another scope, given its relative + * hierarchical name. Each component of the key is used to scan one + * more step down the tree until the name runs out or the search + * fails. + */ +NetScope* Design::find_scope_(NetScope*scope, const std::list&path, + NetScope::TYPE type) const +{ + std::list tmp = path; + + do { + hname_t key = tmp.front(); + /* If we are looking for a module or we are not + * looking at the last path component check for + * a name match (second line). */ + if (scope->type() == NetScope::MODULE + && (type == NetScope::MODULE || tmp.size() > 1) + && scope->module_name()==key.peek_name()) { + + /* Up references may match module name */ + + } else { + scope = scope->child( key ); + if (scope == 0) break; + } + tmp.pop_front(); + } while (! tmp.empty()); + + return scope; +} + /* * This is a relative lookup of a scope by name. The starting point is * the scope parameter within which I start looking for the scope. If @@ -266,36 +266,62 @@ NetScope* Design::find_scope(NetScope*scope, const std::list&path, if (path.empty()) return scope; - for ( ; scope ; scope = scope->parent()) { + // Record the compilation unit scope for use later. + NetScope*unit_scope = scope->unit(); - std::list tmp = path; + // First search upwards through the hierarchy. + while (scope) { + NetScope*found_scope = find_scope_(scope, path, type); + if (found_scope) + return found_scope; - NetScope*cur = scope; - do { - hname_t key = tmp.front(); - /* If we are looking for a module or we are not - * looking at the last path component check for - * a name match (second line). */ - if (cur->type() == NetScope::MODULE - && (type == NetScope::MODULE || tmp.size() > 1) - && cur->module_name()==key.peek_name()) { + // Avoid searching the unit scope twice. + if (scope == unit_scope) + unit_scope = 0; - /* Up references may match module name */ + // Special case - see IEEE 1800-2012 section 23.8.1. + if (unit_scope && is_design_unit(scope) && is_subroutine(type)) { + found_scope = find_scope_(unit_scope, path, type); + if (found_scope) + return found_scope; - } else { - cur = cur->child( key ); - if (cur == 0) break; - } - tmp.pop_front(); - } while (! tmp.empty()); + unit_scope = 0; + } - if (cur) return cur; + scope = scope->parent(); + } + + // If we haven't already done so, search the compilation unit scope. + if (unit_scope) { + NetScope*found_scope = find_scope_(unit_scope, path, type); + if (found_scope) + return found_scope; } // Last chance. Look for the name starting at the root. return find_scope(path); } +/* + * This method locates a scope within another scope, given its relative + * hierarchical name. Each component of the key is used to scan one + * more step down the tree until the name runs out or the search + * fails. + */ +NetScope* Design::find_scope_(NetScope*scope, const hname_t&path, + NetScope::TYPE type) const +{ + /* If we are looking for a module or we are not + * looking at the last path component check for + * a name match (second line). */ + if ((scope->type() == NetScope::MODULE) && (type == NetScope::MODULE) + && (scope->module_name() == path.peek_name())) { + /* Up references may match module name */ + return scope; + } + return scope->child( path ); +} + /* * This is a relative lookup of a scope by name. The starting point is * the scope parameter within which I start looking for the scope. If @@ -307,24 +333,36 @@ NetScope* Design::find_scope(NetScope*scope, const hname_t&path, { assert(scope); - for ( ; scope ; scope = scope->parent()) { + // Record the compilation unit scope for use later. + NetScope*unit_scope = scope->unit(); - NetScope*cur = scope; + // First search upwards through the hierarchy. + while (scope) { + NetScope*found_scope = find_scope_(scope, path, type); + if (found_scope) + return found_scope; - /* If we are looking for a module or we are not - * looking at the last path component check for - * a name match (second line). */ - if (cur->type() == NetScope::MODULE - && (type == NetScope::MODULE) - && cur->module_name()==path.peek_name()) { + // Avoid searching the unit scope twice. + if (scope == unit_scope) + unit_scope = 0; - /* Up references may match module name */ + // Special case - see IEEE 1800-2012 section 23.8.1. + if (unit_scope && is_design_unit(scope) && is_subroutine(type)) { + found_scope = find_scope_(unit_scope, path, type); + if (found_scope) + return found_scope; - } else { - cur = cur->child( path ); + unit_scope = 0; } - if (cur) return cur; + scope = scope->parent(); + } + + // If we haven't already done so, search the compilation unit scope. + if (unit_scope) { + NetScope*found_scope = find_scope_(unit_scope, path, type); + if (found_scope) + return found_scope; } // Last chance. Look for the name starting at the root. @@ -847,6 +885,7 @@ NetFuncDef* Design::find_function(NetScope*scope, const pform_name_t&name) // the function's signals have been elaborated. If this is // the case, elaborate them now. if (func->elab_stage() < 2) { + func->need_const_func(true); const PFunction*pfunc = func->func_pform(); assert(pfunc); pfunc->elaborate_sig(this, func); @@ -866,11 +905,6 @@ NetScope* Design::find_task(NetScope*scope, const pform_name_t&name) return 0; } -void Design::add_root_task(NetScope*tscope, PTaskFunc*tf) -{ - root_tasks_[tscope] = tf; -} - void Design::add_node(NetNode*net) { assert(net->design_ == 0); @@ -889,8 +923,8 @@ void Design::add_node(NetNode*net) void Design::del_node(NetNode*net) { - assert(net->design_ == this); assert(net != 0); + assert(net->design_ == this); /* Interact with the Design::functor method by manipulating the cur and nxt pointers that it is using. */ diff --git a/net_event.cc b/net_event.cc index 9b67b5b99..2b6eb8d35 100644 --- a/net_event.cc +++ b/net_event.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 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 @@ -28,6 +28,7 @@ NetEvent::NetEvent(perm_string n) : name_(n) { + local_flag_ = false; scope_ = 0; snext_ = 0; probes_ = 0; @@ -345,7 +346,7 @@ void NetEvProbe::find_similar_probes(list&plist) } NetEvWait::NetEvWait(NetProc*pr) -: statement_(pr) +: statement_(pr), has_t0_trigger_(false) { } @@ -369,6 +370,7 @@ NetEvWait::~NetEvWait() tmp->next = tmp->next->next; delete tmp; } + delete tgt; } events_.clear(); } @@ -441,3 +443,8 @@ NetProc* NetEvWait::statement() { return statement_; } + +const NetProc* NetEvWait::statement() const +{ + return statement_; +} diff --git a/net_func_eval.cc b/net_func_eval.cc index ecbe55780..f310af488 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-2018 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 @@ -23,6 +23,10 @@ # include # include "ivl_assert.h" +#if __cplusplus < 201103L +#define unique_ptr auto_ptr +#endif + using namespace std; /* @@ -88,6 +92,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 +217,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 +369,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 +455,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 +513,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 @@ -874,7 +1022,7 @@ NetExpr* NetESignal::evaluate_function(const LineInfo&loc, NetExpr* NetETernary::evaluate_function(const LineInfo&loc, map&context_map) const { - auto_ptr cval (cond_->evaluate_function(loc, context_map)); + unique_ptr cval (cond_->evaluate_function(loc, context_map)); switch (const_logical(cval.get())) { diff --git a/net_link.cc b/net_link.cc index 15c67e046..3b0a79d45 100644 --- a/net_link.cc +++ b/net_link.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2013 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 @@ -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()) { @@ -467,6 +481,17 @@ NetNet* Nexus::pick_any_net() return 0; } +NetNode* Nexus::pick_any_node() +{ + for (Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { + NetNode*node = dynamic_cast(cur->get_obj()); + if (node != 0) + return node; + } + + return 0; +} + const char* Nexus::name() const { if (name_) diff --git a/net_nex_input.cc b/net_nex_input.cc index aff41f281..38e7e148a 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-2017 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 @@ -27,29 +27,29 @@ # include "netlist.h" # include "netmisc.h" -NexusSet* NetExpr::nex_input(bool) +NexusSet* NetExpr::nex_input(bool, bool) const { cerr << get_fileline() << ": internal error: nex_input not implemented: " << *this << endl; - return 0; + return new NexusSet; } -NexusSet* NetProc::nex_input(bool) +NexusSet* NetProc::nex_input(bool, bool) const { cerr << get_fileline() << ": internal error: NetProc::nex_input not implemented" << endl; - return 0; + return new NexusSet; } -NexusSet* NetEArrayPattern::nex_input(bool rem_out) +NexusSet* NetEArrayPattern::nex_input(bool rem_out, bool search_funcs) const { NexusSet*result = new NexusSet; for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { if (items_[idx]==0) continue; - NexusSet*tmp = items_[idx]->nex_input(rem_out); + NexusSet*tmp = items_[idx]->nex_input(rem_out, search_funcs); if (tmp == 0) continue; result->add(*tmp); @@ -58,32 +58,32 @@ NexusSet* NetEArrayPattern::nex_input(bool rem_out) return result; } -NexusSet* NetEBinary::nex_input(bool rem_out) +NexusSet* NetEBinary::nex_input(bool rem_out, bool search_funcs) const { - NexusSet*result = left_->nex_input(rem_out); - NexusSet*tmp = right_->nex_input(rem_out); + NexusSet*result = left_->nex_input(rem_out, search_funcs); + NexusSet*tmp = right_->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; return result; } -NexusSet* NetEConcat::nex_input(bool rem_out) +NexusSet* NetEConcat::nex_input(bool rem_out, bool search_funcs) const { - if (parms_[0] == NULL) return NULL; - NexusSet*result = parms_[0]->nex_input(rem_out); + if (parms_[0] == NULL) return new NexusSet; + NexusSet*result = parms_[0]->nex_input(rem_out, search_funcs); for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { if (parms_[idx] == NULL) { delete result; - return NULL; + return new NexusSet; } - NexusSet*tmp = parms_[idx]->nex_input(rem_out); + NexusSet*tmp = parms_[idx]->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; } return result; } -NexusSet* NetEAccess::nex_input(bool) +NexusSet* NetEAccess::nex_input(bool, bool) const { return new NexusSet; } @@ -91,59 +91,55 @@ NexusSet* NetEAccess::nex_input(bool) /* * A constant has not inputs, so always return an empty set. */ -NexusSet* NetEConst::nex_input(bool) +NexusSet* NetEConst::nex_input(bool, bool) const { return new NexusSet; } -NexusSet* NetECReal::nex_input(bool) +NexusSet* NetECReal::nex_input(bool, bool) const { return new NexusSet; } -NexusSet* NetEEvent::nex_input(bool) +NexusSet* NetEEvent::nex_input(bool, bool) const { return new NexusSet; } -NexusSet* NetELast::nex_input(bool) +NexusSet* NetELast::nex_input(bool, bool) const { return new NexusSet; } -NexusSet* NetENetenum::nex_input(bool) +NexusSet* NetENetenum::nex_input(bool, bool) const { return new NexusSet; } -NexusSet* NetENew::nex_input(bool) +NexusSet* NetENew::nex_input(bool, bool) const { return new NexusSet; } -NexusSet* NetENull::nex_input(bool) +NexusSet* NetENull::nex_input(bool, bool) const { return new NexusSet; } -NexusSet* NetEProperty::nex_input(bool) +NexusSet* NetEProperty::nex_input(bool, bool) const { return new NexusSet; } -NexusSet* NetEScope::nex_input(bool) +NexusSet* NetEScope::nex_input(bool, bool) const { return new NexusSet; } -NexusSet* NetESelect::nex_input(bool rem_out) +NexusSet* NetESelect::nex_input(bool rem_out, bool search_funcs) const { - NexusSet*result = base_? base_->nex_input(rem_out) : new NexusSet(); - NexusSet*tmp = expr_->nex_input(rem_out); - if (tmp == NULL) { - delete result; - return NULL; - } + NexusSet*result = base_? base_->nex_input(rem_out, search_funcs) : new NexusSet(); + NexusSet*tmp = expr_->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; /* See the comment for NetESignal below. */ @@ -157,30 +153,29 @@ NexusSet* NetESelect::nex_input(bool rem_out) /* * The $fread, etc. system functions can have NULL arguments. */ -NexusSet* NetESFunc::nex_input(bool rem_out) +NexusSet* NetESFunc::nex_input(bool rem_out, bool search_funcs) const { - if (parms_.empty()) - return new NexusSet; + NexusSet*result = new NexusSet; - NexusSet*result; - if (parms_[0]) result = parms_[0]->nex_input(rem_out); - else result = new NexusSet; - for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { + if (parms_.empty()) return result; + + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { if (parms_[idx]) { - NexusSet*tmp = parms_[idx]->nex_input(rem_out); + NexusSet*tmp = parms_[idx]->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; } } + return result; } -NexusSet* NetEShallowCopy::nex_input(bool) +NexusSet* NetEShallowCopy::nex_input(bool, bool) const { return new NexusSet; } -NexusSet* NetESignal::nex_input(bool rem_out) +NexusSet* NetESignal::nex_input(bool rem_out, bool search_funcs) const { /* * This is not what I would expect for the various selects (bit, @@ -194,7 +189,7 @@ NexusSet* NetESignal::nex_input(bool rem_out) /* If we have an array index add it to the sensitivity list. */ if (word_) { NexusSet*tmp; - tmp = word_->nex_input(rem_out); + tmp = word_->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; if (warn_sens_entire_arr) { @@ -209,27 +204,44 @@ NexusSet* NetESignal::nex_input(bool rem_out) return result; } -NexusSet* NetETernary::nex_input(bool rem_out) +NexusSet* NetETernary::nex_input(bool rem_out, bool search_funcs) const { NexusSet*tmp; - NexusSet*result = cond_->nex_input(rem_out); + NexusSet*result = cond_->nex_input(rem_out, search_funcs); - tmp = true_val_->nex_input(rem_out); + tmp = true_val_->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; - tmp = false_val_->nex_input(rem_out); + tmp = false_val_->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; return result; } -NexusSet* NetEUFunc::nex_input(bool rem_out) +NexusSet* NetEUFunc::nex_input(bool rem_out, bool search_funcs) const { NexusSet*result = new NexusSet; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { - NexusSet*tmp = parms_[idx]->nex_input(rem_out); + NexusSet*tmp = parms_[idx]->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + + if (search_funcs) { + NetFuncDef*func = func_->func_def(); + NexusSet*tmp = func->proc()->nex_input(rem_out, search_funcs); + // Remove the function inputs + NexusSet*in = new NexusSet; + for (unsigned idx = 0 ; idx < func->port_count() ; idx += 1) { + NetNet*net = func->port(idx); + assert(net->pin_count() == 1); + in->add(net->pin(0).nexus(), 0, net->vector_width()); + } + tmp->rem(*in); + delete in; + result->add(*tmp); delete tmp; } @@ -237,21 +249,28 @@ NexusSet* NetEUFunc::nex_input(bool rem_out) return result; } -NexusSet* NetEUnary::nex_input(bool rem_out) +NexusSet* NetEUnary::nex_input(bool rem_out, bool search_funcs) const { - return expr_->nex_input(rem_out); + return expr_->nex_input(rem_out, search_funcs); } -NexusSet* NetAssign_::nex_input(bool rem_out) +NexusSet* NetAlloc::nex_input(bool, bool) const { + return new NexusSet; +} + +NexusSet* NetAssign_::nex_input(bool rem_out, bool search_funcs) const +{ + assert(! nest_); NexusSet*result = new NexusSet; + if (word_) { - NexusSet*tmp = word_->nex_input(rem_out); + NexusSet*tmp = word_->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; } if (base_) { - NexusSet*tmp = base_->nex_input(rem_out); + NexusSet*tmp = base_->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; } @@ -259,15 +278,21 @@ NexusSet* NetAssign_::nex_input(bool rem_out) return result; } -NexusSet* NetAssignBase::nex_input(bool rem_out) +NexusSet* NetAssignBase::nex_input(bool rem_out, bool search_funcs) const { - NexusSet*result = rval_->nex_input(rem_out); + NexusSet*result = new NexusSet; + // For the deassign and release statements there is no R-value. + if (rval_) { + NexusSet*tmp = rval_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } /* It is possible that the lval_ can have nex_input values. In particular, index expressions are statement inputs as well, so should be addressed here. */ for (NetAssign_*cur = lval_ ; cur ; cur = cur->more) { - NexusSet*tmp = cur->nex_input(rem_out); + NexusSet*tmp = cur->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; } @@ -292,16 +317,15 @@ NexusSet* NetAssignBase::nex_input(bool rem_out) * In this example, "t" should not be in the input set because it is * used by the sequence as a temporary value. */ -NexusSet* NetBlock::nex_input(bool rem_out) +NexusSet* NetBlock::nex_input(bool rem_out, bool search_funcs) const { - if (last_ == 0) - return new NexusSet; + if (last_ == 0) return new NexusSet; - if (type_ != SEQU) { + if (! search_funcs && (type_ != SEQU)) { cerr << get_fileline() << ": internal error: Sorry, " << "I don't know how to synthesize fork/join blocks." << endl; - return 0; + return new NexusSet; } NetProc*cur = last_->next_; @@ -312,10 +336,10 @@ NexusSet* NetBlock::nex_input(bool rem_out) do { /* Get the inputs for the current statement. */ - NexusSet*tmp = cur->nex_input(rem_out); + NexusSet*tmp = cur->nex_input(rem_out, search_funcs); /* Add the current input set to the accumulated input set. */ - if (tmp != 0) result->add(*tmp); + result->add(*tmp); delete tmp; /* Add the current outputs to the accumulated output set if @@ -339,11 +363,9 @@ NexusSet* NetBlock::nex_input(bool rem_out) * the inputs to all the guards, and the inputs to all the guarded * statements. */ -NexusSet* NetCase::nex_input(bool rem_out) +NexusSet* NetCase::nex_input(bool rem_out, bool search_funcs) const { - NexusSet*result = expr_->nex_input(rem_out); - if (result == 0) - return 0; + NexusSet*result = expr_->nex_input(rem_out, search_funcs); for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { @@ -351,8 +373,7 @@ NexusSet* NetCase::nex_input(bool rem_out) if (items_[idx].statement == 0) continue; - NexusSet*tmp = items_[idx].statement->nex_input(rem_out); - assert(tmp); + NexusSet*tmp = items_[idx].statement->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; @@ -360,8 +381,7 @@ NexusSet* NetCase::nex_input(bool rem_out) case is special and is identified by a null guard. The default guard obviously has no input. */ if (items_[idx].guard) { - tmp = items_[idx].guard->nex_input(rem_out); - assert(tmp); + tmp = items_[idx].guard->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; } @@ -370,24 +390,18 @@ NexusSet* NetCase::nex_input(bool rem_out) return result; } -NexusSet* NetCAssign::nex_input(bool) +NexusSet* NetCondit::nex_input(bool rem_out, bool search_funcs) const { - cerr << get_fileline() << ": internal warning: NetCAssign::nex_input()" - << " not implemented." << endl; - return new NexusSet; -} + NexusSet*result = expr_->nex_input(rem_out, search_funcs); -NexusSet* NetCondit::nex_input(bool rem_out) -{ - NexusSet*result = expr_->nex_input(rem_out); if (if_ != 0) { - NexusSet*tmp = if_->nex_input(rem_out); + NexusSet*tmp = if_->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; } if (else_ != 0) { - NexusSet*tmp = else_->nex_input(rem_out); + NexusSet*tmp = else_->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; } @@ -395,47 +409,100 @@ NexusSet* NetCondit::nex_input(bool rem_out) return result; } -NexusSet* NetDoWhile::nex_input(bool rem_out) +NexusSet* NetDisable::nex_input(bool, bool) const { - NexusSet*result = proc_->nex_input(rem_out); - NexusSet*tmp = cond_->nex_input(rem_out); - result->add(*tmp); - delete tmp; - return result; -} - -NexusSet* NetForce::nex_input(bool) -{ - cerr << get_fileline() << ": internal warning: NetForce::nex_input()" - << " not implemented." << endl; return new NexusSet; } -NexusSet* NetForLoop::nex_input(bool rem_out) +NexusSet* NetDoWhile::nex_input(bool rem_out, bool search_funcs) const { - NexusSet*result = init_expr_->nex_input(rem_out); + NexusSet*result = cond_->nex_input(rem_out, search_funcs); - NexusSet*tmp = condition_->nex_input(rem_out); - result->add(*tmp); - delete tmp; - - tmp = statement_->nex_input(rem_out); - result->add(*tmp); - delete tmp; - - tmp = step_statement_->nex_input(rem_out); - result->add(*tmp); - delete tmp; + if (proc_) { + NexusSet*tmp = proc_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } return result; } -NexusSet* NetForever::nex_input(bool rem_out) +NexusSet* NetEvTrig::nex_input(bool, bool) const { - NexusSet*result = statement_->nex_input(rem_out); + return new NexusSet; +} + +NexusSet* NetEvWait::nex_input(bool rem_out, bool search_funcs) const +{ + NexusSet*result = new NexusSet; + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + return result; } +NexusSet* NetForever::nex_input(bool rem_out, bool search_funcs) const +{ + NexusSet*result = new NexusSet; + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + + return result; +} + +NexusSet* NetForLoop::nex_input(bool rem_out, bool search_funcs) const +{ + NexusSet*result = new NexusSet; + + if (init_expr_) { + NexusSet*tmp = init_expr_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + + if (condition_) { + NexusSet*tmp = condition_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + + if (step_statement_) { + NexusSet*tmp = step_statement_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + + if (gn_shared_loop_index_flag) { + NexusSet*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; +} + +NexusSet* NetFree::nex_input(bool, bool) const +{ + return new NexusSet; +} + /* * The NetPDelay statement is a statement of the form * @@ -445,36 +512,44 @@ NexusSet* NetForever::nex_input(bool rem_out) * include the input set of the because it does not affect the * result. The statement can be omitted. */ -NexusSet* NetPDelay::nex_input(bool rem_out) +NexusSet* NetPDelay::nex_input(bool rem_out, bool search_funcs) const { - if (statement_ == 0) return 0; - NexusSet*result = statement_->nex_input(rem_out); + NexusSet*result = new NexusSet; + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + return result; } -NexusSet* NetRepeat::nex_input(bool rem_out) +NexusSet* NetRepeat::nex_input(bool rem_out, bool search_funcs) const { - NexusSet*result = statement_->nex_input(rem_out); - NexusSet*tmp = expr_->nex_input(rem_out); - result->add(*tmp); - delete tmp; + NexusSet*result = expr_->nex_input(rem_out, search_funcs); + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + return result; } /* * The $display, etc. system tasks can have NULL arguments. */ -NexusSet* NetSTask::nex_input(bool rem_out) +NexusSet* NetSTask::nex_input(bool rem_out, bool search_funcs) const { - if (parms_.empty()) - return new NexusSet; + NexusSet*result = new NexusSet; - NexusSet*result; - if (parms_[0]) result = parms_[0]->nex_input(rem_out); - else result = new NexusSet; - for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { + if (parms_.empty()) return result; + + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { if (parms_[idx]) { - NexusSet*tmp = parms_[idx]->nex_input(rem_out); + NexusSet*tmp = parms_[idx]->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; } @@ -488,16 +563,20 @@ NexusSet* NetSTask::nex_input(bool rem_out) * parameters to consider, because the compiler already removed them * and converted them to blocking assignments. */ -NexusSet* NetUTask::nex_input(bool) +NexusSet* NetUTask::nex_input(bool, bool) const { return new NexusSet; } -NexusSet* NetWhile::nex_input(bool rem_out) +NexusSet* NetWhile::nex_input(bool rem_out, bool search_funcs) const { - NexusSet*result = proc_->nex_input(rem_out); - NexusSet*tmp = cond_->nex_input(rem_out); - result->add(*tmp); - delete tmp; + NexusSet*result = cond_->nex_input(rem_out, search_funcs); + + if (proc_) { + NexusSet*tmp = proc_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + return result; } diff --git a/net_nex_output.cc b/net_nex_output.cc index 194947955..9c1d32e10 100644 --- a/net_nex_output.cc +++ b/net_nex_output.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2017 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,8 +36,13 @@ void NetProc::nex_output(NexusSet&) << endl; } +void NetAlloc::nex_output(NexusSet&) +{ +} + void NetAssign_::nex_output(NexusSet&out) { + assert(! nest_); assert(sig_); unsigned use_word = 0; unsigned use_base = 0; @@ -89,8 +94,7 @@ void NetAssignBase::nex_output(NexusSet&out) void NetBlock::nex_output(NexusSet&out) { - if (last_ == 0) - return; + if (last_ == 0) return; NetProc*cur = last_; do { @@ -104,10 +108,8 @@ void NetCase::nex_output(NexusSet&out) for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { // Empty statements clearly have no output. - if (items_[idx].statement == 0) - continue; + if (items_[idx].statement == 0) continue; - assert(items_[idx].statement); items_[idx].statement->nex_output(out); } @@ -115,22 +117,31 @@ void NetCase::nex_output(NexusSet&out) void NetCondit::nex_output(NexusSet&out) { - if (if_ != 0) - if_->nex_output(out); - if (else_ != 0) - else_->nex_output(out); + if (if_) if_->nex_output(out); + if (else_) else_->nex_output(out); +} + +void NetDisable::nex_output(NexusSet&) +{ } void NetDoWhile::nex_output(NexusSet&out) { - if (proc_ != 0) - proc_->nex_output(out); + if (proc_) proc_->nex_output(out); +} + +void NetEvTrig::nex_output(NexusSet&) +{ } void NetEvWait::nex_output(NexusSet&out) { - assert(statement_); - statement_->nex_output(out); + if (statement_) statement_->nex_output(out); +} + +void NetForever::nex_output(NexusSet&out) +{ + if (statement_) statement_->nex_output(out); } void NetForLoop::nex_output(NexusSet&out) @@ -138,11 +149,20 @@ void NetForLoop::nex_output(NexusSet&out) if (statement_) statement_->nex_output(out); } +void NetFree::nex_output(NexusSet&) +{ +} + void NetPDelay::nex_output(NexusSet&out) { if (statement_) statement_->nex_output(out); } +void NetRepeat::nex_output(NexusSet&out) +{ + if (statement_) statement_->nex_output(out); +} + /* * For the purposes of synthesis, system task calls have no output at * all. This is OK because most system tasks are not synthesizable in @@ -163,6 +183,5 @@ void NetUTask::nex_output(NexusSet&) void NetWhile::nex_output(NexusSet&out) { - if (proc_ != 0) - proc_->nex_output(out); + if (proc_) proc_->nex_output(out); } diff --git a/net_proc.cc b/net_proc.cc index 2aedec7c2..68127445f 100644 --- a/net_proc.cc +++ b/net_proc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2015 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 @@ -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) @@ -188,6 +199,7 @@ NetForever::~NetForever() NetForLoop::NetForLoop(NetNet*ind, NetExpr*iexpr, NetExpr*cond, NetProc*sub, NetProc*step) : index_(ind), init_expr_(iexpr), condition_(cond), statement_(sub), step_statement_(step) { + as_block_ = NULL; } void NetForLoop::wrap_up() diff --git a/net_scope.cc b/net_scope.cc index 5ebd657d6..b9873786e 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -1,5 +1,6 @@ /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 2016 CERN Michele Castellana (michele.castellana@cern.ch) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -23,6 +24,7 @@ # include "netlist.h" # include "netclass.h" # include "netenum.h" +# include "netvector.h" # include # include # include @@ -110,10 +112,10 @@ void Definitions::add_class(netclass_t*net_class) * in question. */ -NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, bool nest, - bool program, bool interface) +NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, NetScope*in_unit, + bool nest, bool program, bool interface, bool compilation_unit) : type_(t), name_(n), nested_module_(nest), program_block_(program), - is_interface_(interface), up_(up) + is_interface_(interface), is_unit_(compilation_unit), unit_(in_unit), up_(up) { events_ = 0; lcounter_ = 0; @@ -122,6 +124,9 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, bool nest, calls_stask_ = false; in_final_ = false; + if (compilation_unit) + unit_ = this; + if (up) { assert(t!=CLASS); need_const_func_ = up->need_const_func_; @@ -131,6 +136,8 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, bool nest, time_from_timescale_ = up->time_from_timescale(); // Need to check for duplicate names? up_->children_[name_] = this; + if (unit_ == 0) + unit_ = up_->unit_; } else { need_const_func_ = false; is_const_func_ = false; @@ -139,6 +146,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; @@ -161,6 +169,8 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, bool nest, lineno_ = 0; def_lineno_ = 0; genvar_tmp_val = 0; + tie_hi_ = 0; + tie_lo_ = 0; } NetScope::~NetScope() @@ -205,6 +215,8 @@ const netenum_t*NetScope::find_enumeration_for_name(perm_string name) NetEConstEnum*tmp = cur_scope->enum_names_[name]; if (tmp) break; cur_scope = cur_scope->parent(); + if (cur_scope == 0) + cur_scope = unit_; } assert(cur_scope); @@ -264,19 +276,20 @@ bool NetScope::auto_name(const char*prefix, char pad, const char* suffix) assert(self != up_->children_.end()); assert(self->second == this); - char tmp[32]; - int pad_pos = strlen(prefix); - int max_pos = sizeof(tmp) - strlen(suffix) - 1; - strncpy(tmp, prefix, sizeof(tmp)); - tmp[31] = 0; + // This is to keep the pad attempts from being stuck in some + // sort of infinite loop. This should not be a practical + // limit, but an extreme one. + const size_t max_pad_attempts = 32 + strlen(prefix); + + string use_prefix = prefix; // Try a variety of potential new names. Make sure the new // name is not in the parent scope. Keep looking until we are // sure we have a unique name, or we run out of names to try. - while (pad_pos <= max_pos) { + while (use_prefix.size() <= max_pad_attempts) { // Try this name... - strcat(tmp + pad_pos, suffix); - hname_t new_name(lex_strings.make(tmp)); + string tmp = use_prefix + suffix; + hname_t new_name(lex_strings.make(tmp.c_str()), name_.peek_numbers()); if (!up_->child(new_name)) { // Ah, this name is unique. Rename myself, and // change my name in the parent scope. @@ -285,7 +298,9 @@ bool NetScope::auto_name(const char*prefix, char pad, const char* suffix) up_->children_[name_] = this; return true; } - tmp[pad_pos++] = pad; + + // Name collides, so try a different name. + use_prefix = use_prefix + pad; } return false; } @@ -296,16 +311,16 @@ bool NetScope::auto_name(const char*prefix, char pad, const char* suffix) */ bool NetScope::replace_parameter(perm_string key, PExpr*val, NetScope*scope) { - bool flag = false; + if (parameters.find(key) == parameters.end()) + return false; - if (parameters.find(key) != parameters.end()) { - param_expr_t&ref = parameters[key]; - ref.val_expr = val; - ref.val_scope = scope; - flag = true; - } + param_expr_t&ref = parameters[key]; + if (ref.local_flag) + return false; - return flag; + ref.val_expr = val; + ref.val_scope = scope; + return true; } bool NetScope::make_parameter_unannotatable(perm_string key) @@ -356,12 +371,7 @@ const NetExpr* NetScope::get_parameter(Design*des, msb = 0; lsb = 0; const NetExpr*tmp = enumeration_expr(key); - if (tmp) return tmp; - - tmp = des->enumeration_expr(key); - if (tmp) return tmp; - - return 0; + return tmp; } NetScope::param_ref_t NetScope::find_parameter(perm_string key) @@ -378,11 +388,6 @@ NetScope::param_ref_t NetScope::find_parameter(perm_string key) return idx; } -NetScope::TYPE NetScope::type() const -{ - return type_; -} - void NetScope::print_type(ostream&stream) const { switch (type_) { @@ -501,6 +506,7 @@ void NetScope::add_module_port_info( unsigned idx, perm_string name, PortType::E unsigned long width ) { assert(type_ == MODULE); + assert(ports_.size() > idx); PortInfo &info = ports_[idx]; info.name = name; info.type = ptype; @@ -648,15 +654,11 @@ netclass_t*NetScope::find_class(perm_string name) if (type_==CLASS && name_==hname_t(name)) return class_def_; - // Look for the class that directly within this scope. + // Look for the class directly within this scope. map::const_iterator cur = classes_.find(name); if (cur != classes_.end()) return cur->second; - // If this is a module scope, then look no further. - if (type_==MODULE) - return 0; - if (up_==0 && type_==CLASS) { assert(class_def_); @@ -664,12 +666,16 @@ netclass_t*NetScope::find_class(perm_string name) return def_parent->find_class(name); } - // If there is no further to look, ... - if (up_ == 0) - return 0; - // Try looking up for the class. - return up_->find_class(name); + if (up_!=0 && type_!=MODULE) + return up_->find_class(name); + + // Try the compilation unit. + if (unit_ != 0) + return unit_->find_class(name); + + // Nowhere left to try... + return 0; } /* @@ -741,3 +747,33 @@ perm_string NetScope::local_symbol() res << "_s" << (lcounter_++); return lex_strings.make(res.str()); } + +void NetScope::add_tie_hi(Design*des) +{ + if (tie_hi_ == 0) { + NetNet*sig = new NetNet(this, lex_strings.make("_LOGIC1"), + NetNet::WIRE, &netvector_t::scalar_logic); + sig->local_flag(true); + + tie_hi_ = new NetLogic(this, local_symbol(), + 1, NetLogic::PULLUP, 1); + des->add_node(tie_hi_); + + connect(sig->pin(0), tie_hi_->pin(0)); + } +} + +void NetScope::add_tie_lo(Design*des) +{ + if (tie_lo_ == 0) { + NetNet*sig = new NetNet(this, lex_strings.make("_LOGIC0"), + NetNet::WIRE, &netvector_t::scalar_logic); + sig->local_flag(true); + + tie_lo_ = new NetLogic(this, local_symbol(), + 1, NetLogic::PULLDOWN, 1); + des->add_node(tie_lo_); + + connect(sig->pin(0), tie_lo_->pin(0)); + } +} diff --git a/netclass.cc b/netclass.cc index 0ae3ba1dc..7e69a604d 100644 --- a/netclass.cc +++ b/netclass.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2017 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 @@ -165,7 +165,8 @@ bool netclass_t::test_for_missing_initializers() const NetScope*netclass_t::method_from_name(perm_string name) const { NetScope*task = class_scope_->child( hname_t(name) ); - if (task == 0) return 0; + if ((task == 0) && super_) + task = super_->method_from_name(name); return task; } diff --git a/netlist.cc b/netlist.cc index 44f142246..c0650959e 100644 --- a/netlist.cc +++ b/netlist.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2017 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,6 +24,7 @@ # include # include # include +# include # include "compiler.h" # include "netlist.h" # include "netmisc.h" @@ -175,6 +176,7 @@ bool NetPins::pins_are_virtual(void) const NetPins::NetPins(unsigned npins) : npins_(npins) { + default_dir_ = Link::PASSIVE; pins_ = NULL; // Wait until someone asks. if (disable_virtual_pins) devirtualize_pins(); // Ask. Bummer. } @@ -557,7 +559,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(); } } @@ -1021,11 +1023,36 @@ NetProc::~NetProc() NetProcTop::NetProcTop(NetScope*s, ivl_process_type_t t, NetProc*st) : type_(t), statement_(st), scope_(s) { + synthesized_design_ = 0; } NetProcTop::~NetProcTop() { + if (!synthesized_design_) { + delete statement_; + return; + } + + NexusSet nex_set; + statement_->nex_output(nex_set); + delete statement_; + + bool flag = false; + for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) { + + NetNet*net = nex_set[idx].lnk.nexus()->pick_any_net(); + if (net->peek_lref() > 0) { + cerr << get_fileline() << ": warning: '" << net->name() + << "' is driven by more than one process." << endl; + flag = true; + } + } + if (flag) { + cerr << get_fileline() << ": sorry: Cannot synthesize signals " + "that are driven by more than one process." << endl; + synthesized_design_->errors += 1; + } } NetProc* NetProcTop::statement() @@ -1153,8 +1180,8 @@ unsigned NetReplicate::repeat() const * ... */ -NetFF::NetFF(NetScope*s, perm_string n, unsigned width__) -: NetNode(s, n, 8), width_(width__) +NetFF::NetFF(NetScope*s, perm_string n, bool negedge__, unsigned width__) +: NetNode(s, n, 8), negedge_(negedge__), width_(width__) { pin_Clock().set_dir(Link::INPUT); pin_Enable().set_dir(Link::INPUT); @@ -1170,6 +1197,11 @@ NetFF::~NetFF() { } +bool NetFF::is_negedge() const +{ + return negedge_; +} + unsigned NetFF::width() const { return width_; @@ -1275,6 +1307,60 @@ const verinum& NetFF::sset_value() const return sset_value_; } +/* + * The NetLatch class represents an LPM_LATCH device. The pinout is assigned + * like so: + * 0 -- Enable + * 1 -- Data + * 2 -- Q + */ + +NetLatch::NetLatch(NetScope*s, perm_string n, unsigned width__) +: NetNode(s, n, 3), width_(width__) +{ + pin_Enable().set_dir(Link::INPUT); + pin_Data().set_dir(Link::INPUT); + pin_Q().set_dir(Link::OUTPUT); +} + +NetLatch::~NetLatch() +{ +} + +unsigned NetLatch::width() const +{ + return width_; +} + +Link& NetLatch::pin_Enable() +{ + return pin(0); +} + +const Link& NetLatch::pin_Enable() const +{ + return pin(0); +} + +Link& NetLatch::pin_Data() +{ + return pin(1); +} + +const Link& NetLatch::pin_Data() const +{ + return pin(1); +} + +Link& NetLatch::pin_Q() +{ + return pin(2); +} + +const Link& NetLatch::pin_Q() const +{ + return pin(2); +} NetAbs::NetAbs(NetScope*s, perm_string n, unsigned w) : NetNode(s, n, 2), width_(w) @@ -2683,7 +2769,7 @@ static DelayType delay_type_from_expr(const NetExpr*expr) * The looping structures can use the same basic code so put it here * instead of duplicating it for each one (repeat and while). */ -static DelayType get_loop_delay_type(const NetExpr*expr, const NetProc*proc) +static DelayType get_loop_delay_type(const NetExpr*expr, const NetProc*proc, bool print_delay) { DelayType result; @@ -2694,12 +2780,20 @@ static DelayType get_loop_delay_type(const NetExpr*expr, const NetProc*proc) break; /* We have a constant true expression so the body always runs. */ case DEFINITE_DELAY: - result = proc->delay_type(); + if (proc) { + result = proc->delay_type(print_delay); + } else { + result = NO_DELAY; + } break; /* We don't know if the body will run so reduce a DEFINITE_DELAY * to a POSSIBLE_DELAY. All other stay the same. */ case POSSIBLE_DELAY: - result = combine_delays(NO_DELAY, proc->delay_type()); + if (proc) { + result = combine_delays(NO_DELAY, proc->delay_type(print_delay)); + } else { + result = NO_DELAY; + } break; /* This should never happen since delay_type_from_expr() only * returns three different values. */ @@ -2712,25 +2806,40 @@ static DelayType get_loop_delay_type(const NetExpr*expr, const NetProc*proc) } /* The default object does not have any delay. */ -DelayType NetProc::delay_type() const +DelayType NetProc::delay_type(bool /* print_delay */ ) const { return NO_DELAY; } -DelayType NetBlock::delay_type() const +DelayType NetBlock::delay_type(bool print_delay) const { - DelayType result = NO_DELAY; + // A join_none has no delay. + if (type() == PARA_JOIN_NONE) return NO_DELAY; - for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { - DelayType dt = cur->delay_type(); - if (dt > result) result = dt; - if (dt == DEFINITE_DELAY) break; + DelayType result; + // A join_any has the minimum delay. + if (type() == PARA_JOIN_ANY) { + result = DEFINITE_DELAY; + for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { + DelayType dt = cur->delay_type(print_delay); + if (dt < result) result = dt; + if ((dt == NO_DELAY) && !print_delay) break; + } + + // A begin or join has the maximum delay. + } else { + result = NO_DELAY; + for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { + DelayType dt = cur->delay_type(print_delay); + if (dt > result) result = dt; + if ((dt == DEFINITE_DELAY) && !print_delay) break; + } } return result; } -DelayType NetCase::delay_type() const +DelayType NetCase::delay_type(bool print_delay) const { DelayType result = NO_DELAY; bool def_stmt = false; @@ -2738,13 +2847,15 @@ DelayType NetCase::delay_type() const for (unsigned idx = 0; idx < nstmts; idx += 1) { if (!expr(idx)) def_stmt = true; + DelayType dt = stat(idx) ? stat(idx)->delay_type(print_delay) : NO_DELAY; if (idx == 0) { - result = stat(idx)->delay_type(); + result = dt; } else { - result = combine_delays(result, stat(idx)->delay_type()); + result = combine_delays(result, dt); } } +// FIXME: If all the cases are covered (e.g. an enum) then this is not true. /* If we don't have a default statement we don't know for sure * that we have a delay. */ if (!def_stmt) result = combine_delays(NO_DELAY, result); @@ -2752,69 +2863,636 @@ DelayType NetCase::delay_type() const return result; } -DelayType NetCondit::delay_type() const +DelayType NetCondit::delay_type(bool print_delay) const { - DelayType if_type = if_ ? if_->delay_type() : NO_DELAY; - DelayType el_type = else_? else_->delay_type() : NO_DELAY; + DelayType if_type = if_ ? if_->delay_type(print_delay) : NO_DELAY; + DelayType el_type = else_? else_->delay_type(print_delay) : NO_DELAY; return combine_delays(if_type, el_type); } /* * A do/while will execute the body at least once. */ -DelayType NetDoWhile::delay_type() const +DelayType NetDoWhile::delay_type(bool print_delay) const { - return proc_->delay_type(); + if (proc_) return proc_->delay_type(print_delay); + + return ZERO_DELAY; } -DelayType NetEvWait::delay_type() const +DelayType NetEvWait::delay_type(bool print_delay) const { + if (print_delay) { + cerr << get_fileline() << ": error: an event control is not allowed " + "in an always_comb, always_ff or always_latch process." + << endl; + } + return DEFINITE_DELAY; } -DelayType NetForever::delay_type() const +DelayType NetForever::delay_type(bool print_delay) const { - return statement_->delay_type(); + if (statement_) return statement_->delay_type(print_delay); + + return ZERO_DELAY; } -DelayType NetForLoop::delay_type() const +DelayType NetForLoop::delay_type(bool print_delay) const { - return get_loop_delay_type(condition_, statement_); + return get_loop_delay_type(condition_, statement_, print_delay); } -DelayType NetPDelay::delay_type() const +DelayType NetPDelay::delay_type(bool print_delay) const { + if (print_delay) { + cerr << get_fileline() << ": error: a blocking delay is not allowed " + "in an always_comb, always_ff or always_latch process." + << endl; + } + if (expr_) { - return delay_type_from_expr(expr_); - } else { - if (delay() > 0) { - return DEFINITE_DELAY; + if (statement_) { + return combine_delays(delay_type_from_expr(expr_), + statement_->delay_type(print_delay)); } else { - if (statement_) { - return statement_->delay_type(); - } else { - return NO_DELAY; - } + return delay_type_from_expr(expr_); + } + } + + if (delay() > 0) return DEFINITE_DELAY; + + if (statement_) { + return combine_delays(ZERO_DELAY, + statement_->delay_type(print_delay)); + } else { + return ZERO_DELAY; + } +} + +DelayType NetRepeat::delay_type(bool print_delay) const +{ + return get_loop_delay_type(expr_, statement_, print_delay); +} + +DelayType NetTaskDef::delay_type(bool print_delay) const +{ + if (proc_) { + return proc_->delay_type(print_delay); + } else { + return NO_DELAY; + } +} + +DelayType NetUTask::delay_type(bool print_delay) const +{ + return task()->task_def()->delay_type(print_delay); +} + +static bool do_expr_event_match(const NetExpr*expr, const NetEvWait*evwt) +{ + // The event wait should only have a single event. + if (evwt->nevents() != 1) return false; + // The event should have a single probe. + const NetEvent *evt = evwt->event(0); + if (evt->nprobe() != 1) return false; + // The probe should be for any edge. + const NetEvProbe *prb = evt->probe(0); + if (prb->edge() != NetEvProbe::ANYEDGE) return false; + // Create a NexusSet from the event probe signals. + NexusSet *ns_evwt = new NexusSet; + for (unsigned idx =0; idx < prb->pin_count(); idx += 1) { + if (! prb->pin(idx).is_linked()) { + delete ns_evwt; + return false; + } + // Casting away const is safe since this nexus set is only being read. + ns_evwt->add(const_cast (prb->pin(idx).nexus()), + 0, prb->pin(idx).nexus()->vector_width()); + } + // Get the NexusSet for the expression. + NexusSet *ns_expr = expr->nex_input(); + // Make sure the event and expression NexusSets match exactly. + if (ns_evwt->size() != ns_expr->size()) { + delete ns_evwt; + delete ns_expr; + return false; + } + ns_expr->rem(*ns_evwt); + delete ns_evwt; + if (ns_expr->size() != 0) { + delete ns_expr; + return false; + } + delete ns_expr; + + return true; +} + +static bool while_is_wait(const NetExpr*expr, const NetProc*stmt) +{ + if (const NetEvWait*evwt = dynamic_cast(stmt)) { + if (evwt->statement()) return false; + const NetEBComp*cond = dynamic_cast(expr); + if (! cond) return false; + if (cond->op() != 'N') return false; + const NetEConst*cval = dynamic_cast(cond->right()); + if (! cval) return false; + const verinum val = cval->value(); + if (val.len() != 1) return false; + if (val.get(0) != verinum::V1) return false; + if (! do_expr_event_match(cond->left(), evwt)) return false; + if (evwt->get_lineno() != cond->get_lineno()) return false; + if (evwt->get_file() != cond->get_file()) return false; + return true; + } + return false; +} + +DelayType NetWhile::delay_type(bool print_delay) const +{ + // If the wait was a constant value the compiler already removed it + // so we know we can only have a possible delay. + if (while_is_wait(cond_, proc_)) { + if (print_delay) { + cerr << get_fileline() << ": error: a wait statement is " + "not allowed in an " + "always_comb, always_ff or always_latch process." + << endl; + } + return POSSIBLE_DELAY; + } + return get_loop_delay_type(cond_, proc_, print_delay); +} + +/* + * These are the check_synth() functions. They are used to print + * a warning if the item is not synthesizable. + */ +static const char * get_process_type_as_string(ivl_process_type_t pr_type) +{ + switch (pr_type) { + case IVL_PR_ALWAYS_COMB: + return "in an always_comb process."; + break; + case IVL_PR_ALWAYS_FF: + return "in an always_ff process."; + break; + case IVL_PR_ALWAYS_LATCH: + return "in an always_latch process."; + break; + default: + assert(0); + return 0; + } +} + +static void print_synth_warning(const NetProc *net_proc, const char *name, + ivl_process_type_t pr_type) +{ + cerr << net_proc->get_fileline() << ": warning: " << name + << " statement cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; +} + +static void check_if_logic_l_value(const NetAssignBase *base, + ivl_process_type_t pr_type) +{ + if (base->l_val_count() != 1) return; + + const NetAssign_*lval = base->l_val(0); + if (! lval) return; + + NetNet*sig = lval->sig(); + if (! sig) return; + + if ((sig->data_type() != IVL_VT_BOOL) && + (sig->data_type() != IVL_VT_LOGIC)) { + cerr << base->get_fileline() << ": warning: Assinging to a " + "non-integral variable ("<< sig->name() + << ") cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; + } +} + +/* By default elements can be synthesized or ignored. */ +bool NetProc::check_synth(ivl_process_type_t /* pr_type */, + const NetScope* /* scope */ ) const +{ + return false; +} + +// FIXME: User function calls still need to be checked (NetEUFunc). +// : Non-constant system functions need a warning (NetESFunc). +// : Constant functions should already be elaborated. + +/* By default assign elements can be synthesized. */ +bool NetAssignBase::check_synth(ivl_process_type_t /* pr_type */, + const NetScope* /* scope */ ) const +{ + return false; +} + +bool NetAssign::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + check_if_logic_l_value(this, pr_type); + +// FIXME: Check that ff/latch only use this for internal signals. + return false; +} + +bool NetAssignNB::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + bool result = false; + if (pr_type == IVL_PR_ALWAYS_COMB) { + cerr << get_fileline() << ": warning: A non-blocking assignment " + "should not be used in an always_comb process." << endl; + } + + if (event_) { + cerr << get_fileline() << ": error: A non-blocking assignment " + "cannot be synthesized with an event control " + << get_process_type_as_string(pr_type) << endl; + result = true; + } + + check_if_logic_l_value(this, pr_type); + + return result; +} + +bool NetBlock::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + // Only a begin/end can be synthesized. + if (type() != SEQU) { + cerr << get_fileline() << ": error: A fork/"; + switch (type()) { + case PARA: + cerr << "join"; + break; + case PARA_JOIN_ANY: + cerr << "join_any"; + break; + case PARA_JOIN_NONE: + cerr << "join_none"; + break; + default: + assert(0); + } + cerr << " statement cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; + result = true; + } + + const NetScope*save_scope = scope; + if (subscope()) scope = subscope(); + if (scope != save_scope) { + result |= scope->check_synth(pr_type, scope); + } + for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { + result |= cur->check_synth(pr_type, scope); + } + scope = save_scope; + return result; +} + +bool NetCase::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + for (unsigned idx = 0; idx < nitems(); idx += 1) { + if (stat(idx)) result |= stat(idx)->check_synth(pr_type, scope); + } +// FIXME: Check for ff/latch/comb structures. + return result; +} + +bool NetCAssign::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "A procedural assign", pr_type); + return false; +} + +bool NetCondit::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + if (if_) result |= if_->check_synth(pr_type, scope); + if (else_) result |= else_->check_synth(pr_type, scope); +// FIXME: Check for ff/latch/comb structures. + return result; +} + +bool NetDeassign::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "A procedural deassign", pr_type); + return false; +} + +bool NetDisable::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + while (scope) { + if (scope != target_) scope = scope->parent(); + else break; + } + + + if (! scope) { + cerr << get_fileline() << ": warning: A disable statement can " + "only be synthesized when disabling an enclosing block " + << get_process_type_as_string(pr_type) << endl; + } + return false; +} + +bool NetDoWhile::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + print_synth_warning(this, "A do/while", pr_type); + if (proc_) result |= proc_->check_synth(pr_type, scope); + return result; +} + +bool NetEvTrig::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "An event trigger", pr_type); + return false; +} + +// The delay check above has already marked this as an error. +bool NetEvWait::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + if (statement_) result |= statement_->check_synth(pr_type, scope); + return result; +} + +bool NetForce::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "A force", pr_type); + return false; +} + +bool NetForever::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + print_synth_warning(this, "A forever", pr_type); + if (statement_) result |= statement_->check_synth(pr_type, scope); + return result; +} + +/* + * A bunch of private routines to verify that a for loop has the correct + * structure for synthesis. + */ +static void print_for_idx_warning(const NetProc*proc, const char*check, + ivl_process_type_t pr_type, NetNet*idx) +{ + cerr << proc->get_fileline() << ": warning: A for statement must use " + "the index (" << idx->name() << ") in the " << check + << " expression to be synthesized " + << get_process_type_as_string(pr_type) << endl; +} + +static void check_for_const_synth(const NetExpr*expr, const NetProc*proc, + const char*str, ivl_process_type_t pr_type) +{ + if (! dynamic_cast(expr)) { + cerr << proc-> get_fileline() << ": warning: A for " + "statement must " << str + << " value to be synthesized " + << get_process_type_as_string(pr_type) << endl; + } +} + +static void check_for_bin_synth(const NetExpr*left,const NetExpr*right, + const char*str, const char*check, + const NetProc*proc, + ivl_process_type_t pr_type, NetNet*index) +{ + const NetESignal*lsig = dynamic_cast(left); + const NetESignal*rsig = dynamic_cast(right); + + if (lsig && (lsig->sig() == index)) { + check_for_const_synth(right, proc, str, pr_type); + } else if (rsig && (rsig->sig() == index)) { + check_for_const_synth(left, proc, str, pr_type); + } else { + print_for_idx_warning(proc, check, pr_type, index); + } +} + +static void print_for_step_warning(const NetProc*proc, + ivl_process_type_t pr_type) +{ + cerr << proc->get_fileline() << ": warning: A for statement step must " + "be a simple assignment statement to be synthesized " + << get_process_type_as_string(pr_type) << endl; +} + +static void print_for_step_warning(const NetProc*proc, + ivl_process_type_t pr_type, NetNet*idx) +{ + cerr << proc->get_fileline() << ": warning: A for statement step must " + "be an assignment to the index variable (" + << idx->name() << ") to be synthesized " + << get_process_type_as_string(pr_type) << endl; +} + +static void check_for_bstep_synth(const NetExpr*expr, const NetProc*proc, + ivl_process_type_t pr_type, NetNet*index) +{ + if (const NetECast*tmp = dynamic_cast(expr)) { + expr = tmp->expr(); + } + + if (const NetEBAdd*tmp = dynamic_cast(expr)) { + check_for_bin_synth(tmp->left(), tmp->right(), + "change by a constant", "step", proc, pr_type, + index); + } else { + cerr << proc->get_fileline() << ": warning: A for statement " + "step must be a simple binary +/- " + "to be synthesized " + << get_process_type_as_string(pr_type) << endl; + } +} + +static void check_for_step_synth(const NetAssign*assign, const NetProc*proc, + ivl_process_type_t pr_type, NetNet*index) +{ + if (assign->l_val_count() != 1) { + print_for_step_warning(proc, pr_type); + } else if (assign->l_val(0)->sig() != index) { + print_for_step_warning(proc, pr_type, index); + } else { + switch (assign->assign_operator()) { + case '+': + case '-': + check_for_const_synth(assign->rval(), proc, + "have a constant step", pr_type); + break; + case 0: + check_for_bstep_synth(assign->rval(), proc, pr_type, index); + break; + default: + cerr << proc->get_fileline() << ": warning: A for statement " + "step does not support operator '" + << assign->assign_operator() + << "' it must be +/- to be synthesized " + << get_process_type_as_string(pr_type) << endl; + break; } } } -DelayType NetRepeat::delay_type() const +bool NetForLoop::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const { - return get_loop_delay_type(expr_, statement_); + bool result = false; + +// FIXME: What about an enum (NetEConstEnum)? + if (! dynamic_cast(init_expr_)) { + cerr << get_fileline() << ": warning: A for statement must " + "have a constant initial value to be synthesized " + << get_process_type_as_string(pr_type) << endl; + } + +// FIXME: Do the following also need to be supported in the condition? +// It would seem like they are hard to use to find the bounds. +// From NetEBinary +// What about NetEBits sig & constant, etc. +// From NetEUnary +// What about NetEUBits ! sig or ! (sig == constat) +// What about NetEUReduce &signal + if (const NetESignal*tmp = dynamic_cast(condition_)) { + if (tmp->sig() != index_) { + print_for_idx_warning(this, "condition", pr_type, index_); + } + } else if (const NetEBComp*cmp = dynamic_cast(condition_)) { + check_for_bin_synth(cmp->left(), cmp->right(), + "compare against a constant", "condition", + this, pr_type, index_); + } else { + print_for_idx_warning(this, "condition", pr_type, index_); + } + + if (const NetAssign*tmp = dynamic_cast(step_statement_)) { + check_for_step_synth(tmp, this, pr_type, index_); + } else { + print_for_step_warning(this, pr_type); + } + + if (statement_) result |= statement_->check_synth(pr_type, scope); + return result; } -DelayType NetTaskDef::delay_type() const +// The delay check above has already marked this as an error. +bool NetPDelay::check_synth(ivl_process_type_t /* pr_type */, + const NetScope* /* scope */ ) const { - return proc_->delay_type(); + return false; } -DelayType NetUTask::delay_type() const +bool NetRelease::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const { - return task()->task_def()->delay_type(); + print_synth_warning(this, "A release", pr_type); + return false; } -DelayType NetWhile::delay_type() const +bool NetRepeat::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const { - return get_loop_delay_type(cond_, proc_); + bool result = false; + print_synth_warning(this, "A repeat", pr_type); + if (statement_) result |= statement_->check_synth(pr_type, scope); + return result; +} + +bool NetScope::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */) const +{ + bool result = false; + // Skip local events/signals + for (NetEvent*cur = events_ ; cur ; cur = cur->snext_) { + if (cur->local_flag()) continue; + cerr << cur->get_fileline() << ": warning: An event (" + << cur->name() << ") cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; + } + for (signals_map_iter_t cur = signals_map_.begin(); + cur != signals_map_.end() ; ++ cur) { + const NetNet*sig = cur->second; + if ((sig->data_type() != IVL_VT_BOOL) && + (sig->data_type() != IVL_VT_LOGIC)) { + cerr << sig->get_fileline() << ": warning: A non-integral " + "variable (" << sig->name() << ") cannot be " + "synthesized " + << get_process_type_as_string(pr_type) << endl; + } + } + return result; +} + +bool NetSTask::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */) const +{ + if (strcmp(name(), "$ivl_darray_method$delete") == 0) { + cerr << get_fileline() << ": warning: Dynamic array " + "delete method cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; + } else { + cerr << get_fileline() << ": warning: System task (" + << name() << ") cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; + } + return false; +} + +bool NetTaskDef::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */) const +{ + bool result = false; + const NetScope *tscope = this->scope(); + result |= tscope->check_synth(pr_type, tscope); + if (! tscope->is_auto()) { + cerr << tscope->get_def_file() << ":" + << tscope->get_def_lineno() + << ": warning: user task (" << tscope->basename() + << ") must be automatic to be synthesized " + << get_process_type_as_string(pr_type) << endl; + } + if (proc_) result |= proc_->check_synth(pr_type, tscope); + return result; +} + +bool NetUTask::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + return task()->task_def()->check_synth(pr_type, scope); +} + +bool NetWhile::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + // A wait is already maked as an error in the delay check above. + if (! while_is_wait(cond_, proc_)) { + print_synth_warning(this, "A while", pr_type); + if (proc_) result |= proc_->check_synth(pr_type, scope); + } + return result; } diff --git a/netlist.h b/netlist.h index 28dc78667..31cfc889e 100644 --- a/netlist.h +++ b/netlist.h @@ -1,7 +1,7 @@ #ifndef IVL_netlist_H #define IVL_netlist_H /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2017 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -90,6 +90,12 @@ class netvector_t; struct target; struct functor_t; +#if defined(__cplusplus) && defined(_MSC_VER) +# define ENUM_UNSIGNED_INT : unsigned int +#else +# define ENUM_UNSIGNED_INT +#endif + ostream& operator << (ostream&o, ivl_variable_type_t val); extern void join_island(NetPins*obj); @@ -102,8 +108,7 @@ class Link { friend class NexusSet; public: - enum DIR { PASSIVE, INPUT, OUTPUT }; - + enum DIR ENUM_UNSIGNED_INT { PASSIVE, INPUT, OUTPUT }; private: // Only NetPins can create/delete Link objects Link(); ~Link(); @@ -245,7 +250,6 @@ class NetPins : public LineInfo { */ class NetObj : public NetPins, public Attrib { - public: public: // The name of the object must be a permallocated string. A // lex_strings string, for example. @@ -256,6 +260,7 @@ class NetObj : public NetPins, public Attrib { const NetScope* scope() const; perm_string name() const { return name_; } + void rename(perm_string n) { name_ = n; } const NetExpr* rise_time() const { return delay1_; } const NetExpr* fall_time() const { return delay2_; } @@ -285,7 +290,7 @@ class NetObj : public NetPins, public Attrib { class IslandBranch { public: - IslandBranch(ivl_discipline_t dis =0) : island_(0), discipline_(dis) { } + explicit IslandBranch(ivl_discipline_t dis =0) : island_(0), discipline_(dis) { } ivl_island_t get_island() const { return island_; } @@ -374,6 +379,8 @@ class Nexus { NetNet* pick_any_net(); + NetNode* pick_any_node(); + /* This method counts the number of input and output links for this nexus, and assigns the results to the output arguments. */ void count_io(unsigned&inp, unsigned&out) const; @@ -383,6 +390,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; @@ -619,7 +630,7 @@ class NetDelaySrc : public NetObj { class PortType { public: - enum Enum { NOT_A_PORT, PIMPLICIT, PINPUT, POUTPUT, PINOUT, PREF }; + enum Enum ENUM_UNSIGNED_INT { NOT_A_PORT, PIMPLICIT, PINPUT, POUTPUT, PINOUT, PREF }; /* * Merge Port types (used to construct a sane combined port-type @@ -647,7 +658,7 @@ struct PortInfo class NetNet : public NetObj, public PortType { public: - enum Type { NONE, IMPLICIT, IMPLICIT_REG, INTEGER, WIRE, TRI, TRI1, + enum Type ENUM_UNSIGNED_INT { NONE, IMPLICIT, IMPLICIT_REG, INTEGER, WIRE, TRI, TRI1, SUPPLY0, SUPPLY1, WAND, TRIAND, TRI0, WOR, TRIOR, REG, UNRESOLVED_WIRE }; @@ -708,7 +719,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_; } @@ -765,7 +776,7 @@ class NetNet : public NetObj, public PortType { unsigned peek_eref() const; // Assignment statements count their lrefs here. And by - // asignment statements, we mean BEHAVIORAL assignments. + // assignment statements, we mean BEHAVIORAL assignments. void incr_lref(); void decr_lref(); unsigned peek_lref() const { return lref_count_; } @@ -916,10 +927,13 @@ class NetScope : public Definitions, public Attrib { public: enum TYPE { MODULE, CLASS, TASK, FUNC, BEGIN_END, FORK_JOIN, GENBLOCK, PACKAGE }; - /* Create a new scope, and attach it to the given parent. The - name is expected to have been permallocated. */ - NetScope(NetScope*up, const hname_t&name, TYPE t, bool nest=false, - bool program=false, bool interface=false); + /* Create a new scope associated with a given compilation unit, + and attach it to the given parent. If no compilation unit is + specified, the parent's compilation unit is used. The name + is expected to have been permallocated. */ + NetScope(NetScope*up, const hname_t&name, TYPE t, NetScope*in_unit=0, + bool nest=false, bool program=false, bool interface=false, + bool compilation_unit=false); ~NetScope(); /* Rename the scope using the name generated by inserting as @@ -991,10 +1005,12 @@ class NetScope : public Definitions, public Attrib { netclass_t* find_class(perm_string name); - /* The parent and child() methods allow users of NetScope - objects to locate nearby scopes. */ + /* The unit(), parent(), and child() methods allow users of + NetScope objects to locate nearby scopes. */ + NetScope* unit() { return unit_; } NetScope* parent() { return up_; } NetScope* child(const hname_t&name); + const NetScope* unit() const { return unit_; } const NetScope* parent() const { return up_; } const NetScope* child(const hname_t&name) const; @@ -1012,9 +1028,17 @@ class NetScope : public Definitions, public Attrib { // Program blocks and interfaces have elaboration constraints. inline bool program_block() const { return program_block_; } inline bool is_interface() const { return is_interface_; } - TYPE type() const; + inline bool is_unit() const { return is_unit_; } + inline TYPE type() const { return type_; } 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*); @@ -1134,6 +1158,9 @@ class NetScope : public Definitions, public Attrib { perm_string local_symbol(); void dump(ostream&) const; + // Check to see if the scope has items that are not allowed + // in an always_comb/ff/latch process. + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; void emit_scope(struct target_t*tgt) const; bool emit_defs(struct target_t*tgt) const; @@ -1141,6 +1168,12 @@ class NetScope : public Definitions, public Attrib { children of this node as well. */ void run_functor(Design*des, functor_t*fun); + /* These are used in synthesis. They provide shared pullup and + pulldown nodes for this scope. */ + void add_tie_hi(Design*des); + void add_tie_lo(Design*des); + Link&tie_hi() const { return tie_hi_->pin(0); }; + Link&tie_lo() const { return tie_lo_->pin(0); }; /* This member is used during elaboration to pass defparam assignments from the scope pass to the parameter evaluation @@ -1225,6 +1258,8 @@ class NetScope : public Definitions, public Attrib { bool program_block_; // True if the scope is an interface bool is_interface_; + // True if the scope is a compilation unit + bool is_unit_; perm_string file_; perm_string def_file_; @@ -1245,6 +1280,8 @@ class NetScope : public Definitions, public Attrib { vector ports_; + const NetProc*var_init_; + union { NetTaskDef*task_; NetFuncDef*func_; @@ -1253,6 +1290,7 @@ class NetScope : public Definitions, public Attrib { const PFunction*func_pform_; unsigned elab_stage_; + NetScope*unit_; NetScope*up_; map children_; @@ -1262,6 +1300,9 @@ class NetScope : public Definitions, public Attrib { /* Final procedures sets this to notify statements that they are part of a final procedure. */ bool in_final_; + + NetNode*tie_hi_; + NetNode*tie_lo_; }; /* @@ -1622,9 +1663,10 @@ class NetModulo : public NetNode { class NetFF : public NetNode { public: - NetFF(NetScope*s, perm_string n, unsigned vector_width); + NetFF(NetScope*s, perm_string n, bool negedge, unsigned vector_width); ~NetFF(); + bool is_negedge() const; unsigned width() const; Link& pin_Clock(); @@ -1656,11 +1698,41 @@ class NetFF : public NetNode { virtual void functor_node(Design*des, functor_t*fun); private: + bool negedge_; unsigned width_; verinum aset_value_; verinum sset_value_; }; + +/* + * This class represents an LPM_LATCH device. There is no literal gate + * type in Verilog that maps, but gates of this type can be inferred. + */ +class NetLatch : public NetNode { + + public: + NetLatch(NetScope*s, perm_string n, unsigned vector_width); + ~NetLatch(); + + unsigned width() const; + + Link& pin_Enable(); + Link& pin_Data(); + Link& pin_Q(); + + const Link& pin_Enable() const; + const Link& pin_Data() const; + const Link& pin_Q() const; + + virtual void dump_node(ostream&, unsigned ind) const; + virtual bool emit_node(struct target_t*) const; + virtual void functor_node(Design*des, functor_t*fun); + + private: + unsigned width_; +}; + /* * This class implements a basic LPM_MULT combinational multiplier. It * is used as a structural representation of the * operator. The @@ -1976,7 +2048,7 @@ class NetExpr : public LineInfo { // Get the Nexus that are the input to this // expression. Normally this descends down to the reference to // a signal that reads from its input. - virtual NexusSet* nex_input(bool rem_out = true) =0; + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const =0; // Return a version of myself that is structural. This is used // for converting expressions to gates. The arguments are: @@ -2019,7 +2091,7 @@ class NetEArrayPattern : public NetExpr { void dump(ostream&) const; NetEArrayPattern* dup_expr() const; - NexusSet* nex_input(bool rem_out =true); + NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; private: std::vector items_; @@ -2052,7 +2124,7 @@ class NetEConst : public NetExpr { virtual NetEConst* dup_expr() const; virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*); - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; @@ -2124,7 +2196,7 @@ class NetECReal : public NetExpr { virtual NetECReal* dup_expr() const; virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*); - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; @@ -2216,7 +2288,7 @@ class NetPartSelect : public NetNode { * * wire [7:0] foo = NetSubstitute(bar, bat, off); * - * meaus that bar is a vector the same width as foo, bat is a narrower + * means that bar is a vector the same width as foo, bat is a narrower * vector. The off is a constant offset into the bar vector. This * looks something like this: * @@ -2238,6 +2310,7 @@ class NetSubstitute : public NetNode { virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*tgt) const; + virtual void functor_node(Design*des, functor_t*fun); private: unsigned wid_; @@ -2287,7 +2360,7 @@ class NetBUFZ : public NetNode { * * 0 -- Output (always returns 0 or 1) * 1 -- Input - * 2 -- Input (windcard input for EQX and EQZ variants) + * 2 -- Input (wildcard input for EQX and EQZ variants) */ class NetCaseCmp : public NetNode { @@ -2295,6 +2368,8 @@ class NetCaseCmp : public NetNode { enum kind_t { EEQ, // === NEQ, // !== + WEQ, // ==? + WNE, // !=? XEQ, // casex guard tests ZEQ // casez guard tests }; @@ -2565,7 +2640,7 @@ class NetProc : public virtual LineInfo { // Find the nexa that are input by the statement. This is used // for example by @* to find the inputs to the process for the // sensitivity list. - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; // Find the nexa that are set by the statement. Add the output // values to the set passed as a parameter. @@ -2596,42 +2671,63 @@ class NetProc : public virtual LineInfo { // process. Most process types are not. virtual bool is_synchronous(); - // Synthesize as asynchronous logic, and return true. The - // nex_out is where this function attaches its output - // results. The accumulated_nex_out is used by sequential - // blocks to show outputs from the previous code. + // Synthesize as asynchronous logic, and return true on success. + // + // nex_map holds the set of nexuses that are driven by this + // process, nex_out holds the accumulated outputs from this and + // preceding sequential processes (i.e statements in the same + // block), enables holds the accumulated clock/gate enables, + // and bitmasks holds the accumulated masks that flag which bits + // are unconditionally driven (i.e. driven by every clause in + // every statement). On output, the values passed in to nex_out, + // enables, and bitmasks may either be merged with or replaced + // by the values originating from this process, depending on the + // type of statement this process represents. + // + // The clock/gate enables generated by synthesis operate at a + // vector level (i.e. they are asserted if any bit(s) in the + // vector are driven). + typedef vector mask_t; virtual bool synth_async(Design*des, NetScope*scope, - NexusSet&nex_map, - NetBus&nex_out, - NetBus&accumulated_nex_out); + NexusSet&nex_map, NetBus&nex_out, + NetBus&enables, vector&bitmasks); - // Synthesize as synchronous logic, and return true. That - // means binding the outputs to the data port of a FF, and the - // event inputs to a FF clock. Only some key NetProc sub-types + // Synthesize as synchronous logic, and return true on success. + // That means binding the outputs to the data port of a FF, and + // the event inputs to a FF clock. Only some key NetProc sub-types // that have specific meaning in synchronous statements. The // remainder reduce to a call to synth_async that connects the // output to the Data input of the FF. // - // The events argument is filled in be the NetEvWait - // implementation of this method with the probes that it does - // not itself pick off as a clock. These events should be - // picked off by e.g. condit statements as set/reset inputs to - // the flipflop being generated. + // The nex_map, nex_out, ff_ce, and bitmasks arguments serve + // the same purpose as in the synth_async method (where ff_ce + // is equivalent to enables). The events argument is filled + // in by the NetEvWait implementation of this method with the + // probes that it does not itself pick off as a clock. These + // events should be picked off by e.g. condit statements as + // asynchronous set/reset inputs to the flipflop being generated. virtual bool synth_sync(Design*des, NetScope*scope, + bool&ff_negedge, NetNet*ff_clock, NetBus&ff_ce, NetBus&ff_aclr, NetBus&ff_aset, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const std::vector&events); virtual void dump(ostream&, unsigned ind) const; // Recursively checks to see if there is delay in this element. - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + // Check to see if the item is synthesizable. + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; protected: bool synth_async_block_substatement_(Design*des, NetScope*scope, NexusSet&nex_map, - NetBus&accumulated_nex_out, + NetBus&nex_out, + NetBus&enables, + vector&bitmasks, NetProc*substmt); private: friend class NetBlock; @@ -2645,13 +2741,15 @@ class NetProc : public virtual LineInfo { class NetAlloc : public NetProc { public: - NetAlloc(NetScope*); + explicit NetAlloc(NetScope*); ~NetAlloc(); const string name() const; const NetScope* scope() const; + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; @@ -2723,6 +2821,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(). @@ -2753,7 +2857,7 @@ class NetAssign_ { // being outputs. For example foo[idx] = ... is the l-value // (NetAssign_ object) with a foo l-value and the input // expression idx. - NexusSet* nex_input(bool rem_out = true); + NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; // Figuring out nex_output to process ultimately comes down to // this method. @@ -2773,6 +2877,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_; @@ -2800,7 +2905,7 @@ class NetAssignBase : public NetProc { void set_delay(NetExpr*); const NetExpr* get_delay() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void nex_output(NexusSet&o); @@ -2810,11 +2915,12 @@ class NetAssignBase : public NetProc { bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); // This dumps all the lval structures. void dump_lval(ostream&) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: NetAssign_*lval_; @@ -2836,10 +2942,13 @@ class NetAssign : public NetAssignBase { virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, 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; @@ -2856,6 +2965,7 @@ class NetAssignNB : public NetAssignBase { virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; unsigned nevents() const; const NetEvent*event(unsigned) const; @@ -2886,6 +2996,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; @@ -2896,12 +3007,15 @@ class NetBlock : public NetProc { // synthesize as asynchronous logic, and return true. bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); bool synth_sync(Design*des, NetScope*scope, + bool&ff_negedge, NetNet*ff_clk, NetBus&ff_ce, NetBus&ff_aclr,NetBus&ff_aset, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const std::vector&events); // This version of emit_recurse scans all the statements of @@ -2909,12 +3023,13 @@ class NetBlock : public NetProc { // for sequential blocks. void emit_recurse(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: const Type type_; @@ -2952,16 +3067,17 @@ class NetCase : public NetProc { inline const NetExpr*expr(unsigned idx) const { return items_[idx].guard;} inline const NetProc*stat(unsigned idx) const { return items_[idx].statement; } - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void nex_output(NexusSet&out); bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -2973,7 +3089,7 @@ class NetCase : public NetProc { bool synth_async_casez_(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); TYPE type_; @@ -2998,9 +3114,9 @@ class NetCAssign : public NetAssignBase { explicit NetCAssign(NetAssign_*lv, NetExpr*rv); ~NetCAssign(); - virtual NexusSet* nex_input(bool rem_out = true); virtual void dump(ostream&, unsigned ind) const; virtual bool emit_proc(struct target_t*) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: // not implemented NetCAssign(const NetCAssign&); @@ -3031,24 +3147,28 @@ class NetCondit : public NetProc { bool emit_recurse_if(struct target_t*) const; bool emit_recurse_else(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void nex_output(NexusSet&o); bool is_asynchronous(); bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); bool synth_sync(Design*des, NetScope*scope, + bool&ff_negedge, NetNet*ff_clk, NetBus&ff_ce, NetBus&ff_aclr,NetBus&ff_aset, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const std::vector&events); virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3094,6 +3214,7 @@ class NetDeassign : public NetAssignBase { virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: // not implemented NetDeassign(const NetDeassign&); @@ -3117,8 +3238,11 @@ class NetDisable : public NetProc { const NetScope*target() const; + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3145,11 +3269,12 @@ class NetDoWhile : public NetProc { void emit_proc_recurse(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3206,6 +3331,9 @@ class NetEvent : public LineInfo { perm_string name() const; + bool local_flag() const { return local_flag_; } + void local_flag(bool f) { local_flag_ = f; } + // Get information about probes connected to me. unsigned nprobe() const; NetEvProbe* probe(unsigned); @@ -3237,6 +3365,7 @@ class NetEvent : public LineInfo { private: perm_string name_; + bool local_flag_; // The NetScope class uses these to list the events. NetScope*scope_; @@ -3274,8 +3403,11 @@ class NetEvTrig : public NetProc { const NetEvent*event() const; + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: NetEvent*event_; @@ -3291,12 +3423,15 @@ class NetEvWait : public NetProc { void add_event(NetEvent*tgt); void replace_event(NetEvent*orig, NetEvent*repl); + inline void set_t0_trigger() { has_t0_trigger_ = true; }; inline unsigned nevents() const { return events_.size(); } inline const NetEvent*event(unsigned idx) const { return events_[idx]; } inline NetEvent*event(unsigned idx) { return events_[idx]; } + inline bool has_t0_trigger() const { return has_t0_trigger_; }; NetProc*statement(); + const NetProc*statement() const; virtual bool emit_proc(struct target_t*) const; bool emit_recurse(struct target_t*) const; @@ -3310,27 +3445,33 @@ class NetEvWait : public NetProc { // process? This method checks. virtual bool is_synchronous(); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void nex_output(NexusSet&out); virtual bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); virtual bool synth_sync(Design*des, NetScope*scope, + bool&ff_negedge, NetNet*ff_clk, NetBus&ff_ce, NetBus&ff_aclr,NetBus&ff_aset, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const std::vector&events); virtual void dump(ostream&, unsigned ind) const; // This will ignore any statement. virtual void dump_inline(ostream&) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: NetProc*statement_; // Events that I might wait for. std::vectorevents_; + bool has_t0_trigger_; }; ostream& operator << (ostream&out, const NetEvWait&obj); @@ -3373,10 +3514,9 @@ class NetForce : public NetAssignBase { explicit NetForce(NetAssign_*l, NetExpr*r); ~NetForce(); - virtual NexusSet* nex_input(bool rem_out = true); - virtual void dump(ostream&, unsigned ind) const; virtual bool emit_proc(struct target_t*) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; }; /* @@ -3391,10 +3531,12 @@ class NetForever : public NetProc { void emit_recurse(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3413,18 +3555,19 @@ class NetForLoop : public NetProc { void emit_recurse(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; // synthesize as asynchronous logic, and return true. bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); private: NetNet*index_; @@ -3443,13 +3586,15 @@ class NetForLoop : public NetProc { class NetFree : public NetProc { public: - NetFree(NetScope*); + explicit NetFree(NetScope*); ~NetFree(); const string name() const; const NetScope* scope() const; + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; @@ -3513,12 +3658,13 @@ class NetPDelay : public NetProc { uint64_t delay() const; const NetExpr*expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; bool emit_proc_recurse(struct target_t*) const; @@ -3540,10 +3686,12 @@ class NetRepeat : public NetProc { const NetExpr*expr() const; void emit_recurse(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3566,6 +3714,7 @@ class NetRelease : public NetAssignBase { virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: }; @@ -3591,10 +3740,11 @@ class NetSTask : public NetProc { const NetExpr* parm(unsigned idx) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3624,7 +3774,8 @@ class NetTaskDef : public NetBaseDef { ~NetTaskDef(); void dump(ostream&, unsigned) const; - DelayType delay_type() const; + DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: // not implemented NetTaskDef(const NetTaskDef&); @@ -3650,7 +3801,7 @@ class NetELast : public NetExpr { virtual void expr_scan(struct expr_scan_t*) const; virtual NetELast*dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; private: NetNet*sig_; @@ -3679,7 +3830,7 @@ class NetEUFunc : public NetExpr { virtual void expr_scan(struct expr_scan_t*) const; virtual NetEUFunc*dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual NetExpr* eval_tree(); virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3715,7 +3866,7 @@ class NetEAccess : public NetExpr { virtual void expr_scan(struct expr_scan_t*) const; virtual NetEAccess*dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; private: NetBranch*branch_; @@ -3730,18 +3881,19 @@ class NetEAccess : public NetExpr { class NetUTask : public NetProc { public: - NetUTask(NetScope*); + explicit NetUTask(NetScope*); ~NetUTask(); const string name() const; const NetScope* task() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: NetScope*task_; @@ -3762,16 +3914,17 @@ class NetWhile : public NetProc { void emit_proc_recurse(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; private: - NetExpr* cond_; + NetExpr*cond_; NetProc*proc_; }; @@ -3812,8 +3965,14 @@ class NetProcTop : public LineInfo, public Attrib { bool emit(struct target_t*tgt) const; private: + bool tie_off_floating_inputs_(Design*des, + NexusSet&nex_map, NetBus&nex_in, + vector&bitmasks, + bool is_ff_input); + const ivl_process_type_t type_; NetProc*const statement_; + Design*synthesized_design_; NetScope*scope_; friend class Design; @@ -3899,7 +4058,7 @@ class NetEBinary : public NetExpr { virtual NetExpr* eval_tree(); virtual NetExpr* evaluate_function(const LineInfo&loc, map&ctx) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -4022,6 +4181,7 @@ class NetEBComp : public NetEBinary { NetEConst*eval_gt_(const NetExpr*le, const NetExpr*re) const; NetEConst*eval_gteq_(const NetExpr*le, const NetExpr*re) const; NetEConst*eval_eqeqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const; + NetEConst*eval_weqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const; }; /* @@ -4152,7 +4312,7 @@ class NetEConcat : public NetExpr { NetExpr* parm(unsigned idx) const { return parms_[idx]; } virtual ivl_variable_type_t expr_type() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual bool has_width() const; virtual NetEConcat* dup_expr() const; virtual NetEConst* eval_tree(); @@ -4200,7 +4360,7 @@ class NetESelect : public NetExpr { // sub-expression. virtual ivl_variable_type_t expr_type() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual bool has_width() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEConst* eval_tree(); @@ -4222,14 +4382,14 @@ class NetESelect : public NetExpr { class NetEEvent : public NetExpr { public: - NetEEvent(NetEvent*); + explicit NetEEvent(NetEvent*); ~NetEEvent(); const NetEvent* event() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEEvent* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void dump(ostream&os) const; @@ -4245,14 +4405,14 @@ class NetEEvent : public NetExpr { class NetENetenum : public NetExpr { public: - NetENetenum(const netenum_t*); + explicit NetENetenum(const netenum_t*); ~NetENetenum(); const netenum_t* netenum() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetENetenum* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void dump(ostream&os) const; @@ -4276,7 +4436,7 @@ class NetENew : public NetExpr { virtual void expr_scan(struct expr_scan_t*) const; virtual NetENew* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void dump(ostream&os) const; @@ -4298,7 +4458,7 @@ class NetENull : public NetExpr { virtual void expr_scan(struct expr_scan_t*) const; virtual NetENull* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void dump(ostream&os) const; }; @@ -4324,7 +4484,7 @@ class NetEProperty : public NetExpr { ivl_variable_type_t expr_type() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEProperty* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void dump(ostream&os) const; @@ -4342,14 +4502,14 @@ class NetEProperty : public NetExpr { class NetEScope : public NetExpr { public: - NetEScope(NetScope*); + explicit NetEScope(NetScope*); ~NetEScope(); const NetScope* scope() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEScope* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void dump(ostream&os) const; @@ -4383,7 +4543,7 @@ class NetESFunc : public NetExpr { map&ctx) const; virtual ivl_variable_type_t expr_type() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual const netenum_t* enumeration() const; virtual void dump(ostream&) const; @@ -4445,7 +4605,7 @@ class NetESFunc : public NetExpr { /* Added in SystemVerilog 2009 and later. */ CTONES = 0x00020024, /* $countones takes one argument. */ /* Added in SystemVerilog 2012 and later. */ - CTBITS = 0xfffe0025, /* $countbits takes one or more arguments. */ + CTBITS = 0xfffc0025, /* $countbits takes two or more arguments. */ /* Added as Icarus extensions to Verilog-A. */ ABS = 0x00020026, /* $abs takes one argument. */ MAX = 0x00040027, /* $max takes two argument. */ @@ -4455,7 +4615,7 @@ class NetESFunc : public NetExpr { bool takes_nargs_(ID func, unsigned nargs) { if (nargs > 15) nargs = 15; - return func & (1 << (nargs + 16)); + return func & (1U << (nargs + 16)); } const char* name_; @@ -4497,8 +4657,7 @@ class NetESFunc : public NetExpr { NetEConst* evaluate_array_funcs_(ID id, const NetExpr*arg0, const NetExpr*arg1) const; - NetEConst* evaluate_countbits_(const NetExpr*arg0, - const NetExpr*arg1) const; + NetEConst* evaluate_countbits_(void) const; public: bool is_built_in() const { return built_in_id_() != NOT_BUILT_IN; }; @@ -4518,7 +4677,7 @@ class NetEShallowCopy : public NetExpr { virtual void expr_scan(struct expr_scan_t*) const; virtual NetEShallowCopy* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void dump(ostream&os) const; @@ -4550,7 +4709,7 @@ class NetETernary : public NetExpr { virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; virtual ivl_variable_type_t expr_type() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); @@ -4605,7 +4764,7 @@ class NetEUnary : public NetExpr { virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual ivl_variable_type_t expr_type() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -4673,7 +4832,7 @@ class NetECast : public NetEUnary { class NetESignal : public NetExpr { public: - NetESignal(NetNet*n); + explicit NetESignal(NetNet*n); NetESignal(NetNet*n, NetExpr*word_index); ~NetESignal(); @@ -4681,7 +4840,7 @@ class NetESignal : public NetExpr { virtual NetESignal* dup_expr() const; NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); - NexusSet* nex_input(bool rem_out = true); + NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; const netenum_t*enumeration() const; virtual NetExpr*evaluate_function(const LineInfo&loc, @@ -4729,7 +4888,7 @@ struct elaborator_work_item_t { * This class contains an entire design. It includes processes and a * netlist, and can be passed around from function to function. */ -class Design : public Definitions { +class Design { public: Design(); @@ -4751,12 +4910,13 @@ class Design : public Definitions { const char* get_flag(const string&key) const; - NetScope* make_root_scope(perm_string name, bool program_block, - bool is_interface); + NetScope* make_root_scope(perm_string name, NetScope*unit_scope, + bool program_block, bool is_interface); NetScope* find_root_scope(); std::list find_root_scopes() const; - NetScope* make_package_scope(perm_string name); + NetScope* make_package_scope(perm_string name, NetScope*unit_scope, + bool is_unit); std::list find_package_scopes() const; /* Attempt to set the precision to the specified value. If the @@ -4806,11 +4966,6 @@ class Design : public Definitions { // Look for defparams that never matched, and print warnings. void residual_defparams(); - // Do elaborate_sig for objects in $root scope. - void root_elaborate_sig(void); - - void root_elaborate(void); - /* This method locates a signal, starting at a given scope. The name parameter may be partially hierarchical, so this method, unlike the NetScope::find_signal method, @@ -4823,12 +4978,6 @@ class Design : public Definitions { // Tasks NetScope* find_task(NetScope*scope, const pform_name_t&name); - void add_root_task(NetScope*tscope, PTaskFunc*tf); - std::list find_roottask_scopes(void) const; - - // Find a class in the $root scope. - void add_class(netclass_t*cl, PClass*pclass); - netclass_t* find_class(perm_string name) const; // NODES void add_node(NetNode*); @@ -4842,6 +4991,7 @@ class Design : public Definitions { void add_process(NetAnalogTop*); void delete_process(NetProcTop*); bool check_proc_delay() const; + bool check_proc_synth() const; NetNet* find_discipline_reference(ivl_discipline_t dis, NetScope*scope); @@ -4856,6 +5006,12 @@ class Design : public Definitions { unsigned errors; private: + NetScope* find_scope_(NetScope*, const hname_t&name, + NetScope::TYPE type = NetScope::MODULE) const; + + NetScope* find_scope_(NetScope*, const std::list&path, + NetScope::TYPE type = NetScope::MODULE) const; + // Keep a tree of scopes. The NetScope class handles the wide // tree and per-hop searches for me. listroot_scopes_; @@ -4864,12 +5020,6 @@ class Design : public Definitions { // packages do not nest. std::mappackages_; - // Tasks in the $root scope - std::maproot_tasks_; - - // Need this for elaboration of $root scope pclass objects. - std::map class_to_pclass_; - // List the nodes in the design. NetNode*nodes_; // These are in support of the node functor iterator. @@ -4995,4 +5145,5 @@ inline unsigned Link::get_pin() const return pin_; } +#undef ENUM_UNSIGNED_INT #endif /* IVL_netlist_H */ diff --git a/netmisc.cc b/netmisc.cc index e345cb977..59da76061 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2017 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; } @@ -480,8 +523,6 @@ void indices_to_expressions(Design*des, NetScope*scope, const list&src, unsigned count, // True if the expression MUST be constant. bool need_const, - // Total words in target array - unsigned need_addr, // These are the outputs. indices_flags&flags, list&indices, list&indices_const) @@ -692,7 +733,7 @@ NetExpr* make_canonical_index(Design*des, NetScope*scope, indices_flags flags; indices_to_expressions(des, scope, loc, src, src.size(), - need_const, stype->static_dimensions().size(), + need_const, flags, indices_expr, indices_const); @@ -804,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) @@ -826,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; @@ -893,7 +940,7 @@ static NetExpr* do_elab_and_eval(Design*des, NetScope*scope, PExpr*pe, } } - // If the context_width sent is is actually the minimim width, + // If the context_width sent is is actually the minimum width, // then raise the context_width to be big enough for the // lossless expression. if (force_expand && context_width > 0) { @@ -912,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, @@ -928,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, @@ -1211,6 +1260,8 @@ const char *human_readable_op(const char op, bool unary) if (unary) type = "~|"; // NOR else type = "!=="; // Case inequality break; + case 'w': type = "==?"; break; // Wild equality + case 'W': type = "!=?"; break; // Wild inequality case 'l': type = "<<(<)"; break; // Left shifts case 'r': type = ">>"; break; // Logical right shift @@ -1406,7 +1457,7 @@ bool evaluate_index_prefix(Design*des, NetScope*scope, return false; } - prefix_indices .push_back(tmp); + prefix_indices.push_back(tmp); delete texpr; } @@ -1428,7 +1479,7 @@ NetExpr*collapse_array_exprs(Design*des, NetScope*scope, indices_flags flags; indices_to_expressions(des, scope, loc, indices, net->packed_dimensions(), - false, net->unpacked_count(), flags, exprs, exprs_const); + false, flags, exprs, exprs_const); ivl_assert(*loc, exprs.size() == net->packed_dimensions()); // Special Case: there is only 1 packed dimension, so the @@ -1616,3 +1667,38 @@ NetScope* find_method_containing_scope(const LineInfo&, NetScope*scope) return scope; } + + +/* + * Print a warning if we find a mixture of default and explicit timescale + * based delays in the design, since this is likely an error. + */ +void check_for_inconsistent_delays(NetScope*scope) +{ + static bool used_implicit_timescale = false; + static bool used_explicit_timescale = false; + static bool display_ts_dly_warning = true; + + if (scope->time_from_timescale()) + used_explicit_timescale = true; + else + used_implicit_timescale = true; + + if (display_ts_dly_warning && + used_explicit_timescale && + used_implicit_timescale) { + if (gn_system_verilog()) { + cerr << "warning: Found both default and explicit " + "timescale based delays. Use" << endl; + cerr << " : -Wtimescale to find the design " + "element(s) with no explicit" << endl; + cerr << " : timescale." << endl; + } else { + cerr << "warning: Found both default and " + "`timescale based delays. Use" << endl; + cerr << " : -Wtimescale to find the " + "module(s) with no `timescale." << endl; + } + display_ts_dly_warning = false; + } +} diff --git a/netmisc.h b/netmisc.h index 0a93ca636..af639da2b 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-2017 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); @@ -155,7 +175,7 @@ extern NetExpr*normalize_variable_slice_base(const list&indices, NetExpr * * index values in the form [<>][<>].... */ template struct __IndicesManip { - inline __IndicesManip(const std::list&v) : val(v) { } + explicit inline __IndicesManip(const std::list&v) : val(v) { } const std::list&val; }; template inline __IndicesManip as_indices(const std::list&indices) @@ -181,8 +201,6 @@ extern void indices_to_expressions(Design*des, NetScope*scope, const list&src, unsigned count, // True if the expression MUST be constant. bool need_const, - // Total array size, for sizing expressions - unsigned need_addr, // These are the outputs. indices_flags&flags, list&indices,list&indices_const); @@ -260,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, @@ -299,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, @@ -358,10 +378,6 @@ const char *human_readable_op(const char op, bool unary = false); enum const_bool { C_NON, C_0, C_1, C_X }; const_bool const_logical(const NetExpr*expr); -extern bool dly_used_no_timescale; -extern bool dly_used_timescale; -extern bool display_ts_dly_warning; - /* * When scaling a real value to a time we need to do some standard * processing. @@ -389,4 +405,10 @@ extern void assign_unpacked_with_bufz(Design*des, NetScope*scope, extern NetPartSelect* detect_partselect_lval(Link&pin); +/* + * Print a warning if we find a mixture of default and explicit timescale + * based delays in the design, since this is likely an error. + */ +extern void check_for_inconsistent_delays(NetScope*scope); + #endif /* IVL_netmisc_H */ diff --git a/nettypes.cc b/nettypes.cc index 4a01d4d8a..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 @@ -141,19 +141,18 @@ bool prefix_to_slice(const std::vector&dims, // Now similarly go through the prefix numbers, working // through the dimensions until we run out. Accumulate a - // growing slice width (acc_wid) that is used to caculate the + // growing slice width (acc_wid) that is used to calculate the // growing offset (acc_off). list::const_iterator icur = prefix.end(); 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/nodangle.cc b/nodangle.cc index 3dcb32391..1e7551e27 100644 --- a/nodangle.cc +++ b/nodangle.cc @@ -122,10 +122,63 @@ void nodangle_f::event(Design*, NetEvent*ev) } } +static bool floating_net_tested(NetNet*sig) +{ + static set tested_set; + + pair< set::iterator, bool > cur = tested_set.insert(sig); + return !cur.second; +} + +static void check_is_floating(NetNet*sig) +{ + // Some signal types are implicitly driven if nothing else. + if (sig->type() == NetNet::SUPPLY0) return; + if (sig->type() == NetNet::SUPPLY1) return; + if (sig->type() == NetNet::TRI0) return; + if (sig->type() == NetNet::TRI1) return; + if (sig->type() == NetNet::IMPLICIT_REG) return; + if (sig->type() == NetNet::REG) return ; + + // Assignments drive a signal. + if (sig->peek_lref() > 0) return; + + for (unsigned idx = 0 ; idx < sig->pin_count() ; idx += 1) { + if (sig->pin(idx).get_dir() == Link::OUTPUT) + continue; + + if (sig->pin(idx).nexus()->drivers_present()) + continue; + + if (sig->port_type() == PortType::NOT_A_PORT && sig->pin_count()==1) { + cerr << sig->get_fileline() << ": warning: " + << "Signal " << scope_path(sig->scope()) + << "." << sig->name() + << " has no drivers." << endl; + } else if (sig->port_type()==PortType::NOT_A_PORT) { + cerr << sig->get_fileline() << ": warning: " + << "Signal " << scope_path(sig->scope()) + << "." << sig->name() + << "[" << idx << "]" + << " has no drivers." << endl; + } else { + cerr << sig->get_fileline() << ": warning: " + << "Port " << sig->name() + << " of " << scope_path(sig->scope()) + << " has no drivers." << endl; + } + } + +} + void nodangle_f::signal(Design*, NetNet*sig) { if (scomplete) return; + if (warn_floating_nets && !sig->local_flag() && !floating_net_tested(sig)) { + check_is_floating(sig); + } + /* Cannot delete signals referenced in an expression or an l-value. */ if (sig->get_refs() > 0) 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 948437000..c55dac843 100644 --- a/parse.y +++ b/parse.y @@ -1,7 +1,7 @@ %{ /* - * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2017 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -35,9 +35,6 @@ class PSpecPath; extern void lex_end_table(); -bool have_timeunit_decl = false; -bool have_timeprec_decl = false; - static list* param_active_range = 0; static bool param_active_signed = false; static ivl_variable_type_t param_active_type = IVL_VT_LOGIC; @@ -65,6 +62,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 +132,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 +371,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 +453,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 @@ -462,7 +465,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %token BASED_NUMBER DEC_NUMBER UNBASED_NUMBER %token REALTIME %token K_PLUS_EQ K_MINUS_EQ K_INCR K_DECR -%token K_LE K_GE K_EG K_EQ K_NE K_CEQ K_CNE K_LP K_LS K_RS K_RSS K_SG +%token K_LE K_GE K_EG K_EQ K_NE K_CEQ K_CNE K_WEQ K_WNE K_LP K_LS K_RS K_RSS K_SG /* K_CONTRIBUTE is <+, the contribution assign. */ %token K_CONTRIBUTE %token K_PO_POS K_PO_NEG K_POW @@ -526,8 +529,6 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %token K_tagged K_this K_throughout K_timeprecision K_timeunit K_type %token K_typedef K_union K_unique K_var K_virtual K_void K_wait_order %token K_wildcard K_with K_within - /* Fake tokens that are passed once we have an initial token. */ -%token K_timeprecision_check K_timeunit_check /* The new tokens from 1800-2009. */ %token K_accept_on K_checker K_endchecker K_eventually K_global K_implies @@ -560,7 +561,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 +577,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 @@ -592,7 +593,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type function_item function_item_list function_item_list_opt %type task_item task_item_list task_item_list_opt -%type tf_port_declaration tf_port_item tf_port_list tf_port_list_opt +%type tf_port_declaration tf_port_item tf_port_item_list tf_port_list tf_port_list_opt %type modport_simple_port port_name parameter_value_byname %type port_name_list parameter_value_byname_list @@ -607,7 +608,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 +637,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 @@ -648,6 +650,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type statement statement_item statement_or_null %type compressed_statement %type loop_statement for_step jump_statement +%type procedural_assertion_statement %type statement_or_null_list statement_or_null_list_opt %type analog_statement @@ -664,6 +667,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 @@ -673,7 +678,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %left '|' %left '^' K_NXOR K_NOR %left '&' K_NAND -%left K_EQ K_NE K_CEQ K_CNE +%left K_EQ K_NE K_CEQ K_CNE K_WEQ K_WNE %left K_GE K_LE '<' '>' %left K_LS K_RS K_RSS %left '+' '-' @@ -682,7 +687,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %left UNARY_PREC -/* to resolve dangling else ambiguity. */ + /* to resolve dangling else ambiguity. */ %nonassoc less_than_K_else %nonassoc K_else @@ -690,12 +695,22 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %nonassoc '(' %nonassoc K_exclude + /* to resolve timeunits declaration/redeclaration ambiguity */ +%nonassoc no_timeunits_declaration +%nonassoc one_timeunits_declaration +%nonassoc K_timeunit K_timeprecision + %% /* IEEE1800-2005: A.1.2 */ /* source_text ::= [ timeunits_declaration ] { description } */ -source_text : description_list | ; +source_text + : timeunits_declaration_opt + { pform_set_scope_timescale(yyloc); } + description_list + | /* empty */ + ; assertion_item /* IEEE1800-2012: A.6.10 */ : concurrent_assertion_item @@ -723,17 +738,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; } } ; @@ -1044,7 +1059,11 @@ data_type /* IEEE1800-2005: A.2.2.1 */ $$ = tmp; } | struct_data_type - { $$ = $1; } + { if (!$1->packed_flag) { + yyerror(@1, "sorry: Unpacked structs not supported."); + } + $$ = $1; + } | enum_data_type { $$ = $1; } | atom2_type signed_unsigned_opt @@ -1062,7 +1081,7 @@ data_type /* IEEE1800-2005: A.2.2.1 */ | K_time { list*pd = make_range_from_width(64); vector_type_t*tmp = new vector_type_t(IVL_VT_LOGIC, false, pd); - tmp->reg_flag = true; + tmp->reg_flag = !gn_system_verilog(); $$ = tmp; } | TYPE_IDENTIFIER dimensions_opt @@ -1123,11 +1142,7 @@ data_type_or_implicit_or_void } ; - /* NOTE 1: We pull the "timeunits_declaration" into the description - here in order to be a little more flexible with where timeunits - statements may go. This may be a bad idea, but it is legacy now. */ - - /* NOTE 2: The "module" rule of the description combines the + /* NOTE: The "module" rule of the description combines the module_declaration, program_declaration, and interface_declaration rules from the standard description. */ @@ -1189,7 +1204,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); } @@ -1210,7 +1225,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; @@ -1218,7 +1233,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); } @@ -1254,7 +1269,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(); @@ -1360,6 +1375,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 */ @@ -1403,6 +1428,7 @@ loop_statement /* IEEE1800-2005: A.6.8 */ vectortmp_for_list (1); tmp_for_list[0] = tmp_for; PBlock*tmp_blk = current_block_stack.top(); + current_block_stack.pop(); tmp_blk->set_statement(tmp_for_list); $$ = tmp_blk; delete[]$4; @@ -1453,6 +1479,7 @@ loop_statement /* IEEE1800-2005: A.6.8 */ vectortmp_for_list(1); tmp_for_list[0] = tmp_for; PBlock*tmp_blk = current_block_stack.top(); + current_block_stack.pop(); tmp_blk->set_statement(tmp_for_list); $$ = tmp_blk; } @@ -1699,20 +1726,21 @@ 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); } + timeunits_declaration_opt + { pform_set_scope_timescale(@1); } 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 ($10) { + if (strcmp($3,$10) != 0) { + yyerror(@10, "error: End label doesn't match package name"); } - delete[]$7; + delete[]$10; } - delete[]$2; + delete[]$3; } ; @@ -1790,9 +1818,24 @@ property_expr /* IEEE1800-2012 A.2.10 */ : expression ; +procedural_assertion_statement /* IEEE1800-2012 A.6.10 */ + : K_assert '(' expression ')' statement %prec less_than_K_else + { yyerror(@1, "sorry: Simple immediate assertion statements not implemented."); + $$ = 0; + } + | K_assert '(' expression ')' K_else statement + { yyerror(@1, "sorry: Simple immediate assertion statements not implemented."); + $$ = 0; + } + | K_assert '(' expression ')' statement K_else statement + { yyerror(@1, "sorry: Simple immediate assertion statements not implemented."); + $$ = 0; + } + ; + /* The property_qualifier rule is as literally described in the LRM, but the use is usually as { property_qualifier }, which is - implemented bt the property_qualifier_opt rule below. */ + implemented by the property_qualifier_opt rule below. */ property_qualifier /* IEEE1800-2005 A.1.8 */ : class_item_qualifier @@ -1891,7 +1934,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); } @@ -1904,7 +1947,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; } @@ -1927,7 +1970,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); } @@ -1961,7 +2004,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); } @@ -1978,7 +2021,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; } @@ -2001,9 +2044,12 @@ 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); + if (current_task) { + pform_pop_scope(); + current_task = 0; + } } endlabel_opt { // Last step: check any closing name. This is done late so @@ -2063,6 +2109,15 @@ tf_port_declaration /* IEEE1800-2005: A.2.7 */ $$ = tmp; } + + /* Ports can be string. */ + + | port_direction K_string list_of_identifiers ';' + { vector*tmp = pform_make_task_ports(@1, $1, IVL_VT_STRING, true, + 0, $3); + $$ = tmp; + } + ; @@ -2078,10 +2133,16 @@ tf_port_item /* IEEE1800-2005: A.2.7 */ : port_direction_opt data_type_or_implicit IDENTIFIER dimensions_opt tf_port_item_expr_opt { vector*tmp; - NetNet::PortType use_port_type = $1==NetNet::PIMPLICIT? NetNet::PINPUT : $1; + NetNet::PortType use_port_type = $1; + if ((use_port_type == NetNet::PIMPLICIT) && (gn_system_verilog() || ($2 == 0))) + use_port_type = port_declaration_context.port_type; perm_string name = lex_strings.make($3); list* ilist = list_from_identifier($3); + if (use_port_type == NetNet::PIMPLICIT) { + yyerror(@1, "error: missing task/function port direction."); + use_port_type = NetNet::PINPUT; // for error recovery + } if (($2 == 0) && ($1==NetNet::PIMPLICIT)) { // Detect special case this is an undecorated // identifier and we need to get the declaration from @@ -2093,11 +2154,10 @@ tf_port_item /* IEEE1800-2005: A.2.7 */ port_declaration_context.data_type, ilist); - } else { // Otherwise, the decorations for this identifier // indicate the type. Save the type for any right - // context thta may come later. + // context that may come later. port_declaration_context.port_type = use_port_type; if ($2 == 0) { $2 = new vector_type_t(IVL_VT_LOGIC, false, 0); @@ -2115,7 +2175,7 @@ tf_port_item /* IEEE1800-2005: A.2.7 */ assert(tmp->size()==1); tmp->front().defe = $5; } - } + } /* Rules to match error cases... */ @@ -2129,13 +2189,26 @@ tf_port_item /* IEEE1800-2005: A.2.7 */ /* This rule matches the [ = ] part of the tf_port_item rules. */ tf_port_item_expr_opt - : '=' expression { $$ = $2; } - | { $$ = 0; } + : '=' expression + { if (! gn_system_verilog()) { + yyerror(@1, "error: Task/function default arguments require " + "SystemVerilog."); + } + $$ = $2; + } + | { $$ = 0; } ; tf_port_list /* IEEE1800-2005: A.2.7 */ + : { port_declaration_context.port_type = gn_system_verilog() ? NetNet::PINPUT : NetNet::PIMPLICIT; + port_declaration_context.data_type = 0; + } + tf_port_item_list + { $$ = $2; } + ; - : tf_port_list ',' tf_port_item +tf_port_item_list + : tf_port_item_list ',' tf_port_item { vector*tmp; if ($1 && $3) { size_t s1 = $1->size(); @@ -2161,30 +2234,34 @@ tf_port_list /* IEEE1800-2005: A.2.7 */ { yyerror(@2, "error: Syntax error in task/function port declaration."); $$ = $3; } - | tf_port_list ',' + | tf_port_item_list ',' { yyerror(@2, "error: NULL port declarations are not allowed."); $$ = $1; } - | tf_port_list ';' + | tf_port_item_list ';' { yyerror(@2, "error: ';' is an invalid port declaration separator."); $$ = $1; } ; - /* NOTE: Icarus Verilog is a little more generous with the - timeunits declarations by allowing them to happen in multiple - places in the file. So the rule is adjusted to be invoked by the - "description" rule. This theoretically allows files to be - concatenated together and still compile. */ timeunits_declaration /* IEEE1800-2005: A.1.2 */ : K_timeunit TIME_LITERAL ';' - { pform_set_timeunit($2, false, false); } + { pform_set_timeunit($2, allow_timeunit_decl); } | K_timeunit TIME_LITERAL '/' TIME_LITERAL ';' - { pform_set_timeunit($2, false, false); - pform_set_timeprecision($4, false, false); + { bool initial_decl = allow_timeunit_decl && allow_timeprec_decl; + pform_set_timeunit($2, initial_decl); + pform_set_timeprec($4, initial_decl); } | K_timeprecision TIME_LITERAL ';' - { pform_set_timeprecision($2, false, false); } + { pform_set_timeprec($2, allow_timeprec_decl); } + ; + + /* Allow zero, one, or two declarations. The second declaration might + be a repeat declaration, but the pform functions take care of that. */ +timeunits_declaration_opt + : /* empty */ %prec no_timeunits_declaration + | timeunits_declaration %prec one_timeunits_declaration + | timeunits_declaration timeunits_declaration ; value_range /* IEEE1800-2005: A.8.3 */ @@ -2203,7 +2280,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; @@ -2233,6 +2310,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. */ @@ -2307,10 +2397,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); } @@ -3023,7 +3123,7 @@ branch_probe_expression ; expression - : expr_primary + : expr_primary_or_typename { $$ = $1; } | inc_or_dec_expression { $$ = $1; } @@ -3196,6 +3296,11 @@ expression FILE_NAME(tmp, @2); $$ = tmp; } + | expression K_WEQ attribute_list_opt expression + { PEBinary*tmp = new PEBComp('w', $1, $4); + FILE_NAME(tmp, @2); + $$ = tmp; + } | expression K_LE attribute_list_opt expression { PEBinary*tmp = new PEBComp('L', $1, $4); FILE_NAME(tmp, @2); @@ -3216,6 +3321,11 @@ expression FILE_NAME(tmp, @2); $$ = tmp; } + | expression K_WNE attribute_list_opt expression + { PEBinary*tmp = new PEBComp('W', $1, $4); + FILE_NAME(tmp, @2); + $$ = tmp; + } | expression K_LOR attribute_list_opt expression { PEBinary*tmp = new PEBLogic('o', $1, $4); FILE_NAME(tmp, @2); @@ -3318,6 +3428,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); @@ -3335,6 +3459,22 @@ expr_primary FILE_NAME(tmp, @1); $$ = tmp; } + | TIME_LITERAL + { int unit; + + based_size = 0; + $$ = 0; + if ($1 == 0 || !get_time_unit($1, unit)) + yyerror(@1, "internal error: delay."); + else { + double p = pow(10.0, (double)(unit - pform_get_timeunit())); + double time = atof($1) * p; + + verireal *v = new verireal(time); + $$ = new PEFNumber(v); + FILE_NAME($$, @1); + } + } | SYSTEM_IDENTIFIER { perm_string tn = lex_strings.make($1); PECallFunction*tmp = new PECallFunction(tn); @@ -3343,15 +3483,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 */ @@ -3369,7 +3500,7 @@ expr_primary /* An identifier followed by an expression list in parentheses is a function call. If a system identifier, then a system function - call. It can also be a call to a class method (functino). */ + call. It can also be a call to a class method (function). */ | hierarchy_identifier '(' expression_list_with_nuls ')' { list*expr_list = $3; @@ -3655,12 +3786,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."); @@ -3976,14 +4106,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); } ; @@ -4143,6 +4280,9 @@ port_declaration use_type = NetNet::IMPLICIT_REG; } else if (dynamic_cast ($4)) { use_type = NetNet::IMPLICIT_REG; + } else if (enum_type_t*etype = dynamic_cast ($4)) { + if(etype->base_type == IVL_VT_LOGIC) + use_type = NetNet::IMPLICIT_REG; } } ptmp = pform_module_port_reference(name, @2.text, @2.first_line); @@ -4189,7 +4329,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; @@ -4297,64 +4437,20 @@ cont_assign_list { $$ = $1; } ; - /* We allow zero, one or two unique declarations. */ -local_timeunit_prec_decl_opt - : /* Empty */ - | K_timeunit TIME_LITERAL '/' TIME_LITERAL ';' - { pform_set_timeunit($2, true, false); - have_timeunit_decl = true; - pform_set_timeprecision($4, true, false); - have_timeprec_decl = true; - } - | local_timeunit_prec_decl - | local_timeunit_prec_decl local_timeunit_prec_decl2 - ; - - /* By setting the appropriate have_time???_decl we allow only - one declaration of each type in this module. */ -local_timeunit_prec_decl - : K_timeunit TIME_LITERAL ';' - { pform_set_timeunit($2, true, false); - have_timeunit_decl = true; - } - | K_timeprecision TIME_LITERAL ';' - { pform_set_timeprecision($2, true, false); - have_timeprec_decl = true; - } - ; -local_timeunit_prec_decl2 - : K_timeunit TIME_LITERAL ';' - { pform_set_timeunit($2, true, false); - have_timeunit_decl = true; - } - | K_timeprecision TIME_LITERAL ';' - { pform_set_timeprecision($2, true, false); - have_timeprec_decl = true; - } - /* As the second item this form is always a check. */ - | K_timeunit TIME_LITERAL '/' TIME_LITERAL ';' - { pform_set_timeunit($2, true, true); - pform_set_timeprecision($4, true, true); - } - ; - /* This is the global structure of a module. A module is a start section, with optional ports, then an optional list of module 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); } - local_timeunit_prec_decl_opt - { have_timeunit_decl = true; // Every thing past here is - have_timeprec_decl = true; // a check! - pform_check_timeunit_prec(); - } + { pform_module_set_ports($8); } + timeunits_declaration_opt + { pform_set_scope_timescale(@2); } module_item_list_opt module_end { Module::UCDriveType ucd; @@ -4375,24 +4471,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); - have_timeunit_decl = false; // We will allow decls again. - have_timeprec_decl = false; + pform_endmodule($4, in_celldefine, ucd); } endlabel_opt { // Last step: check any closing name. This is done late so @@ -4400,19 +4494,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: @@ -4423,9 +4517,9 @@ module yyerror(@8, "error: Module end labels require " "SystemVerilog."); } - delete[]$16; + delete[]$17; } - delete[]$3; + delete[]$4; } ; @@ -4529,8 +4623,8 @@ module_item } pform_makewire(@2, $4, str_strength, $5, $2, data_type); if ($1) { - yyerror(@2, "sorry: Attributes not supported " - "on net declaration assignments."); + yywarn(@2, "Attributes are not supported on net declaration " + "assignments and will be discarded."); delete $1; } } @@ -4546,8 +4640,8 @@ module_item } pform_makewire(@2, 0, $4, $5, $2, data_type); if ($1) { - yyerror(@2, "sorry: Attributes not supported " - "on net declaration assignments."); + yywarn(@2, "Attributes are not supported on net declaration " + "assignments and will be discarded."); delete $1; } } @@ -4556,8 +4650,8 @@ module_item { real_type_t*data_type = new real_type_t(real_type_t::REAL); pform_makewire(@2, 0, str_strength, $3, NetNet::WIRE, data_type); if ($1) { - yyerror(@2, "sorry: Attributes not supported " - "on net declaration assignments."); + yywarn(@2, "Attributes are not supported on net declaration " + "assignments and will be discarded."); delete $1; } } @@ -4568,58 +4662,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; } @@ -4702,9 +4840,8 @@ module_item | attribute_list_opt IDENTIFIER parameter_value_opt gate_instance_list ';' { perm_string tmp1 = lex_strings.make($2); - pform_make_modgates(@2, tmp1, $3, $4); + pform_make_modgates(@2, tmp1, $3, $4, $1); delete[]$2; - if ($1) delete $1; } | attribute_list_opt @@ -4727,6 +4864,18 @@ module_item { PProcess*tmp = pform_make_behavior(IVL_PR_ALWAYS, $3, $1); FILE_NAME(tmp, @2); } + | attribute_list_opt K_always_comb statement_item + { PProcess*tmp = pform_make_behavior(IVL_PR_ALWAYS_COMB, $3, $1); + FILE_NAME(tmp, @2); + } + | attribute_list_opt K_always_ff statement_item + { PProcess*tmp = pform_make_behavior(IVL_PR_ALWAYS_FF, $3, $1); + FILE_NAME(tmp, @2); + } + | attribute_list_opt K_always_latch statement_item + { PProcess*tmp = pform_make_behavior(IVL_PR_ALWAYS_LATCH, $3, $1); + FILE_NAME(tmp, @2); + } | attribute_list_opt K_initial statement_item { PProcess*tmp = pform_make_behavior(IVL_PR_INITIAL, $3, $1); FILE_NAME(tmp, @2); @@ -4741,6 +4890,8 @@ module_item | attribute_list_opt assertion_item + | timeunits_declaration + | class_declaration | task_declaration @@ -4753,7 +4904,7 @@ module_item generate/endgenerate regions do not nest. Generate schemes nest, but generate regions do not. */ - | K_generate module_item_list_opt K_endgenerate + | K_generate generate_item_list_opt K_endgenerate { // Test for bad nesting. I understand it, but it is illegal. if (pform_parent_generate()) { cerr << @1 << ": error: Generate/endgenerate regions cannot nest." << endl; @@ -4790,25 +4941,6 @@ module_item K_endcase { pform_endgenerate(); } - /* Handle some anachronistic syntax cases. */ - | K_generate K_begin module_item_list_opt K_end K_endgenerate - { /* Detect and warn about anachronistic begin/end use */ - if (generation_flag > GN_VER2001 && warn_anachronisms) { - warn_count += 1; - cerr << @2 << ": warning: Anachronistic use of begin/end to surround generate schemes." << endl; - } - } - | K_generate K_begin ':' IDENTIFIER { - pform_start_generate_nblock(@2, $4); - } module_item_list_opt K_end K_endgenerate - { /* Detect and warn about anachronistic named begin/end use */ - if (generation_flag > GN_VER2001 && warn_anachronisms) { - warn_count += 1; - cerr << @2 << ": warning: Anachronistic use of named begin/end to surround generate schemes." << endl; - } - pform_endgenerate(); - } - | modport_declaration | package_import_declaration @@ -4883,16 +5015,18 @@ module_item | KK_attribute '(' error ')' ';' { yyerror(@1, "error: Malformed $attribute parameter list."); } - | K_timeunit_check TIME_LITERAL ';' - { pform_set_timeunit($2, true, true); } - | K_timeunit_check TIME_LITERAL '/' TIME_LITERAL ';' - { pform_set_timeunit($2, true, true); - pform_set_timeprecision($4, true, true); - } - | K_timeprecision_check TIME_LITERAL ';' - { pform_set_timeprecision($2, true, true); } ; +module_item_list + : module_item_list module_item + | module_item + ; + +module_item_list_opt + : module_item_list + | + ; + generate_if : K_if '(' expression ')' { pform_start_generate_if(@1, $3); } ; generate_case_items @@ -4907,15 +5041,37 @@ generate_case_item { pform_endgenerate(); } ; -module_item_list - : module_item_list module_item - | module_item - ; +generate_item + : module_item + /* Handle some anachronistic syntax cases. */ + | K_begin generate_item_list_opt K_end + { /* Detect and warn about anachronistic begin/end use */ + if (generation_flag > GN_VER2001 && warn_anachronisms) { + warn_count += 1; + cerr << @1 << ": warning: Anachronistic use of begin/end to surround generate schemes." << endl; + } + } + | K_begin ':' IDENTIFIER { + pform_start_generate_nblock(@1, $3); + } generate_item_list_opt K_end + { /* Detect and warn about anachronistic named begin/end use */ + if (generation_flag > GN_VER2001 && warn_anachronisms) { + warn_count += 1; + cerr << @1 << ": warning: Anachronistic use of named begin/end to surround generate schemes." << endl; + } + pform_endgenerate(); + } + ; -module_item_list_opt - : module_item_list - | - ; +generate_item_list + : generate_item_list generate_item + | generate_item + ; + +generate_item_list_opt + : generate_item_list + | + ; /* A generate block is the thing within a generate scheme. It may be a single module item, an anonymous block of module items, or a @@ -4924,24 +5080,24 @@ module_item_list_opt only need to take note here of the scope name, if any. */ generate_block - : module_item - | K_begin module_item_list_opt K_end - | K_begin ':' IDENTIFIER module_item_list_opt K_end endlabel_opt - { pform_generate_block_name($3); - if ($6) { - if (strcmp($3,$6) != 0) { - yyerror(@6, "error: End label doesn't match " - "begin name"); - } - if (! gn_system_verilog()) { - yyerror(@6, "error: Begin end labels require " - "SystemVerilog."); - } - delete[]$6; - } - delete[]$3; - } - ; + : module_item + | K_begin generate_item_list_opt K_end + | K_begin ':' IDENTIFIER generate_item_list_opt K_end endlabel_opt + { pform_generate_block_name($3); + if ($6) { + if (strcmp($3,$6) != 0) { + yyerror(@6, "error: End label doesn't match " + "begin name"); + } + if (! gn_system_verilog()) { + yyerror(@6, "error: Begin end labels require " + "SystemVerilog."); + } + delete[]$6; + } + delete[]$3; + } + ; generate_block_opt : generate_block | ';' ; @@ -5006,10 +5162,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; @@ -5039,6 +5191,11 @@ param_type param_active_signed = true; param_active_type = IVL_VT_BOOL; } + | TYPE_IDENTIFIER + { pform_set_param_from_type(@1, $1.type, $1.text, param_active_range, + param_active_signed, param_active_type); + delete[]$1.text; + } ; /* parameter and localparam assignment lists are broken into @@ -5435,11 +5592,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; } ; @@ -5692,7 +5855,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; @@ -5704,7 +5883,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; @@ -6052,6 +6247,8 @@ statement_item /* This is roughly statement_item in the LRM */ $$ = tmp; } + | procedural_assertion_statement { $$ = $1; } + | loop_statement { $$ = $1; } | jump_statement { $$ = $1; } @@ -6095,7 +6292,7 @@ statement_item /* This is roughly statement_item in the LRM */ { yyerror(@1, "error: Malformed conditional expression."); $$ = $5; } - /* SytemVerilog adds the compressed_statement */ + /* SystemVerilog adds the compressed_statement */ | compressed_statement ';' { $$ = $1; } @@ -6228,7 +6425,7 @@ statement_item /* This is roughly statement_item in the LRM */ $$ = tmp; } | K_wait K_fork ';' - { PEventStatement*tmp = new PEventStatement(0); + { PEventStatement*tmp = new PEventStatement((PEEvent*)0); FILE_NAME(tmp,@1); $$ = tmp; } @@ -6735,7 +6932,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/parse_api.h b/parse_api.h index a698c3bee..1b76b767b 100644 --- a/parse_api.h +++ b/parse_api.h @@ -1,7 +1,7 @@ #ifndef IVL_parse_api_H #define IVL_parse_api_H /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2017 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 @@ -42,28 +42,21 @@ struct enum_type_t; */ extern std::map pform_modules; extern std::map pform_primitives; -extern std::map pform_typedefs; -extern std::set pform_enum_sets; -extern std::map pform_tasks; -extern std::map pform_classes; +extern std::vector pform_units; extern std::map pform_packages; extern void pform_dump(std::ostream&out, const PClass*pac); extern void pform_dump(std::ostream&out, const PPackage*pac); extern void pform_dump(std::ostream&out, const PTaskFunc*tf); -extern void elaborate_rootscope_enumerations(Design*des); -extern void elaborate_rootscope_classes(Design*des); -extern void elaborate_rootscope_tasks(Design*des); - /* - * This code actually invokes the parser to make modules. The first - * parameter is the name of the file that is to be parsed. The - * optional second parameter is the opened descriptor for the file. If - * the descriptor is 0 (or skipped) then the function will attempt to - * open the file on its own. + * This code actually invokes the parser to make modules. If the path + * parameter is "-", the parser reads from stdin, otherwise it attempts + * to open and read the specified file. When reading from a file, if + * the ivlpp_string variable is not set to null, the file will be piped + * through the command specified by ivlpp_string before being parsed. */ -extern int pform_parse(const char*path, FILE*file =0); +extern int pform_parse(const char*path); extern string vl_file; diff --git a/parse_misc.h b/parse_misc.h index ddee21bf7..d02902013 100644 --- a/parse_misc.h +++ b/parse_misc.h @@ -1,7 +1,7 @@ #ifndef IVL_parse_misc_H #define IVL_parse_misc_H /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2017 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 @@ -71,12 +71,6 @@ extern unsigned long based_size; extern bool in_celldefine; enum UCDriveType { UCD_NONE, UCD_PULL0, UCD_PULL1 }; extern UCDriveType uc_drive; -/* - * Flags to control if we are declaring or checking a timeunit or - * timeprecision statement. - */ -extern bool have_timeunit_decl; -extern bool have_timeprec_decl; /* * The parser signals back to the lexor that the next identifier diff --git a/pform.cc b/pform.cc index 79cc05cf5..d93d48659 100644 --- a/pform.cc +++ b/pform.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2017 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -40,6 +40,7 @@ # include # include # include +# include # include "ivl_assert.h" # include "ivl_alloc.h" @@ -62,20 +63,19 @@ map pform_modules; map pform_primitives; /* - * typedefs in the $root scope go here. + * The pform_units is a list of the SystemVerilog compilation unit scopes. + * The current compilation unit is the last element in the list. All items + * declared or defined at the top level (outside any design element) are + * added to the current compilation unit scope. */ -mappform_typedefs; -setpform_enum_sets; +vector pform_units; -/* - * Class definitions in the $root scope go here. - */ -map pform_classes; - -/* - * Task and function definitions in the $root scope go here. - */ -map pform_tasks; +static bool is_compilation_unit(LexicalScope*scope) +{ + // A compilation unit is the only scope that doesn't have a parent. + assert(scope); + return scope->parent_scope() == 0; +} std::string vlltype::get_fileline() const { @@ -86,6 +86,42 @@ std::string vlltype::get_fileline() const } +static bool is_hex_digit_str(const char *str) +{ + while (*str) { + if (!isxdigit(*str)) return false; + str++; + } + return true; +} + +static bool is_dec_digit_str(const char *str) +{ + while (*str) { + if (!isdigit(*str)) return false; + str++; + } + return true; +} + +static bool is_oct_digit_str(const char *str) +{ + while (*str) { + if (*str < '0' || *str > '7') return false; + str++; + } + return true; +} + +static bool is_bin_digit_str(const char *str) +{ + while (*str) { + if (*str != '0' && *str != '1') return false; + str++; + } + return true; +} + /* * Parse configuration file with format =, where key * is the hierarchical name of a valid parameter name, and value @@ -152,90 +188,137 @@ void parm_to_defparam_list(const string¶m) ptr = strchr(nkey, '.'); } name.push_back(name_component_t(lex_strings.make(nkey))); + free(key); // Resolve value to PExpr class. Should support all kind of constant // format including based number, dec number, real number and string. - if (*value == '"') { // string type - char *buf = strdup (value); - char *buf_ptr = buf+1; - // Parse until another '"' or '\0' - while (*buf_ptr != '"' && *buf_ptr != '\0') { - buf_ptr++; - // Check for escape, especially '\"', which does not mean the - // end of string. - if (*buf_ptr == '\\' && *(buf_ptr+1) != '\0') - buf_ptr += 2; - } - if (*buf_ptr == '\0') // String end without '"' - cerr << ": error: missing close quote of string for defparam: " << name << endl; - else if (*(buf_ptr+1) != 0) { // '"' appears within string with no escape - cerr << buf_ptr << endl; - cerr << ": error: \'\"\' appears within string value for defparam: " << name - << ". Ignore characters after \'\"\'" << endl; - } - *buf_ptr = '\0'; - buf_ptr = buf+1; - // Remember to use 'new' to allocate string for PEString - // because 'delete' is used by its destructor. - char *nchar = strcpy(new char [strlen(buf_ptr)+1], buf_ptr); - PExpr* ndec = new PEString(nchar); + // Is it a string? + if (*value == '"') { + char *buf = strdup (value); + char *buf_ptr = buf+1; + // Parse until another '"' or '\0' + while (*buf_ptr != '"' && *buf_ptr != '\0') { + buf_ptr++; + // Check for escape, especially '\"', which does not mean the + // end of string. + if (*buf_ptr == '\\' && *(buf_ptr+1) != '\0') + buf_ptr += 2; + } + if (*buf_ptr == '\0') // String end without '"' + cerr << ": error: missing close quote of string for defparam: " << name << endl; + else if (*(buf_ptr+1) != 0) { // '"' appears within string with no escape + cerr << buf_ptr << endl; + cerr << ": error: \'\"\' appears within string value for defparam: " << name + << ". Ignore characters after \'\"\'" << endl; + } + + *buf_ptr = '\0'; + buf_ptr = buf+1; + // Remember to use 'new' to allocate string for PEString + // because 'delete' is used by its destructor. + char *nchar = strcpy(new char [strlen(buf_ptr)+1], buf_ptr); + PExpr* ndec = new PEString(nchar); Module::user_defparms.push_back( make_pair(name, ndec) ); - free(buf); + free(buf); + free(value); + return; } - else { // number type - char *num = strchr(value, '\''); - if (num != 0) { - verinum *val; - // BASED_NUMBER, something like - scope.parameter='b11 - // make sure to check 'h' first because 'b'&'d' may be included - // in hex format - if (strchr(num, 'h') || strchr(num, 'H')) - val = make_unsized_hex(num); - else if (strchr(num, 'd') || strchr(num, 'D')) - if (strchr(num, 'x') || strchr(num, 'X') || strchr(num, 'z') || strchr(num, 'Z')) - val = make_undef_highz_dec(num); - else - val = make_unsized_dec(num); - else if (strchr(num, 'b') || strchr(num, 'B')) { - val = make_unsized_binary(num); - } - else if (strchr(num, 'o') || strchr(num, 'O')) - val = make_unsized_octal(num); - else { - cerr << ": error: value specify error for defparam: " << name << endl; - free(key); - free(value); - return; - } - // BASED_NUMBER with size, something like - scope.parameter=2'b11 - if (num != value) { - *num = 0; - verinum *siz = make_unsized_dec(value); - val = pform_verinum_with_size(siz, val, "", 0); - } - - PExpr* ndec = new PENumber(val); - Module::user_defparms.push_back( make_pair(name, ndec) ); - - } - else { - // REALTIME, something like - scope.parameter=1.22 or scope.parameter=1e2 - if (strchr(value, '.') || strchr(value, 'e') || strchr(value, 'E')) { - verireal *val = new verireal(value); - PExpr* nreal = new PEFNumber(val); - Module::user_defparms.push_back( make_pair(name, nreal) ); - } - else { - // DEC_NUMBER, something like - scope.parameter=3 - verinum *val = make_unsized_dec(value); - PExpr* ndec = new PENumber(val); - Module::user_defparms.push_back( make_pair(name, ndec) ); - } - } + // Is it a based number? + char *num = strchr(value, '\''); + if (num != 0) { + verinum *val; + const char *base = num + 1; + if (*base == 's' || *base == 'S') + base++; + switch (*base) { + case 'h': + case 'H': + if (is_hex_digit_str(base+1)) { + val = make_unsized_hex(num); + } else { + cerr << ": error: invalid digit in hex value specified for defparam: " << name << endl; + free(value); + return; + } + break; + case 'd': + case 'D': + if (is_dec_digit_str(base+1)) { + val = make_unsized_dec(num); + } else { + cerr << ": error: invalid digit in decimal value specified for defparam: " << name << endl; + free(value); + return; + } + break; + case 'o': + case 'O': + if (is_oct_digit_str(base+1)) { + val = make_unsized_octal(num); + } else { + cerr << ": error: invalid digit in octal value specified for defparam: " << name << endl; + free(value); + return; + } + break; + case 'b': + case 'B': + if (is_bin_digit_str(base+1)) { + val = make_unsized_binary(num); + } else { + cerr << ": error: invalid digit in binary value specified for defparam: " << name << endl; + free(value); + return; + } + break; + default: + cerr << ": error: invalid numeric base specified for defparam: " << name << endl; + free(value); + return; + } + if (num != value) { // based number with size + *num = 0; + if (is_dec_digit_str(value)) { + verinum *siz = make_unsized_dec(value); + val = pform_verinum_with_size(siz, val, "", 0); + } else { + cerr << ": error: invalid size for value specified for defparam: " << name << endl; + free(value); + return; + } + } + PExpr* ndec = new PENumber(val); + Module::user_defparms.push_back( make_pair(name, ndec) ); + free(value); + return; } - free(key); + + // Is it a decimal number? + num = (value[0] == '-') ? value + 1 : value; + if (is_dec_digit_str(num)) { + verinum *val = make_unsized_dec(num); + if (value[0] == '-') *val = -(*val); + PExpr* ndec = new PENumber(val); + Module::user_defparms.push_back( make_pair(name, ndec) ); + free(value); + return; + } + + // Is it a real number? + char *end = 0; + double rval = strtod(value, &end); + if (end != value && *end == 0) { + verireal *val = new verireal(rval); + PExpr* nreal = new PEFNumber(val); + Module::user_defparms.push_back( make_pair(name, nreal) ); + free(value); + return; + } + + // None of the above. + cerr << ": error: invalid value specified for defparam: " << name << endl; free(value); } @@ -273,30 +356,27 @@ static PModport*pform_cur_modport = 0; static NetNet::Type pform_default_nettype = NetNet::WIRE; /* - * These variables track the current time scale, as well as where the - * timescale was set. This supports warnings about tangled timescales. + * These variables track the time scale set by the most recent `timescale + * directive. Time scales set by SystemVerilog timeunit and timeprecision + * declarations are stored directly in the current lexical scope. */ static int pform_time_unit; static int pform_time_prec; -/* These two flags check the initial timeprecision and timeunit - * declaration inside a module. - */ -static bool tp_decl_flag = false; -static bool tu_decl_flag = false; - /* - * Flags used to set time_from_timescale based on timeunit and - * timeprecision. + * These variables track where the most recent `timescale directive + * occurred. This allows us to warn about time scales that are inherited + * from another file. */ -static bool tu_global_flag = false; -static bool tp_global_flag = false; -static bool tu_local_flag = false; -static bool tp_local_flag = false; - static char*pform_timescale_file = 0; static unsigned pform_timescale_line; +/* + * These variables track whether we can accept new timeunits declarations. + */ +bool allow_timeunit_decl = true; +bool allow_timeprec_decl = true; + static inline void FILE_NAME(LineInfo*obj, const char*file, unsigned lineno) { obj->set_lineno(lineno); @@ -312,10 +392,25 @@ 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(); + assert(lexical_scope); +} + +static LexicalScope::lifetime_t find_lifetime(LexicalScope::lifetime_t lifetime) +{ + if (lifetime != LexicalScope::INHERITED) + return lifetime; + + return lexical_scope->default_lifetime; } static PScopeExtra* find_nearest_scopex(LexicalScope*scope) @@ -328,28 +423,98 @@ static PScopeExtra* find_nearest_scopex(LexicalScope*scope) return scopex; } -LexicalScope* pform_peek_scope(void) +/* + * Set the local time unit/precision. This version is used for setting + * the time scale for design elements (modules, packages, etc.) and is + * called after any initial timeunit and timeprecision declarations + * have been parsed. + */ +void pform_set_scope_timescale(const struct vlltype&loc) { - assert(lexical_scope); - return lexical_scope; + PScopeExtra*scope = dynamic_cast(lexical_scope); + assert(scope); + + PScopeExtra*parent = find_nearest_scopex(scope->parent_scope()); + + bool used_global_timescale = false; + if (scope->time_unit_is_default) { + if (is_compilation_unit(scope)) { + scope->time_unit = def_ts_units; + } else if (!is_compilation_unit(parent)) { + scope->time_unit = parent->time_unit; + scope->time_unit_is_default = parent->time_unit_is_default; + } else if (pform_timescale_file != 0) { + scope->time_unit = pform_time_unit; + scope->time_unit_is_default = false; + used_global_timescale = true; + } else /* parent is compilation unit */ { + scope->time_unit = parent->time_unit; + scope->time_unit_is_default = parent->time_unit_is_default; + } + } + if (scope->time_prec_is_default) { + if (is_compilation_unit(scope)) { + scope->time_precision = def_ts_prec; + } else if (!is_compilation_unit(parent)) { + scope->time_precision = parent->time_precision; + scope->time_prec_is_default = parent->time_prec_is_default; + } else if (pform_timescale_file != 0) { + scope->time_precision = pform_time_prec; + scope->time_prec_is_default = false; + used_global_timescale = true; + } else { + scope->time_precision = parent->time_precision; + scope->time_prec_is_default = parent->time_prec_is_default; + } + } + + if (gn_system_verilog() && (scope->time_unit < scope->time_precision)) { + if (scope->time_unit_is_local || scope->time_prec_is_local) { + VLerror("error: a timeprecision is missing or is too large!"); + } + } else { + assert(scope->time_unit >= scope->time_precision); + } + + if (warn_timescale && used_global_timescale + && (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; + } + + allow_timeunit_decl = false; + allow_timeprec_decl = false; } -PClass* pform_push_class_scope(const struct vlltype&loc, perm_string name) +/* + * Set the local time unit/precision. This version is used for setting + * the time scale for subsidiary items (classes, subroutines, etc.), + * which simply inherit their time scale from their parent scope. + */ +static void pform_set_scope_timescale(PScope*scope, const PScope*parent) +{ + scope->time_unit = parent->time_unit; + scope->time_precision = parent->time_precision; + scope->time_unit_is_default = parent->time_unit_is_default; + scope->time_prec_is_default = parent->time_prec_is_default; +} + +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); PScopeExtra*scopex = find_nearest_scopex(lexical_scope); - + assert(scopex); assert(!pform_cur_generate); - /* If no scope was found then this is being defined in the - * compilation unit scope. */ - if (scopex == 0) { - pform_classes[name] = class_scope; - lexical_scope = class_scope; - return class_scope; - } + pform_set_scope_timescale(class_scope, scopex); if (scopex->classes.find(name) != scopex->classes.end()) { cerr << class_scope->get_fileline() << ": error: duplicate " @@ -364,29 +529,42 @@ 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); + allow_timeunit_decl = true; + allow_timeprec_decl = true; + 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); PScopeExtra*scopex = find_nearest_scopex(lexical_scope); - if ((scopex == 0) && !gn_system_verilog()) { + assert(scopex); + if (is_compilation_unit(scopex) && !gn_system_verilog()) { cerr << task->get_fileline() << ": error: task declarations " "must be contained within a module." << endl; error_count += 1; } + pform_set_scope_timescale(task, scopex); + if (pform_cur_generate) { // Check if the task is already in the dictionary. if (pform_cur_generate->tasks.find(task->pscope_name()) != @@ -398,7 +576,7 @@ PTask* pform_push_task_scope(const struct vlltype&loc, char*name, bool is_auto) error_count += 1; } pform_cur_generate->tasks[task->pscope_name()] = task; - } else if (scopex) { + } else { // Check if the task is already in the dictionary. if (scopex->tasks.find(task->pscope_name()) != scopex->tasks.end()) { cerr << task->get_fileline() << ": error: duplicate " @@ -407,15 +585,6 @@ PTask* pform_push_task_scope(const struct vlltype&loc, char*name, bool is_auto) error_count += 1; } scopex->tasks[task->pscope_name()] = task; - - } else { - if (pform_tasks.find(task_name) != pform_tasks.end()) { - cerr << task->get_fileline() << ": error: " - << "Duplicate definition for task '" << name - << "' in $root scope." << endl; - error_count += 1; - } - pform_tasks[task_name] = task; } lexical_scope = task; @@ -424,20 +593,27 @@ 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); PScopeExtra*scopex = find_nearest_scopex(lexical_scope); - if ((scopex == 0) && (generation_flag < GN_VER2005_SV)) { + assert(scopex); + if (is_compilation_unit(scopex) && !gn_system_verilog()) { cerr << func->get_fileline() << ": error: function declarations " "must be contained within a module." << endl; error_count += 1; } + pform_set_scope_timescale(func, scopex); + if (pform_cur_generate) { // Check if the function is already in the dictionary. if (pform_cur_generate->funcs.find(func->pscope_name()) != @@ -450,7 +626,7 @@ PFunction* pform_push_function_scope(const struct vlltype&loc, const char*name, } pform_cur_generate->funcs[func->pscope_name()] = func; - } else if (scopex != 0) { + } else { // Check if the function is already in the dictionary. if (scopex->funcs.find(func->pscope_name()) != scopex->funcs.end()) { cerr << func->get_fileline() << ": error: duplicate " @@ -459,15 +635,6 @@ PFunction* pform_push_function_scope(const struct vlltype&loc, const char*name, error_count += 1; } scopex->funcs[func->pscope_name()] = func; - - } else { - if (pform_tasks.find(func_name) != pform_tasks.end()) { - cerr << func->get_fileline() << ": error: " - << "Duplicate definition for function '" << name - << "' in $root scope." << endl; - error_count += 1; - } - pform_tasks[func_name] = func; } lexical_scope = func; @@ -489,6 +656,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; @@ -566,24 +734,31 @@ static void pform_put_wire_in_scope(perm_string name, PWire*net) static void pform_put_enum_type_in_scope(enum_type_t*enum_set) { - if (lexical_scope) { - ivl_assert(*enum_set, lexical_scope); - lexical_scope->enum_sets.insert(enum_set); - } else { - pform_enum_sets.insert(enum_set); - } + lexical_scope->enum_sets.insert(enum_set); } -PWire*pform_get_make_wire_in_scope(perm_string name, NetNet::Type net_type, NetNet::PortType port_type, ivl_variable_type_t vt_type) +PWire*pform_get_make_wire_in_scope(const struct vlltype&li, perm_string name, + NetNet::Type net_type, NetNet::PortType port_type, + ivl_variable_type_t vt_type) { PWire*cur = pform_get_wire_in_scope(name); + + // If the wire already exists and is fully defined, this + // must be a redeclaration. Start again with a new wire. + if (cur && cur->get_data_type() != IVL_VT_NO_TYPE) { + LineInfo tloc; + FILE_NAME(&tloc, li); + cerr << tloc.get_fileline() << ": error: duplicate declaration " + "for net or variable '" << name << "'." << endl; + error_count += 1; + delete cur; + cur = 0; + } + if (cur == 0) { cur = new PWire(name, net_type, port_type, vt_type); pform_put_wire_in_scope(name, cur); } else { - // If this is a duplicate wire, the data type has already - // been set, then return NULL. - if (cur->get_data_type() != IVL_VT_NO_TYPE) return 0; bool rc = cur->set_wire_type(net_type); assert(rc); rc = cur->set_data_type(vt_type); @@ -598,12 +773,7 @@ void pform_set_typedef(perm_string name, data_type_t*data_type, std::listtypedefs[name] - : pform_typedefs[name]; + data_type_t*&ref = lexical_scope->typedefs[name]; ivl_assert(*data_type, ref == 0); ref = data_type; @@ -613,25 +783,10 @@ void pform_set_typedef(perm_string name, data_type_t*data_type, std::list::iterator cur = pform_typedefs.find(name); - if (cur != pform_typedefs.end()) - return cur->second; - else - return 0; -} - data_type_t* pform_test_type_identifier(const char*txt) { perm_string name = lex_strings.make(txt); - // If there is no lexical_scope yet, then look only in the - // $root scope for typedefs. - if (lexical_scope == 0) { - return test_type_identifier_in_root(name); - } - LexicalScope*cur_scope = lexical_scope; do { map::iterator cur; @@ -661,10 +816,6 @@ data_type_t* pform_test_type_identifier(const char*txt) cur_scope = cur_scope->parent_scope(); } while (cur_scope); - // See if there is a typedef in the $root scope. - if (data_type_t*tmp = test_type_identifier_in_root(name)) - return tmp; - return 0; } @@ -675,13 +826,6 @@ data_type_t* pform_test_type_identifier(const char*txt) */ bool pform_test_type_identifier_local(perm_string name) { - if (lexical_scope == 0) { - if (test_type_identifier_in_root(name)) - return true; - else - return false; - } - LexicalScope*cur_scope = lexical_scope; map::iterator cur; @@ -832,64 +976,23 @@ static void pform_declare_implicit_nets(PExpr*expr) /* * The lexor calls this function to set the active timescale when it * detects a `timescale directive. The function saves the directive - * values (for use by modules) and if warnings are enabled checks to - * see if some modules have no timescale. + * values (for use by subsequent design elements) and if warnings are + * enabled checks to see if some design elements have no timescale. */ void pform_set_timescale(int unit, int prec, const char*file, unsigned lineno) { - bool first_flag = true; - assert(unit >= prec); pform_time_unit = unit; pform_time_prec = prec; - /* A `timescale clears the timeunit/timeprecision state. */ - tu_global_flag = false; - tp_global_flag = false; if (pform_timescale_file) { free(pform_timescale_file); - first_flag = false; } if (file) pform_timescale_file = strdup(file); else pform_timescale_file = 0; pform_timescale_line = lineno; - - if (!warn_timescale || !first_flag || !file) return; - - /* Look to see if we have any modules without a timescale. */ - bool have_no_ts = false; - map::iterator mod; - for (mod = pform_modules.begin(); mod != pform_modules.end(); ++ mod ) { - const Module*mp = (*mod).second; - if (mp->time_from_timescale || - mp->timescale_warn_done) continue; - have_no_ts = true; - break; - } - - /* If we do then print a message for the new ones. */ - if (have_no_ts) { - cerr << file << ":" << lineno << ": warning: " - << "Some modules have no timescale. This may cause" - << endl; - cerr << file << ":" << lineno << ": : " - << "confusing timing results. Affected modules are:" - << endl; - - for (mod = pform_modules.begin() - ; mod != pform_modules.end() ; ++ mod ) { - Module*mp = (*mod).second; - if (mp->time_from_timescale || - mp->timescale_warn_done) continue; - mp->timescale_warn_done = true; - - cerr << file << ":" << lineno << ": : " - << " -- module " << (*mod).first - << " declared here: " << mp->get_fileline() << endl; - } - } } bool get_time_unit(const char*cp, int &unit) @@ -1009,67 +1112,56 @@ static bool get_time_unit_prec(const char*cp, int &res, bool is_unit) return true; } -void pform_set_timeunit(const char*txt, bool in_module, bool only_check) +void pform_set_timeunit(const char*txt, bool initial_decl) { int val; if (get_time_unit_prec(txt, val, true)) return; - if (in_module) { - if (!only_check) { - pform_cur_module.front()->time_unit = val; - tu_decl_flag = true; - tu_local_flag = true; - } else if (!tu_decl_flag) { - VLerror(yylloc, "error: repeat timeunit found and the " - "initial module timeunit is missing."); - return; - } else if (pform_cur_module.front()->time_unit != val) { - VLerror(yylloc, "error: repeat timeunit does not match " - "the initial module timeunit " - "declaration."); - return; - } + PScopeExtra*scope = dynamic_cast(lexical_scope); + assert(scope); - } else { - /* Skip a global timeunit when `timescale is defined. */ - if (pform_timescale_file) return; - tu_global_flag = true; - pform_time_unit = val; + if (initial_decl) { + scope->time_unit = val; + scope->time_unit_is_local = true; + scope->time_unit_is_default = false; + allow_timeunit_decl = false; + } else if (!scope->time_unit_is_local) { + VLerror(yylloc, "error: repeat timeunit found and the initial " + "timeunit for this scope is missing."); + } else if (scope->time_unit != val) { + VLerror(yylloc, "error: repeat timeunit does not match the " + "initial timeunit for this scope."); } } int pform_get_timeunit() { - return pform_cur_module.front()->time_unit; + PScopeExtra*scopex = find_nearest_scopex(lexical_scope); + assert(scopex); + return scopex->time_unit; } -void pform_set_timeprecision(const char*txt, bool in_module, bool only_check) +void pform_set_timeprec(const char*txt, bool initial_decl) { int val; if (get_time_unit_prec(txt, val, false)) return; - if (in_module) { - if (!only_check) { - pform_cur_module.front()->time_precision = val; - tp_decl_flag = true; - tp_local_flag = true; - } else if (!tp_decl_flag) { - VLerror(yylloc, "error: repeat timeprecision found and the " - "initial module timeprecision is missing."); - return; - } else if (pform_cur_module.front()->time_precision != val) { - VLerror(yylloc, "error: repeat timeprecision does not match " - "the initial module timeprecision " - "declaration."); - return; - } - } else { - /* Skip a global timeprecision when `timescale is defined. */ - if (pform_timescale_file) return; - pform_time_prec = val; - tp_global_flag=true; + PScopeExtra*scope = dynamic_cast(lexical_scope); + assert(scope); + + if (initial_decl) { + scope->time_precision = val; + scope->time_prec_is_local = true; + scope->time_prec_is_default = false; + allow_timeprec_decl = false; + } else if (!scope->time_prec_is_local) { + VLerror(yylloc, "error: repeat timeprecision found and the initial " + "timeprecision for this scope is missing."); + } else if (scope->time_precision != val) { + VLerror(yylloc, "error: repeat timeprecision does not match the " + "initial timeprecision for this scope."); } } @@ -1087,16 +1179,21 @@ verinum* pform_verinum_with_size(verinum*siz, verinum*val, verinum::V pad; - switch (val->get(val->len()-1)) { - case verinum::Vz: - pad = verinum::Vz; - break; - case verinum::Vx: + if (val->len() == 0) { pad = verinum::Vx; - break; - default: - pad = verinum::V0; - break; + } else { + + switch (val->get(val->len()-1)) { + case verinum::Vz: + pad = verinum::Vz; + break; + case verinum::Vx: + pad = verinum::Vx; + break; + default: + pad = verinum::V0; + break; + } } verinum*res = new verinum(pad, size, true); @@ -1131,6 +1228,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 +1237,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,54 +1262,26 @@ 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; - 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; + cur_module->default_lifetime = find_lifetime(lifetime); FILE_NAME(cur_module, loc); + cur_module->library_flag = pform_library_flag; pform_cur_module.push_front(cur_module); + allow_timeunit_decl = true; + allow_timeprec_decl = true; + lexical_scope = cur_module; /* The generate scheme numbering starts with *1*, not 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); } -/* - * In SystemVerilog we can have separate timeunit and timeprecision - * declarations. We need to have the values worked out by time this - * task is called. - */ -void pform_check_timeunit_prec() -{ - assert(! pform_cur_module.empty()); - if ((generation_flag & (GN_VER2005_SV | GN_VER2009 | GN_VER2012)) && - (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 >= - pform_cur_module.front()->time_precision); -} - /* * This function is called by the parser to make a simple port * reference. This is a name without a .X(...), so the internal name @@ -1249,8 +1325,6 @@ void pform_endmodule(const char*name, bool inside_celldefine, Module*cur_module = pform_cur_module.front(); pform_cur_module.pop_front(); - cur_module->time_from_timescale = (tu_local_flag && tp_local_flag) - || (pform_timescale_file != 0); perm_string mod_name = cur_module->mod_name(); assert(strcmp(name, mod_name) == 0); cur_module->is_cell = inside_celldefine; @@ -1277,16 +1351,9 @@ void pform_endmodule(const char*name, bool inside_celldefine, use_module_map[mod_name] = cur_module; } - // The current lexical scope should be this module by now, and - // this module should not have a parent lexical scope. + // The current lexical scope should be this module by now. ivl_assert(*cur_module, lexical_scope == cur_module); pform_pop_scope(); - ivl_assert(*cur_module, ! pform_cur_module.empty() || lexical_scope == 0); - - tp_decl_flag = false; - tu_decl_flag = false; - tu_local_flag = false; - tp_local_flag = false; } static void pform_add_genvar(const struct vlltype&li, const perm_string&name, @@ -1905,6 +1972,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) { @@ -2000,9 +2068,11 @@ static void pform_makegate(PGBuiltin::Type type, return; } - for (list::iterator cur = info.parms->begin() - ; cur != info.parms->end() ; ++cur) { - pform_declare_implicit_nets(*cur); + if (info.parms) { + for (list::iterator cur = info.parms->begin() + ; cur != info.parms->end() ; ++cur) { + pform_declare_implicit_nets(*cur); + } } perm_string dev_name = lex_strings.make(info.name); @@ -2071,7 +2141,8 @@ static void pform_make_modgate(perm_string type, struct parmvalue_t*overrides, list*wires, PExpr*msb, PExpr*lsb, - const char*fn, unsigned ln) + const char*fn, unsigned ln, + std::list*attr) { for (list::iterator idx = wires->begin() ; idx != wires->end() ; ++idx) { @@ -2102,6 +2173,7 @@ static void pform_make_modgate(perm_string type, pform_cur_generate->add_gate(cur); else pform_cur_module.front()->add_gate(cur); + pform_bind_attributes(cur->attributes, attr); } static void pform_make_modgate(perm_string type, @@ -2109,7 +2181,8 @@ static void pform_make_modgate(perm_string type, struct parmvalue_t*overrides, list*bind, PExpr*msb, PExpr*lsb, - const char*fn, unsigned ln) + const char*fn, unsigned ln, + std::list*attr) { unsigned npins = bind->size(); named*pins = new named[npins]; @@ -2146,12 +2219,14 @@ static void pform_make_modgate(perm_string type, pform_cur_generate->add_gate(cur); else pform_cur_module.front()->add_gate(cur); + pform_bind_attributes(cur->attributes, attr); } void pform_make_modgates(const struct vlltype&loc, perm_string type, struct parmvalue_t*overrides, - svector*gates) + svector*gates, + std::list*attr) { assert(! pform_cur_module.empty()); if (pform_cur_module.front()->program_block) { @@ -2173,7 +2248,7 @@ void pform_make_modgates(const struct vlltype&loc, pform_make_modgate(type, cur_name, overrides, cur.parms_by_name, cur.range.first, cur.range.second, - cur.file, cur.lineno); + cur.file, cur.lineno, attr); } else if (cur.parms) { @@ -2187,14 +2262,14 @@ void pform_make_modgates(const struct vlltype&loc, pform_make_modgate(type, cur_name, overrides, cur.parms, cur.range.first, cur.range.second, - cur.file, cur.lineno); + cur.file, cur.lineno, attr); } else { list*wires = new list; pform_make_modgate(type, cur_name, overrides, wires, cur.range.first, cur.range.second, - cur.file, cur.lineno); + cur.file, cur.lineno, attr); } } @@ -2248,23 +2323,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 +2357,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 +2366,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 +2385,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 +2475,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 @@ -2422,7 +2530,11 @@ static PWire* pform_get_or_make_wire(const vlltype&li, perm_string name, ivl_variable_type_t dtype) { PWire*cur = pform_get_wire_in_scope(name); - if (cur) { + + // If the wire already exists but isn't yet fully defined, + // carry on adding details. + if (cur && (cur->get_data_type() == IVL_VT_NO_TYPE || + cur->get_wire_type() == NetNet::IMPLICIT) ) { // If this is not implicit ("implicit" meaning we don't // know what the type is yet) then set the type now. if (type != NetNet::IMPLICIT) { @@ -2440,6 +2552,18 @@ static PWire* pform_get_or_make_wire(const vlltype&li, perm_string name, return cur; } + // If the wire already exists and is fully defined, this + // must be a redeclaration. Start again with a new wire. + if (cur) { + LineInfo tloc; + FILE_NAME(&tloc, li); + cerr << tloc.get_fileline() << ": error: duplicate declaration " + "for net or variable '" << name << "' in '" + << pform_cur_module.front()->mod_name() << "'." << endl; + error_count += 1; + delete cur; + } + cur = new PWire(name, type, ptype, dtype); FILE_NAME(cur, li.text, li.first_line); @@ -2570,11 +2694,17 @@ void pform_makewire(const vlltype&li, * net_decl_assign_t argument. */ void pform_makewire(const struct vlltype&li, - std::list*, str_pair_t , + std::list*delay, + str_pair_t str, std::list*assign_list, NetNet::Type type, data_type_t*data_type) { + if (is_compilation_unit(lexical_scope) && !gn_system_verilog()) { + VLerror(li, "error: variable declarations must be contained within a module."); + return; + } + list*names = new list; for (list::iterator cur = assign_list->begin() @@ -2588,8 +2718,18 @@ void pform_makewire(const struct vlltype&li, while (! assign_list->empty()) { decl_assignment_t*first = assign_list->front(); assign_list->pop_front(); - // For now, do not handle assignment expressions. - assert(! first->expr.get()); + if (PExpr*expr = first->expr.release()) { + if (type == NetNet::REG || type == NetNet::IMPLICIT_REG) { + pform_make_var_init(li, first->name, expr); + } else { + PWire*cur = pform_get_wire_in_scope(first->name); + assert(cur); + PEIdent*lval = new PEIdent(first->name); + FILE_NAME(lval, li.text, li.first_line); + PGAssign*ass = pform_make_pgassign(lval, expr, delay, str); + FILE_NAME(ass, li.text, li.first_line); + } + } delete first; } } @@ -2661,7 +2801,10 @@ vector*pform_make_task_ports(const struct vlltype&loc, } curw->set_signed(signed_flag); - if (isint) assert(curw->set_wire_type(NetNet::INTEGER)); + if (isint) { + bool flag = curw->set_wire_type(NetNet::INTEGER); + assert(flag); + } /* If there is a range involved, it needs to be set. */ if (range) { @@ -2744,7 +2887,9 @@ vector*pform_make_task_ports(const struct vlltype&loc, ret = do_make_task_ports(loc, pt, IVL_VT_CLASS, class_type, names); } - ret = do_make_task_ports(loc, pt, IVL_VT_NO_TYPE, vtype, names); + if (! ret) { + ret = do_make_task_ports(loc, pt, IVL_VT_NO_TYPE, vtype, names); + } if (unpacked_dims) { for (list::iterator cur = names->begin() @@ -2868,10 +3013,16 @@ void pform_set_parameter(const struct vlltype&loc, LexicalScope::range_t*value_range) { LexicalScope*scope = lexical_scope; + if (is_compilation_unit(scope) && !gn_system_verilog()) { + VLerror(loc, "error: parameter declarations must be contained within a module."); + return; + } if (scope == pform_cur_generate) { VLerror("parameter declarations are not permitted in generate blocks"); return; } + PScopeExtra*scopex = find_nearest_scopex(lexical_scope); + assert(scopex); // Check if the parameter name is already in the dictionary. if (scope->parameters.find(name) != scope->parameters.end()) { @@ -2879,26 +3030,26 @@ void pform_set_parameter(const struct vlltype&loc, FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: duplicate definition " "for parameter '" << name << "' in '" - << pform_cur_module.front()->mod_name() << "'." << endl; + << scopex->pscope_name() << "'." << endl; error_count += 1; } if (scope->localparams.find(name) != scope->localparams.end()) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: localparam and " - << "parameter in '" << pform_cur_module.front()->mod_name() + << "parameter in '" << scopex->pscope_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } // Only a Module scope has specparams. - if ((scope == pform_cur_module.front()) && - (dynamic_cast (scope)) && + if ((dynamic_cast (scope)) && + (scope == pform_cur_module.front()) && (pform_cur_module.front()->specparams.find(name) != pform_cur_module.front()->specparams.end())) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: specparam and " - "parameter in '" << pform_cur_module.front()->mod_name() + "parameter in '" << scopex->pscope_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } @@ -2925,8 +3076,8 @@ void pform_set_parameter(const struct vlltype&loc, parm.range = value_range; // Only a Module keeps the position of the parameter. - if ((scope == pform_cur_module.front()) && - (dynamic_cast (scope))) + if ((dynamic_cast (scope)) && + (scope == pform_cur_module.front())) pform_cur_module.front()->param_names.push_back(name); } @@ -2935,7 +3086,12 @@ void pform_set_localparam(const struct vlltype&loc, bool signed_flag, list*range, PExpr*expr) { LexicalScope*scope = lexical_scope; - ivl_assert(loc, scope); + if (is_compilation_unit(scope) && !gn_system_verilog()) { + VLerror(loc, "error: localparam declarations must be contained within a module."); + return; + } + PScopeExtra*scopex = find_nearest_scopex(lexical_scope); + assert(scopex); // Check if the localparam name is already in the dictionary. if (scope->localparams.find(name) != scope->localparams.end()) { @@ -2943,14 +3099,14 @@ void pform_set_localparam(const struct vlltype&loc, FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: duplicate definition " "for localparam '" << name << "' in '" - << pform_cur_module.front()->mod_name() << "'." << endl; + << scopex->pscope_name() << "'." << endl; error_count += 1; } if (scope->parameters.find(name) != scope->parameters.end()) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: parameter and " - << "localparam in '" << pform_cur_module.front()->mod_name() + << "localparam in '" << scopex->pscope_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } @@ -2961,7 +3117,7 @@ void pform_set_localparam(const struct vlltype&loc, LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: specparam and " - "localparam in '" << pform_cur_module.front()->mod_name() + "localparam in '" << scopex->pscope_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } @@ -3055,6 +3211,28 @@ void pform_set_defparam(const pform_name_t&name, PExpr*expr) pform_cur_module.front()->defparms.push_back(make_pair(name,expr)); } +void pform_set_param_from_type(const struct vlltype&loc, + const data_type_t *data_type, + const char *name, + list *¶m_range, + bool ¶m_signed, + ivl_variable_type_t ¶m_type) +{ + if (const vector_type_t *vec = dynamic_cast (data_type)) { + param_range = vec->pdims.get(); + param_signed = vec->signed_flag; + param_type = vec->base_type; + return; + } + + param_range = 0; + param_signed = false; + param_type = IVL_VT_NO_TYPE; + cerr << loc.get_fileline() << ": sorry: cannot currently create a " + "parameter of type '" << name << "' which was defined at: " + << data_type->get_fileline() << "." << endl; + error_count += 1; +} /* * Specify paths. */ @@ -3157,30 +3335,59 @@ 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; } -static void pform_set_integer_2atom(uint64_t width, bool signed_flag, perm_string name, NetNet::Type net_type, list*attr) +static void pform_set_integer_2atom(const struct vlltype&li, uint64_t width, bool signed_flag, perm_string name, NetNet::Type net_type, list*attr) { - PWire*cur = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, IVL_VT_BOOL); + PWire*cur = pform_get_make_wire_in_scope(li, name, net_type, NetNet::NOT_A_PORT, IVL_VT_BOOL); assert(cur); cur->set_signed(signed_flag); @@ -3194,12 +3401,12 @@ static void pform_set_integer_2atom(uint64_t width, bool signed_flag, perm_strin pform_bind_attributes(cur->attributes, attr, true); } -static void pform_set_integer_2atom(uint64_t width, bool signed_flag, list*names, NetNet::Type net_type, list*attr) +static void pform_set_integer_2atom(const struct vlltype&li, uint64_t width, bool signed_flag, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur ) { perm_string txt = *cur; - pform_set_integer_2atom(width, signed_flag, txt, net_type, attr); + pform_set_integer_2atom(li, width, signed_flag, txt, net_type, attr); } } @@ -3210,7 +3417,7 @@ template static void pform_set2_data_type(const struct vlltype&li, T*d VLerror(li, "Compound type is not PACKED in this context."); } - PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, base_type); + PWire*net = pform_get_make_wire_in_scope(li, name, net_type, NetNet::NOT_A_PORT, base_type); assert(net); net->set_data_type(data_type); pform_bind_attributes(net->attributes, attr, true); @@ -3228,14 +3435,8 @@ static void pform_set_enum(const struct vlltype&li, enum_type_t*enum_type, perm_string name, NetNet::Type net_type, std::list*attr) { - PWire*cur = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, enum_type->base_type); - // A NULL is returned for a duplicate enumeration. - if (! cur) { - cerr << li.get_fileline() << ": error: Found duplicate " - << "enumeration named " << name << "." << endl; - error_count += 1; - return; - } + PWire*cur = pform_get_make_wire_in_scope(li, name, net_type, NetNet::NOT_A_PORT, enum_type->base_type); + assert(cur); cur->set_signed(enum_type->signed_flag); @@ -3297,11 +3498,11 @@ void pform_set_data_type(const struct vlltype&li, data_type_t*data_type, list (data_type)) { - pform_set_integer_2atom(atom2_type->type_code, atom2_type->signed_flag, names, net_type, attr); + pform_set_integer_2atom(li, atom2_type->type_code, atom2_type->signed_flag, names, net_type, attr); } else if (struct_type_t*struct_type = dynamic_cast (data_type)) { - pform_set_struct_type(struct_type, names, net_type, attr); + pform_set_struct_type(li, struct_type, names, net_type, attr); } else if (enum_type_t*enum_type = dynamic_cast (data_type)) { @@ -3322,7 +3523,7 @@ void pform_set_data_type(const struct vlltype&li, data_type_t*data_type, list (data_type)) { - pform_set_class_type(class_type, names, net_type, attr); + pform_set_class_type(li, class_type, names, net_type, attr); } else if (parray_type_t*array_type = dynamic_cast (data_type)) { @@ -3330,7 +3531,7 @@ void pform_set_data_type(const struct vlltype&li, data_type_t*data_type, list (data_type)) { - pform_set_string_type(string_type, names, net_type, attr); + pform_set_string_type(li, string_type, names, net_type, attr); } else { VLerror(li, "internal error: Unexpected data_type."); @@ -3371,6 +3572,16 @@ vector* pform_make_udp_input_ports(list*names) PProcess* pform_make_behavior(ivl_process_type_t type, Statement*st, list*attr) { + // Add an implicit @* around the statement for the always_comb and + // always_latch statements. + if ((type == IVL_PR_ALWAYS_COMB) || (type == IVL_PR_ALWAYS_LATCH)) { + PEventStatement *tmp = new PEventStatement(true); + tmp->set_file(st->get_file()); + tmp->set_lineno(st->get_lineno()); + tmp->set_statement(st); + st = tmp; + } + PProcess*pp = new PProcess(type, st); // If we are in a part of the code where the meta-comment @@ -3390,8 +3601,10 @@ PProcess* pform_make_behavior(ivl_process_type_t type, Statement*st, pform_put_behavior_in_scope(pp); ivl_assert(*st, ! pform_cur_module.empty()); - if (pform_cur_module.front()->program_block && type == IVL_PR_ALWAYS) { - cerr << st->get_fileline() << ": error: Always statements not allowed" + if (pform_cur_module.front()->program_block && + ((type == IVL_PR_ALWAYS) || (type == IVL_PR_ALWAYS_COMB) || + (type == IVL_PR_ALWAYS_FF) || (type == IVL_PR_ALWAYS_LATCH))) { + cerr << st->get_fileline() << ": error: Always statements are not allowed" << " in program blocks." << endl; error_count += 1; } @@ -3443,31 +3656,72 @@ void pform_add_modport_port(const struct vlltype&loc, FILE*vl_input = 0; extern void reset_lexor(); -int pform_parse(const char*path, FILE*file) +int pform_parse(const char*path) { vl_file = path; - if (file == 0) { + if (strcmp(path, "-") == 0) { + vl_input = stdin; + } else if (ivlpp_string) { + char*cmdline = (char*)malloc(strlen(ivlpp_string) + + strlen(path) + 4); + strcpy(cmdline, ivlpp_string); + strcat(cmdline, " \""); + strcat(cmdline, path); + strcat(cmdline, "\""); - if (strcmp(path, "-") == 0) - vl_input = stdin; - else - vl_input = fopen(path, "r"); + if (verbose_flag) + cerr << "Executing: " << cmdline << endl<< flush; + + vl_input = popen(cmdline, "r"); if (vl_input == 0) { - cerr << "Unable to open " <default_lifetime = LexicalScope::STATIC; + unit->set_file(filename_strings.make(path)); + unit->set_lineno(1); + pform_units.push_back(unit); + + pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); + + allow_timeunit_decl = true; + allow_timeprec_decl = true; + + lexical_scope = unit; + } reset_lexor(); error_count = 0; warn_count = 0; int rc = VLparse(); - if (file == 0) - fclose(vl_input); + if (vl_input != stdin) { + if (ivlpp_string) + pclose(vl_input); + else + fclose(vl_input); + } if (rc) { cerr << "I give up." << endl; diff --git a/pform.h b/pform.h index 2e7f8ea5e..2b9c04f23 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-2017 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 @@ -104,7 +104,7 @@ struct net_decl_assign_t { /* The lgate is gate instantiation information. */ struct lgate { - inline lgate(int =0) + explicit inline lgate(int =0) : parms(0), parms_by_name(0), file(NULL), lineno(0) { } @@ -147,7 +147,11 @@ extern bool pform_in_interface(void); */ extern PWire* pform_get_wire_in_scope(perm_string name); -extern PWire* pform_get_make_wire_in_scope(perm_string name, NetNet::Type net_type, NetNet::PortType port_type, ivl_variable_type_t vt_type); +extern PWire* pform_get_make_wire_in_scope(const struct vlltype&li, + perm_string name, + NetNet::Type net_type, + NetNet::PortType port_type, + ivl_variable_type_t vt_type); /* * The parser uses startmodule and endmodule together to build up a @@ -163,18 +167,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*); +extern void pform_set_scope_timescale(const struct vlltype&loc); -/* 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 +198,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 +224,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 +260,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 +372,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, @@ -369,11 +390,11 @@ extern void pform_set_reg_idx(perm_string name, extern void pform_set_data_type(const struct vlltype&li, data_type_t*, list*names, NetNet::Type net_type, list*attr); -extern void pform_set_struct_type(struct_type_t*struct_type, std::list*names, NetNet::Type net_type, std::list*attr); +extern void pform_set_struct_type(const struct vlltype&li, struct_type_t*struct_type, std::list*names, NetNet::Type net_type, std::list*attr); -extern void pform_set_string_type(const string_type_t*string_type, std::list*names, NetNet::Type net_type, std::list*attr); +extern void pform_set_string_type(const struct vlltype&li, const string_type_t*string_type, std::list*names, NetNet::Type net_type, std::list*attr); -extern void pform_set_class_type(class_type_t*class_type, std::list*names, NetNet::Type net_type, std::list*addr); +extern void pform_set_class_type(const struct vlltype&li, class_type_t*class_type, std::list*names, NetNet::Type net_type, std::list*addr); /* pform_set_attrib and pform_set_type_attrib exist to support the @@ -407,6 +428,13 @@ extern void pform_set_specparam(const struct vlltype&loc, PExpr*expr); extern void pform_set_defparam(const pform_name_t&name, PExpr*expr); +extern void pform_set_param_from_type(const struct vlltype&loc, + const data_type_t *data_type, + const char *name, + list *¶m_range, + bool ¶m_signed, + ivl_variable_type_t ¶m_type); + /* * Functions related to specify blocks. */ @@ -454,7 +482,8 @@ extern void pform_makegates(const struct vlltype&loc, extern void pform_make_modgates(const struct vlltype&loc, perm_string type, struct parmvalue_t*overrides, - svector*gates); + svector*gates, + list*attr); /* Make a continuous assignment node, with optional bit- or part- select. */ extern void pform_make_pgassign_list(list*alist, @@ -542,8 +571,12 @@ extern void parm_to_defparam_list(const string¶m); */ extern bool get_time_unit(const char*cp, int &unit); extern int pform_get_timeunit(); -extern void pform_set_timeunit(const char*txt, bool in_module, bool only_check); -extern void pform_set_timeprecision(const char*txt, bool in_module, - bool only_check); +extern void pform_set_timeunit(const char*txt, bool initial_decl); +extern void pform_set_timeprec(const char*txt, bool initial_decl); +/* + * Flags to determine whether this is an initial declaration. + */ +extern bool allow_timeunit_decl; +extern bool allow_timeprec_decl; #endif /* IVL_pform_H */ diff --git a/pform_class_type.cc b/pform_class_type.cc index ec2f99de4..9c1b7354e 100644 --- a/pform_class_type.cc +++ b/pform_class_type.cc @@ -22,18 +22,18 @@ # include "parse_misc.h" # include "ivl_assert.h" -static void pform_set_class_type(class_type_t*class_type, perm_string name, NetNet::Type net_type, list*attr) +static void pform_set_class_type(const struct vlltype&li, class_type_t*class_type, perm_string name, NetNet::Type net_type, list*attr) { - PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, IVL_VT_CLASS); + PWire*net = pform_get_make_wire_in_scope(li, name, net_type, NetNet::NOT_A_PORT, IVL_VT_CLASS); assert(net); net->set_data_type(class_type); pform_bind_attributes(net->attributes, attr, true); } -void pform_set_class_type(class_type_t*class_type, list*names, NetNet::Type net_type, list*attr) +void pform_set_class_type(const struct vlltype&li, class_type_t*class_type, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur) { - pform_set_class_type(class_type, *cur, net_type, attr); + pform_set_class_type(li, class_type, *cur, net_type, attr); } } diff --git a/pform_dump.cc b/pform_dump.cc index 4cbd994ec..c0f329f86 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-2017 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,6 +121,15 @@ std::ostream& operator << (std::ostream&out, ivl_process_type_t pt) case IVL_PR_ALWAYS: out << "always"; break; + case IVL_PR_ALWAYS_COMB: + out << "always_comb"; + break; + case IVL_PR_ALWAYS_FF: + out << "always_ff"; + break; + case IVL_PR_ALWAYS_LATCH: + out << "always_latch"; + break; case IVL_PR_FINAL: out << "final"; break; @@ -318,7 +327,7 @@ void PECallFunction::dump(ostream &out) const void PECastSize::dump(ostream &out) const { - out << size_ << "'("; + out << *size_ << "'("; base_->dump(out); out << ")"; } @@ -627,10 +636,10 @@ void PGBuiltin::dump(ostream&out, unsigned ind) const out << "bufif1 "; break; case PGBuiltin::NOTIF0: - out << "bufif0 "; + out << "notif0 "; break; case PGBuiltin::NOTIF1: - out << "bufif1 "; + out << "notif1 "; break; case PGBuiltin::NAND: out << "nand "; @@ -729,6 +738,7 @@ void PGModule::dump(ostream&out, unsigned ind) const dump_pins(out); } out << ");" << endl; + dump_attributes_map(out, attributes, 8); } void Statement::dump(ostream&out, unsigned ind) const @@ -789,6 +799,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) { @@ -841,7 +853,7 @@ void PCase::dump(ostream&out, unsigned ind) const for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) { PCase::Item*cur = (*items_)[idx]; - if (cur->expr.size()) { + if (! cur->expr.empty()) { out << setw(ind+2) << "" << "default:"; } else { @@ -926,7 +938,10 @@ void PDisable::dump(ostream&out, unsigned ind) const void PDoWhile::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "do" << endl; - statement_->dump(out, ind+3); + if (statement_) + statement_->dump(out, ind+3); + else + out << setw(ind+3) << "" << "/* NOOP */" << endl; out << setw(ind) << "" << "while (" << *cond_ << ");" << endl; } @@ -989,13 +1004,19 @@ void PForeach::dump(ostream&fd, unsigned ind) const } fd << "] /* " << get_fileline() << " */" << endl; - statement_->dump(fd, ind+3); + if (statement_) + statement_->dump(fd, ind+3); + else + fd << setw(ind+3) << "" << "/* NOOP */" << endl; } void PForever::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "forever /* " << get_fileline() << " */" << endl; - statement_->dump(out, ind+3); + if (statement_) + statement_->dump(out, ind+3); + else + out << setw(ind+3) << "" << "/* NOOP */" << endl; } void PForStatement::dump(ostream&out, unsigned ind) const @@ -1003,7 +1024,10 @@ void PForStatement::dump(ostream&out, unsigned ind) const out << setw(ind) << "" << "for (" << *name1_ << " = " << *expr1_ << "; " << *cond_ << "; )" << endl; step_->dump(out, ind+6); - statement_->dump(out, ind+3); + if (statement_) + statement_->dump(out, ind+3); + else + out << setw(ind+3) << "" << "/* NOOP */" << endl; } void PFunction::dump(ostream&out, unsigned ind) const @@ -1030,6 +1054,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 @@ -1045,7 +1071,10 @@ void PRelease::dump(ostream&out, unsigned ind) const void PRepeat::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "repeat (" << *expr_ << ")" << endl; - statement_->dump(out, ind+3); + if (statement_) + statement_->dump(out, ind+3); + else + out << setw(ind+3) << "" << "/* NOOP */" << endl; } void PReturn::dump(ostream&fd, unsigned ind) const @@ -1072,6 +1101,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 @@ -1126,7 +1157,10 @@ void PTrigger::dump(ostream&out, unsigned ind) const void PWhile::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "while (" << *cond_ << ")" << endl; - statement_->dump(out, ind+3); + if (statement_) + statement_->dump(out, ind+3); + else + out << setw(ind+3) << "" << "/* NOOP */" << endl; } void PProcess::dump(ostream&out, unsigned ind) const @@ -1136,7 +1170,10 @@ void PProcess::dump(ostream&out, unsigned ind) const dump_attributes_map(out, attributes, ind+2); - statement_->dump(out, ind+2); + if (statement_) + statement_->dump(out, ind+2); + else + out << setw(ind+2) << "" << "/* NOOP */" << endl; } void AProcess::dump(ostream&out, unsigned ind) const @@ -1148,6 +1185,11 @@ void AProcess::dump(ostream&out, unsigned ind) const case IVL_PR_ALWAYS: out << setw(ind) << "" << "analog"; break; + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: + assert(0); + break; case IVL_PR_FINAL: out << setw(ind) << "" << "analog final"; break; @@ -1157,7 +1199,10 @@ void AProcess::dump(ostream&out, unsigned ind) const dump_attributes_map(out, attributes, ind+2); - statement_->dump(out, ind+2); + if (statement_) + statement_->dump(out, ind+2); + else + out << setw(ind+2) << "" << "/* NOOP */" << endl; } void PSpecPath::dump(std::ostream&out, unsigned ind) const @@ -1269,6 +1314,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 +1455,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 +1619,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 ) { @@ -1664,8 +1720,10 @@ void PPackage::pform_dump(std::ostream&out) const dump_localparams_(out, 4); dump_parameters_(out, 4); dump_enumerations_(out, 4); + dump_wires_(out, 4); dump_tasks_(out, 4); dump_funcs_(out, 4); + dump_var_inits_(out, 4); out << "endpackage" << endl; } 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_string_type.cc b/pform_string_type.cc index 50a7f4fce..9d1ec73bc 100644 --- a/pform_string_type.cc +++ b/pform_string_type.cc @@ -21,18 +21,17 @@ # include "parse_misc.h" # include "ivl_assert.h" -static void pform_set_string_type(const string_type_t*, perm_string name, NetNet::Type net_type, list*attr) +static void pform_set_string_type(const struct vlltype&li, const string_type_t*, perm_string name, NetNet::Type net_type, list*attr) { - PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, IVL_VT_STRING); + PWire*net = pform_get_make_wire_in_scope(li, name, net_type, NetNet::NOT_A_PORT, IVL_VT_STRING); assert(net); pform_bind_attributes(net->attributes, attr, true); } -void pform_set_string_type(const string_type_t*string_type, list*names, NetNet::Type net_type, list*attr) +void pform_set_string_type(const struct vlltype&li, const string_type_t*string_type, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur) { - pform_set_string_type(string_type, *cur, net_type, attr); + pform_set_string_type(li, string_type, *cur, net_type, attr); } } - diff --git a/pform_struct_type.cc b/pform_struct_type.cc index 766fc77e3..d4ef9e7c9 100644 --- a/pform_struct_type.cc +++ b/pform_struct_type.cc @@ -62,32 +62,32 @@ ivl_variable_type_t struct_type_t::figure_packed_base_type(void) const * out the base type of the packed variable. Elaboration, later on, * well figure out the rest. */ -static void pform_set_packed_struct(struct_type_t*struct_type, perm_string name, NetNet::Type net_type, list*attr) +static void pform_set_packed_struct(const struct vlltype&li, struct_type_t*struct_type, perm_string name, NetNet::Type net_type, list*attr) { ivl_variable_type_t base_type = struct_type->figure_packed_base_type(); - PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, base_type); + PWire*net = pform_get_make_wire_in_scope(li, name, net_type, NetNet::NOT_A_PORT, base_type); assert(net); net->set_data_type(struct_type); pform_bind_attributes(net->attributes, attr, true); } -static void pform_set_struct_type(struct_type_t*struct_type, perm_string name, NetNet::Type net_type, list*attr) +static void pform_set_struct_type(const struct vlltype&li, struct_type_t*struct_type, perm_string name, NetNet::Type net_type, list*attr) { if (struct_type->packed_flag) { - pform_set_packed_struct(struct_type, name, net_type, attr); + pform_set_packed_struct(li, struct_type, name, net_type, attr); return; } - // For now, can only handle packed structs. - ivl_assert(*struct_type, 0); + // For now, can only handle packed structs. The parser generates + // a "sorry" message, so no need to do anything here. } -void pform_set_struct_type(struct_type_t*struct_type, list*names, NetNet::Type net_type, list*attr) +void pform_set_struct_type(const struct vlltype&li, struct_type_t*struct_type, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur) { - pform_set_struct_type(struct_type, *cur, net_type, attr); + pform_set_struct_type(li, struct_type, *cur, net_type, attr); } } @@ -99,7 +99,7 @@ static void pform_makewire(const struct vlltype&li, { ivl_variable_type_t base_type = struct_type->figure_packed_base_type(); - PWire*cur = pform_get_make_wire_in_scope(name, NetNet::WIRE, ptype, base_type); + PWire*cur = pform_get_make_wire_in_scope(li, name, NetNet::WIRE, ptype, base_type); assert(cur); FILE_NAME(cur, li); cur->set_data_type(struct_type); diff --git a/pform_types.h b/pform_types.h index de616f1d9..92061527e 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-2018 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 @@ -33,6 +33,10 @@ # include # include +#if __cplusplus < 201103L +#define unique_ptr auto_ptr +#endif + /* * parse-form types. */ @@ -50,7 +54,7 @@ typedef named named_number_t; typedef named named_pexpr_t; /* - * The pform_range_t holds variable diimensions for type + * The pform_range_t holds variable dimensions for type * declarations. The two expressions are interpreted as the first and * last values of the range. For example: * @@ -72,6 +76,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 @@ -102,7 +121,7 @@ struct name_component_t { struct decl_assignment_t { perm_string name; std::listindex; - std::auto_ptr expr; + std::unique_ptr expr; }; struct pform_tf_port_t { @@ -155,14 +174,14 @@ struct enum_type_t : public data_type_t { ivl_variable_type_t base_type; bool signed_flag; bool integer_flag; // True if "integer" was used - std::auto_ptr< list > range; - std::auto_ptr< list > names; + std::unique_ptr< list > range; + std::unique_ptr< list > names; LineInfo li; }; struct struct_member_t : public LineInfo { - std::auto_ptr type; - std::auto_ptr< list > names; + std::unique_ptr type; + std::unique_ptr< list > names; void pform_dump(std::ostream&out, unsigned indent) const; }; @@ -173,7 +192,7 @@ struct struct_type_t : public data_type_t { bool packed_flag; bool union_flag; - std::auto_ptr< list > members; + std::unique_ptr< list > members; }; struct atom2_type_t : public data_type_t { @@ -219,7 +238,7 @@ struct vector_type_t : public data_type_t { bool reg_flag; // True if "reg" was used bool integer_flag; // True if "integer" was used bool implicit_flag; // True if this type is implicitly logic/reg - std::auto_ptr< list > pdims; + std::unique_ptr< list > pdims; }; struct array_base_t : public data_type_t { @@ -228,7 +247,7 @@ struct array_base_t : public data_type_t { : base_type(btype), dims(pd) { } data_type_t*base_type; - std::auto_ptr< list > dims; + std::unique_ptr< list > dims; }; /* @@ -361,4 +380,8 @@ extern std::ostream& operator<< (std::ostream&out, const pform_name_t&); extern std::ostream& operator<< (std::ostream&out, const name_component_t&that); extern std::ostream& operator<< (std::ostream&out, const index_component_t&that); +#if __cplusplus < 201103L +#undef unique_ptr +#endif + #endif /* IVL_pform_types_H */ diff --git a/scripts/MAKE_RELEASE.sh b/scripts/MAKE_RELEASE.sh index 2aa76447e..939b3473f 100644 --- a/scripts/MAKE_RELEASE.sh +++ b/scripts/MAKE_RELEASE.sh @@ -4,7 +4,7 @@ # the number for a snapshot and the path to a temporary directory. # for example: # -# sh scripts/MAKE_RELEASE.sh 0.9.1 ~/tmp +# sh scripts/MAKE_RELEASE.sh 10.1 ~/tmp # # The above assumes that there is a tag "v0_9_1" at the point # to be released. (The tag has the "v", but the argument to this @@ -13,13 +13,17 @@ # and finally creates a file called verilog-0.9.1.tar.gz that # contains the release ready to go. # -# The complete steps to make a release x.y.z generally are: +# The complete steps to make a release x.y generally are: # -# git tag -a v0_9_1 +# Edit version_base.h to suit. +# +# Edit verilog.spec to suit. +# +# git tag -a v10_1 # (Make the tag in the local git repository.) # -# sh scripts/MAKE_RELEASE.sh 0.9.1 ~/tmp -# (Make the snapshot bundle verilog-0.9.1.tar.gz) +# sh scripts/MAKE_RELEASE.sh 10.1 ~/tmp +# (Make the snapshot bundle verilog-10.1.tar.gz) # # git push --tags # (Publish the tag to the repository.) diff --git a/scripts/devel-stub.conf b/scripts/devel-stub.conf index 202c7eed5..5936f6662 100644 --- a/scripts/devel-stub.conf +++ b/scripts/devel-stub.conf @@ -15,7 +15,7 @@ iwidth:32 sys_func:vpi/system.sft sys_func:vpi/v2005_math.sft sys_func:vpi/va_math.sft -warnings:ailnpstv +warnings:adfilnpstv debug:eval_tree debug:elaborate debug:emit diff --git a/sv_vpi_user.h b/sv_vpi_user.h index 1282e0ee2..c4f96addd 100644 --- a/sv_vpi_user.h +++ b/sv_vpi_user.h @@ -72,6 +72,13 @@ EXTERN_C_START /********* Many-to-One ***********/ #define vpiMember 742 +/********* task/function properties **********/ +#define vpiOtherFunc 6 + +/* Icarus-specific function type to use string as the return type */ +#define vpiStringFunc 10 +#define vpiSysFuncString vpiSysFuncString + EXTERN_C_END #endif /* SV_VPI_USER_H */ diff --git a/svector.h b/svector.h index bf17dcaba..44864bda6 100644 --- a/svector.h +++ b/svector.h @@ -1,7 +1,7 @@ #ifndef IVL_svector_H #define IVL_svector_H /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2017 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 @@ -39,7 +39,7 @@ template class svector { explicit svector(unsigned size) : nitems_(size), items_(new TYPE[size]) { for (unsigned idx = 0 ; idx < size ; idx += 1) - items_[idx] = 0; + items_[idx] = TYPE(0); } svector(const svector&that) @@ -57,7 +57,7 @@ template class svector { items_[l.nitems_+idx] = r[idx]; } - svector(const svector&l, TYPE r) + svector(const svector&l, TYPE&r) : nitems_(l.nitems_ + 1), items_(new TYPE[nitems_]) { for (unsigned idx = 0 ; idx < l.nitems_ ; idx += 1) items_[idx] = l[idx]; diff --git a/symbol_search.cc b/symbol_search.cc index 951c6ad3f..8b8e22239 100644 --- a/symbol_search.cc +++ b/symbol_search.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2017 Stephen Williams (steve@icarus.com) * Copyright CERN 2012 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -66,6 +66,7 @@ static bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, bool prefix_scope = false; bool recurse_flag = false; + assert(li); ivl_assert(*li, ! path.empty()); name_component_t path_tail = path.back(); path.pop_back(); @@ -94,7 +95,7 @@ static bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, scope = recurse.scope; prefix_scope = true; - if (scope->is_auto() && li) { + if (scope->is_auto()) { cerr << li->get_fileline() << ": error: Hierarchical " "reference to automatically allocated item " "`" << path_tail.name << "' in path `" << path << "'" << endl; @@ -142,14 +143,21 @@ static bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, } } - // Don't scan up past a module boundary. - if (scope->type()==NetScope::MODULE && !scope->nested_module()) - break; // Don't scan up if we are searching within a prefixed scope. if (prefix_scope) break; - scope = scope->parent(); + // Don't scan up past a module boundary. + if (scope->type()==NetScope::MODULE && !scope->nested_module()) + scope = 0; + else + scope = scope->parent(); + + // Last chance - try the compilation unit. + if (scope == 0 && start_scope != 0) { + scope = start_scope->unit(); + start_scope = 0; + } } // Last chance: this is a single name, so it might be the name diff --git a/syn-rules.y b/syn-rules.y index 8a86fe6e5..cb9bf50d4 100644 --- a/syn-rules.y +++ b/syn-rules.y @@ -1,7 +1,7 @@ %{ /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 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,11 +121,6 @@ static void hookup_DFF_CE(NetFF*ff, NetESignal*d, NetEvProbe*pclk, connect(ff->pin_Clock(), pclk->pin(0)); if (ce) connect(ff->pin_Enable(), ce->pin(0)); - ff->attribute(perm_string::literal("LPM_FFType"), verinum("DFF")); - if (pclk->edge() == NetEvProbe::NEGEDGE) - ff->attribute(perm_string::literal("Clock:LPM_Polarity"), verinum("INVERT")); - - /* This lval_ represents a reg that is a WIRE in the synthesized results. This function signals the destructor to change the REG that this l-value refers to into a @@ -165,7 +160,8 @@ static void make_DFF_CE(Design*des, NetProcTop*top, if (a->sig()) { // cerr << "new NetFF named " << a->name() << endl; - NetFF*ff = new NetFF(top->scope(), a->name(), + bool negedge = pclk->edge() == NetEvProbe::NEGEDGE; + NetFF*ff = new NetFF(top->scope(), a->name(), negedge, a->sig()->vector_width()); hookup_DFF_CE(ff, d, pclk, ce, a, rval_pinoffset); des->add_node(ff); @@ -308,6 +304,15 @@ static void syn_start_process(NetProcTop*t) last_ = first_; ptr_ = first_; + // Can the following be converted into S_ALWAYS? + if ((t->type() == IVL_PR_ALWAYS_COMB) || + (t->type() == IVL_PR_ALWAYS_FF) || + (t->type() == IVL_PR_ALWAYS_LATCH)) { + cerr << t->get_fileline() << ": internal error: " + << " Need to check if this can be synthesized." << endl; + assert(0); + } + first_->token = (t->type() == IVL_PR_ALWAYS)? S_ALWAYS : S_INITIAL; first_->top = t; first_->next_ = 0; diff --git a/synth.cc b/synth.cc index 3e4c85a1d..cd7516d3d 100644 --- a/synth.cc +++ b/synth.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2017 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 @@ -134,6 +134,9 @@ void synth_f::process(Design*des, NetProcTop*top) top_ = top; switch (top->type()) { case IVL_PR_ALWAYS: + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: proc_always_(des); break; case IVL_PR_INITIAL: diff --git a/synth2.cc b/synth2.cc index e0a964f04..b8cbf64c8 100644 --- a/synth2.cc +++ b/synth2.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2015 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 @@ -28,34 +28,274 @@ using namespace std; -bool NetProc::synth_async(Design*, NetScope*, NexusSet&, NetBus&, NetBus&) +/* General notes on enables and bitmasks. + * + * When synthesising an asynchronous process that contains conditional + * statements (if/case statements), we need to determine the conditions + * that cause each nexus driven by that process to be updated. If a + * nexus is not updated under all circumstances, we must infer a latch. + * To this end, we generate an enable signal for each output nexus. As + * we walk the statement tree for the process, for each substatement we + * pass the enable signals generated so far into the synth_async method, + * and on return from the synth_async method, the enable signals will be + * updated to reflect any conditions introduced by that substatement. + * Once we have synthesised all the statements for that process, if an + * enable signal is not tied high, we must infer a latch for that nexus. + * + * When synthesising a synchronous process, we use the synth_async method + * to synthesise the combinatorial inputs to the D pins of the flip-flops + * we infer for that process. In this case the enable signal can be used + * as a clock enable for the flip-flop. This saves us explicitly feeding + * back the flip-flop output to undriven inputs of any synthesised muxes. + * + * The strategy described above is not sufficient when not all bits in + * a nexus are treated identically (i.e. different conditional clauses + * drive differing parts of the same vector). To handle this properly, + * we would (potentially) need to generate a separate enable signal for + * each bit in the vector. This would be a lot of work, particularly if + * we wanted to eliminate duplicates. For now, the strategy employed is + * to maintain a bitmask for each output nexus that identifies which bits + * in the nexus are unconditionally driven (driven by every clause). When + * we finish synthesising an asynchronous process, if the bitmask is not + * all ones, we must infer a latch. This currently results in an error, + * because to safely synthesise such a latch we would need the bit-level + * gate enables. When we finish synthesising a synchronous process, if + * the bitmask is not all ones, we explicitly feed the flip-flop outputs + * back to undriven inputs of any synthesised muxes to ensure undriven + * parts of the vector retain their previous state when the flip-flop is + * clocked. + * + * The enable signals are passed as links to the current output nexus + * for each signal. If an enable signal is not linked, this is treated + * as if the signal was tied low. + * + * The bitmasks are passed as bool vectors. 'true' indicates a bit is + * unconditionally driven. An empty vector (size = 0) indicates that + * the current substatement doesn't drive any bits in the nexus. + */ + +static void qualify_enable(Design*des, NetScope*scope, NetNet*qualifier, + bool active_state, NetLogic::TYPE gate_type, + Link&enable_i, Link&enable_o) +{ + if (enable_i.is_linked(scope->tie_lo())) { + connect(enable_o, scope->tie_lo()); + return; + } + + if (active_state == false) { + NetLogic*gate = new NetLogic(scope, scope->local_symbol(), + 2, NetLogic::NOT, 1); + des->add_node(gate); + connect(gate->pin(1), qualifier->pin(0)); + + NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, + &netvector_t::scalar_logic); + sig->local_flag(true); + connect(sig->pin(0), gate->pin(0)); + + qualifier = sig; + } + + if (enable_i.is_linked(scope->tie_hi())) { + connect(enable_o, qualifier->pin(0)); + return; + } + + NetLogic*gate = new NetLogic(scope, scope->local_symbol(), + 3, gate_type, 1); + des->add_node(gate); + connect(gate->pin(1), qualifier->pin(0)); + connect(gate->pin(2), enable_i); + connect(enable_o, gate->pin(0)); + + NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, + &netvector_t::scalar_logic); + sig->local_flag(true); + connect(sig->pin(0), gate->pin(0)); +} + +static void multiplex_enables(Design*des, NetScope*scope, NetNet*select, + Link&enable_1, Link&enable_0, Link&enable_o) +{ + if (!enable_1.is_linked() && + !enable_0.is_linked() ) + return; + + if ( enable_1.is_linked(scope->tie_hi()) && + enable_0.is_linked(scope->tie_hi()) ) { + connect(enable_o, scope->tie_hi()); + return; + } + + if (enable_1.is_linked(scope->tie_lo()) || !enable_1.is_linked()) { + qualify_enable(des, scope, select, false, NetLogic::AND, + enable_0, enable_o); + return; + } + if (enable_0.is_linked(scope->tie_lo()) || !enable_0.is_linked()) { + qualify_enable(des, scope, select, true, NetLogic::AND, + enable_1, enable_o); + return; + } + if (enable_1.is_linked(scope->tie_hi())) { + qualify_enable(des, scope, select, true, NetLogic::OR, + enable_0, enable_o); + return; + } + if (enable_0.is_linked(scope->tie_hi())) { + qualify_enable(des, scope, select, false, NetLogic::OR, + enable_1, enable_o); + return; + } + + NetMux*mux = new NetMux(scope, scope->local_symbol(), 1, 2, 1); + des->add_node(mux); + connect(mux->pin_Sel(), select->pin(0)); + connect(mux->pin_Data(1), enable_1); + connect(mux->pin_Data(0), enable_0); + connect(enable_o, mux->pin_Result()); + + NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, + &netvector_t::scalar_logic); + sig->local_flag(true); + connect(sig->pin(0), mux->pin_Result()); +} + +static void merge_sequential_enables(Design*des, NetScope*scope, + Link&top_enable, Link&sub_enable) +{ + if (!sub_enable.is_linked()) + return; + + if (top_enable.is_linked(scope->tie_hi())) + return; + + if (sub_enable.is_linked(scope->tie_hi())) + top_enable.unlink(); + + if (top_enable.is_linked()) { + NetLogic*gate = new NetLogic(scope, scope->local_symbol(), + 3, NetLogic::OR, 1); + des->add_node(gate); + connect(gate->pin(1), sub_enable); + connect(gate->pin(2), top_enable); + top_enable.unlink(); + connect(top_enable, gate->pin(0)); + + NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, + &netvector_t::scalar_logic); + sig->local_flag(true); + connect(sig->pin(0), gate->pin(0)); + } else { + connect(top_enable, sub_enable); + } +} + +static void merge_sequential_masks(NetProc::mask_t&top_mask, NetProc::mask_t&sub_mask) +{ + if (sub_mask.size() == 0) + return; + + if (top_mask.size() == 0) { + top_mask = sub_mask; + return; + } + + assert(top_mask.size() == sub_mask.size()); + for (unsigned idx = 0 ; idx < top_mask.size() ; idx += 1) { + if (sub_mask[idx] == true) + top_mask[idx] = true; + } +} + +static void merge_parallel_masks(NetProc::mask_t&top_mask, NetProc::mask_t&sub_mask) +{ + if (sub_mask.size() == 0) + return; + + if (top_mask.size() == 0) { + top_mask = sub_mask; + return; + } + + assert(top_mask.size() == sub_mask.size()); + for (unsigned idx = 0 ; idx < top_mask.size() ; idx += 1) { + if (sub_mask[idx] == false) + top_mask[idx] = false; + } +} + +static bool all_bits_driven(NetProc::mask_t&mask) +{ + if (mask.size() == 0) + return false; + + for (unsigned idx = 0 ; idx < mask.size() ; idx += 1) { + if (mask[idx] == false) + return false; + } + return true; +} + +bool NetProcTop::tie_off_floating_inputs_(Design*des, + NexusSet&nex_map, NetBus&nex_in, + vector&bitmasks, + bool is_ff_input) +{ + bool flag = true; + for (unsigned idx = 0 ; idx < nex_in.pin_count() ; idx += 1) { + if (nex_in.pin(idx).nexus()->has_floating_input()) { + if (all_bits_driven(bitmasks[idx])) { + // If all bits are unconditionally driven, we can + // use the enable signal to prevent the flip-flop/ + // latch from updating when an undriven mux input + // is selected, so we can just tie off the input. + unsigned width = nex_map[idx].wid; + NetLogic*gate = new NetLogic(scope(), scope()->local_symbol(), + 1, NetLogic::PULLDOWN, width); + des->add_node(gate); + connect(nex_in.pin(idx), gate->pin(0)); + + if (nex_in.pin(idx).nexus()->pick_any_net()) + continue; + + ivl_variable_type_t data_type = IVL_VT_LOGIC; + netvector_t*tmp_vec = new netvector_t(data_type, width-1,0); + NetNet*sig = new NetNet(scope(), scope()->local_symbol(), + NetNet::WIRE, tmp_vec); + sig->local_flag(true); + connect(sig->pin(0), gate->pin(0)); + } else if (is_ff_input) { + // For a flip-flop, we can feed back the output + // to ensure undriven bits hold their last value. + connect(nex_in.pin(idx), nex_map[idx].lnk); + } else { + // This infers a latch, but without generating + // gate enable signals at the bit-level, we + // can't safely latch the undriven bits (we + // shouldn't generate combinatorial loops). + cerr << get_fileline() << ": warning: A latch " + << "has been inferred for some bits of '" + << nex_map[idx].lnk.nexus()->pick_any_net()->name() + << "'." << endl; + + cerr << get_fileline() << ": sorry: Bit-level " + "latch gate enables are not currently " + "supported in synthesis." << endl; + des->errors += 1; + flag = false; + } + } + } + return flag; +} + +bool NetProc::synth_async(Design*, NetScope*, NexusSet&, NetBus&, NetBus&, vector&) { return false; } -bool NetProc::synth_sync(Design*des, NetScope*scope, - NetNet* /* ff_clk */, NetBus& /* ff_ce */, - NetBus& /* ff_aclr*/, NetBus& /* ff_aset*/, - NexusSet&nex_map, NetBus&nex_out, - const vector&events) -{ - if (events.size() > 0) { - cerr << get_fileline() << ": error: Events are unaccounted" - << " for in process synthesis." << endl; - des->errors += 1; - } - - if (debug_synth2) { - cerr << get_fileline() << ": NetProc::synth_sync: " - << "This statement is an async input to a sync process." << endl; - } - - /* Synthesize the input to the DFF. */ - NetBus accumulated_nex_out (scope, nex_out.pin_count()); - return synth_async(des, scope, nex_map, nex_out, accumulated_nex_out); -} - - /* * Async synthesis of assignments is done by synthesizing the rvalue * expression, then connecting the l-value directly to the output of @@ -68,15 +308,51 @@ bool NetProc::synth_sync(Design*des, NetScope*scope, */ bool NetAssignBase::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { + /* If the lval is a concatenation, synthesise each part + separately. */ + if (lval_->more ) { + /* Temporarily set the lval_ and rval_ fields for each + part in turn and recurse. Restore them when done. */ + NetAssign_*full_lval = lval_; + NetExpr*full_rval = rval_; + unsigned offset = 0; + bool flag = true; + while (lval_) { + unsigned width = lval_->lwidth(); + NetEConst*base = new NetEConst(verinum(offset)); + base->set_line(*this); + rval_ = new NetESelect(full_rval->dup_expr(), base, width); + rval_->set_line(*this); + eval_expr(rval_, width); + NetAssign_*more = lval_->more; + lval_->more = 0; + if (!synth_async(des, scope, nex_map, nex_out, enables, bitmasks)) + flag = false; + lval_ = lval_->more = more; + offset += width; + } + lval_ = full_lval; + rval_ = full_rval; + return flag; + } + NetNet*rsig = rval_->synthesize(des, scope, rval_); assert(rsig); + if (lval_->word() && ! dynamic_cast(lval_->word())) { + cerr << get_fileline() << ": sorry: Assignment to variable " + "location in memory is not currently supported in " + "synthesis." << endl; + des->errors += 1; + return false; + } + NetNet*lsig = lval_->sig(); if (!lsig) { cerr << get_fileline() << ": error: " - << "NetAssignBase::synth_async on unsupported lval "; + "NetAssignBase::synth_async on unsupported lval "; dump_lval(cerr); cerr << endl; des->errors += 1; @@ -100,45 +376,37 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, << ", nex_out.pin_count()==" << nex_out.pin_count() << endl; } - if (lval_->more ) { - unsigned base = 0, width = 1; - unsigned i = 0; - NetAssign_ *lval = lval_; - while (lval) { - NetNet *llsig = lval->sig(); - width = lval->lwidth(); - ivl_variable_type_t tmp_data_type = llsig->data_type(); - netvector_t *tmp_type = new netvector_t(tmp_data_type, llsig->vector_width()-1, 0); - - NetNet *tmp = new NetNet(scope, scope->local_symbol(), - NetNet::WIRE, NetNet::not_an_array, tmp_type); - tmp->local_flag(true); - NetPartSelect *ps = new NetPartSelect(rsig, base, width, NetPartSelect::VP); - ps->set_line(*this); - des->add_node(ps); - - connect(tmp->pin(0), ps->pin(0)); - connect(nex_out.pin(i), tmp->pin(0)); - - base += width; - i++; - lval->turn_sig_to_wire_on_release(); - lval = lval->more; - } - return true; + unsigned ptr = 0; + if (nex_out.pin_count() > 1) { + NexusSet tmp_set; + nex_output(tmp_set); + ivl_assert(*this, tmp_set.size() == 1); + ptr = nex_map.find_nexus(tmp_set[0]); + ivl_assert(*this, nex_out.pin_count() > ptr); + ivl_assert(*this, enables.pin_count() > ptr); + ivl_assert(*this, bitmasks.size() > ptr); + } else { + ivl_assert(*this, nex_out.pin_count() == 1); + ivl_assert(*this, enables.pin_count() == 1); + ivl_assert(*this, bitmasks.size() == 1); } + unsigned lval_width = lval_->lwidth(); + unsigned lsig_width = lsig->vector_width(); + ivl_assert(*this, nex_map[ptr].wid == lsig_width); + // Here we note if the l-value is actually a bit/part // select. If so, generate a NetPartSelect to perform the select. - if ((lval_->lwidth()!=lsig->vector_width()) && !scope->loop_index_tmp.empty()) { + bool is_part_select = lval_width != lsig_width; + + long base_off = 0; + if (is_part_select && !scope->loop_index_tmp.empty()) { // If we are within a NetForLoop, there may be an index // value. That is collected from the scope member // loop_index_tmp, and the evaluate_function method // knows how to apply it. ivl_assert(*this, !scope->loop_index_tmp.empty()); - ivl_assert(*this, lval_->lwidth() < lsig->vector_width()); - - long base_off = 0; + ivl_assert(*this, lval_width < lsig_width); // Evaluate the index expression to a constant. const NetExpr*base_expr_raw = lval_->get_base(); @@ -150,65 +418,67 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, ivl_assert(*this, base_off >= 0); ivl_variable_type_t tmp_data_type = rsig->data_type(); - netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig->vector_width()-1,0); + netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig_width-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp->local_flag(true); tmp->set_line(*this); - NetPartSelect*ps = new NetPartSelect(tmp, base_off, lval_->lwidth(), NetPartSelect::PV); + NetPartSelect*ps = new NetPartSelect(tmp, base_off, lval_width, NetPartSelect::PV); ps->set_line(*this); des->add_node(ps); connect(ps->pin(0), rsig->pin(0)); rsig = tmp; - } else if (lval_->lwidth() != lsig->vector_width()) { + } else if (is_part_select) { // In this case, there is no loop_index_tmp, so we are // not within a NetForLoop. Generate a NetSubstitute // object to handle the bit/part-select in the l-value. ivl_assert(*this, scope->loop_index_tmp.empty()); - ivl_assert(*this, lval_->lwidth() < lsig->vector_width()); - - long base_off = 0; + ivl_assert(*this, lval_width < lsig_width); const NetExpr*base_expr_raw = lval_->get_base(); ivl_assert(*this, base_expr_raw); NetExpr*base_expr = base_expr_raw->evaluate_function(*this, scope->loop_index_tmp); if (! eval_as_long(base_off, base_expr)) { - ivl_assert(*this, 0); + cerr << get_fileline() << ": sorry: assignment to variable " + "bit location is not currently supported in " + "synthesis." << endl; + des->errors += 1; + return false; } ivl_assert(*this, base_off >= 0); ivl_variable_type_t tmp_data_type = rsig->data_type(); - netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig->vector_width()-1,0); + netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig_width-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp->local_flag(true); tmp->set_line(*this); - ivl_assert(*this, accumulated_nex_out.pin_count()==1); - NetNet*use_lsig = lsig; - if (accumulated_nex_out.pin(0).is_linked()) { + NetNet*isig = nex_out.pin(ptr).nexus()->pick_any_net(); + if (isig) { if (debug_synth2) { cerr << get_fileline() << ": NetAssignBase::synth_async: " - << " Found a use_sig:" << endl; - accumulated_nex_out.pin(0).dump_link(cerr, 8); + << " Found an isig:" << endl; + nex_out.pin(ptr).dump_link(cerr, 8); } - Nexus*tmp_nex = accumulated_nex_out.pin(0).nexus(); - use_lsig = tmp_nex->pick_any_net(); - ivl_assert(*this, use_lsig); } else { if (debug_synth2) { cerr << get_fileline() << ": NetAssignBase::synth_async: " - << " Found no use_sig, resorting to lsig." << endl; + << " Found no isig, resorting to lsig." << endl; } + isig = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, NetNet::not_an_array, tmp_type); + isig->local_flag(true); + isig->set_line(*this); + connect(isig->pin(0), nex_out.pin(ptr)); } - NetSubstitute*ps = new NetSubstitute(use_lsig, rsig, - tmp->vector_width(), - base_off); + ivl_assert(*this, isig); + NetSubstitute*ps = new NetSubstitute(isig, rsig, lsig_width, base_off); ps->set_line(*this); des->add_node(ps); @@ -216,27 +486,36 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, rsig = tmp; } - if (nex_out.pin_count() > 1) { - NexusSet tmp_set; - nex_output(tmp_set); - ivl_assert(*this, tmp_set.size()==1); - unsigned ptr = nex_map.find_nexus(tmp_set[0]); - ivl_assert(*this, rsig->pin_count()==1); - ivl_assert(*this, nex_map.size()==nex_out.pin_count()); - ivl_assert(*this, nex_out.pin_count() > ptr); - connect(nex_out.pin(ptr), rsig->pin(0)); + rsig = crop_to_width(des, rsig, lsig_width); + ivl_assert(*this, rsig->pin_count()==1); + nex_out.pin(ptr).unlink(); + enables.pin(ptr).unlink(); + connect(nex_out.pin(ptr), rsig->pin(0)); + connect(enables.pin(ptr), scope->tie_hi()); + + mask_t&bitmask = bitmasks[ptr]; + if (is_part_select) { + if (bitmask.size() == 0) { + bitmask = mask_t (lsig_width, false); + } + ivl_assert(*this, bitmask.size() == lsig_width); + for (unsigned idx = 0; idx < lval_width; idx += 1) { + bitmask[base_off + idx] = true; + } + } else if (bitmask.size() > 0) { + for (unsigned idx = 0; idx < bitmask.size(); idx += 1) { + bitmask[idx] = true; + } } else { - ivl_assert(*this, nex_out.pin_count()==1); - ivl_assert(*this, rsig->pin_count()==1); - connect(nex_out.pin(0), rsig->pin(0)); + bitmask = mask_t (lsig_width, true); } /* This lval_ represents a reg that is a WIRE in the synthesized results. This function signals the destructor to change the REG that this l-value refers to into a WIRE. It is done then, at the last minute, so that pending - synthesis can continue to work with it as a WIRE. */ + synthesis can continue to work with it as a REG. */ lval_->turn_sig_to_wire_on_release(); return true; @@ -244,9 +523,15 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, bool NetProc::synth_async_block_substatement_(Design*des, NetScope*scope, NexusSet&nex_map, - NetBus&accumulated_nex_out, + NetBus&nex_out, + NetBus&enables, + vector&bitmasks, NetProc*substmt) { + ivl_assert(*this, nex_map.size() == nex_out.pin_count()); + ivl_assert(*this, nex_map.size() == enables.pin_count()); + ivl_assert(*this, nex_map.size() == bitmasks.size()); + // Create a temporary map of the output only from this statement. NexusSet tmp_map; substmt->nex_output(tmp_map); @@ -255,69 +540,69 @@ bool NetProc::synth_async_block_substatement_(Design*des, NetScope*scope, << "tmp_map.size()==" << tmp_map.size() << " for statement at " << substmt->get_fileline() << endl; - for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) { + for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " - << "accumulated_nex_out[" << idx << "] dump link" << endl; - accumulated_nex_out.pin(idx).dump_link(cerr, 8); + << "incoming nex_out[" << idx << "] dump link" << endl; + nex_out.pin(idx).dump_link(cerr, 8); } } - /* Create also a temporary NetBus to collect the - output from the synthesis. */ + // Create temporary variables to collect the output from the synthesis. NetBus tmp_out (scope, tmp_map.size()); + NetBus tmp_ena (scope, tmp_map.size()); + vector tmp_masks (tmp_map.size()); - // Map (and move) the accumulated_nex_out for this block - // to the version that we can pass to the next - // statement. We will move the result back later. - NetBus accumulated_tmp_out (scope, tmp_map.size()); - for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) { - unsigned ptr = tmp_map.find_nexus(nex_map[idx]); - if (ptr >= tmp_map.size()) - continue; - - connect(accumulated_tmp_out.pin(ptr), accumulated_nex_out.pin(idx)); - accumulated_nex_out.pin(idx).unlink(); + // Map (and move) the accumulated nex_out for this block + // to the version that we can pass to the next statement. + // We will move the result back later. + for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { + unsigned ptr = nex_map.find_nexus(tmp_map[idx]); + ivl_assert(*this, ptr < nex_out.pin_count()); + connect(tmp_out.pin(idx), nex_out.pin(ptr)); + nex_out.pin(ptr).unlink(); } if (debug_synth2) { for (unsigned idx = 0 ; idx < nex_map.size() ; idx += 1) { cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: nex_map[" << idx << "] dump link, base=" << nex_map[idx].base << ", wid=" << nex_map[idx].wid << endl; nex_map[idx].lnk.dump_link(cerr, 8); - } + } for (unsigned idx = 0 ; idx < tmp_map.size() ; idx += 1) { - cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: tmp_map[" << idx << "] dump link, base=" << tmp_map[idx].base << ", wid=" << tmp_map[idx].wid << endl; + cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: tmp_map[" << idx << "] dump link, base=" << tmp_map[idx].base << ", wid=" << tmp_map[idx].wid << endl; tmp_map[idx].lnk.dump_link(cerr, 8); - } - for (unsigned idx = 0 ; idx < accumulated_tmp_out.pin_count() ; idx += 1) { - cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: accumulated_tmp_out[" << idx << "] dump link" << endl; - accumulated_tmp_out.pin(idx).dump_link(cerr, 8); - } + } + for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { + cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: tmp_out[" << idx << "] dump link" << endl; + tmp_out.pin(idx).dump_link(cerr, 8); + } } - bool ok_flag = substmt->synth_async(des, scope, tmp_map, tmp_out, accumulated_tmp_out); + + bool flag = substmt->synth_async(des, scope, tmp_map, tmp_out, tmp_ena, tmp_masks); if (debug_synth2) { cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " - "substmt->synch_async(...) --> " << (ok_flag? "true" : "false") + "substmt->synch_async(...) --> " << (flag? "true" : "false") << " for statement at " << substmt->get_fileline() << "." << endl; } - if (ok_flag == false) - return false; + if (!flag) return false; // Now map the output from the substatement back to the - // accumulated_nex_out for this block. Look for the - // nex_map pin that is linked to the tmp_map.pin(idx) - // pin, and link that to the tmp_out.pin(idx) output link. + // outputs for this block. for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { unsigned ptr = nex_map.find_nexus(tmp_map[idx]); - ivl_assert(*this, ptr < accumulated_nex_out.pin_count()); + ivl_assert(*this, ptr < nex_out.pin_count()); if (debug_synth2) { - cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " + cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " << "tmp_out.pin(" << idx << "):" << endl; tmp_out.pin(idx).dump_link(cerr, 8); - } - connect(accumulated_nex_out.pin(ptr), tmp_out.pin(idx)); + } + connect(nex_out.pin(ptr), tmp_out.pin(idx)); + + merge_sequential_enables(des, scope, enables.pin(ptr), tmp_ena.pin(idx)); + + merge_sequential_masks(bitmasks[ptr], tmp_masks[idx]); } return true; @@ -325,13 +610,13 @@ bool NetProc::synth_async_block_substatement_(Design*des, NetScope*scope, /* * Sequential blocks are translated to asynchronous logic by - * translating each statement of the block, in order, into gates. The - * nex_out for the block is the union of the nex_out for all the - * substatements. + * translating each statement of the block, in order, into gates. + * The nex_out for the block is the union of the nex_out for all + * the substatements. */ bool NetBlock::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { if (last_ == 0) { return true; @@ -342,19 +627,12 @@ bool NetBlock::synth_async(Design*des, NetScope*scope, do { cur = cur->next_; - bool ok_flag = synth_async_block_substatement_(des, scope, nex_map, - accumulated_nex_out, - cur); - flag = flag && ok_flag; - if (ok_flag == false) - continue; + bool sub_flag = synth_async_block_substatement_(des, scope, nex_map, nex_out, + enables, bitmasks, cur); + flag = flag && sub_flag; } while (cur != last_); - // The output from the block is now the accumulated outputs. - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) - connect(nex_out.pin(idx), accumulated_nex_out.pin(idx)); - return flag; } @@ -399,7 +677,7 @@ static NetNet* mux_selector_reduce_width(Design*des, NetScope*scope, // Create the concat: osig = {...,...} NetConcat*osig_cat = new NetConcat(scope, scope->local_symbol(), - sel_need, 2, true); + sel_need, 2, !disable_concatz_generation); osig_cat->set_line(loc); des->add_node(osig_cat); connect(osig_cat->pin(0), osig->pin(0)); @@ -423,13 +701,13 @@ static NetNet* mux_selector_reduce_width(Design*des, NetScope*scope, // Create the part select esig[M-1:N-1] NetPartSelect*ps1 = new NetPartSelect(esig, sel_need-1, - sel_got-sel_need, + sel_got-sel_need+1, NetPartSelect::VP); ps1->set_line(loc); des->add_node(ps1); connect(ps1->pin(1), esig->pin(0)); - netvector_t*ps1_vec = new netvector_t(osig_data_type, sel_got-sel_need-1, 0); + netvector_t*ps1_vec = new netvector_t(osig_data_type, sel_got-sel_need, 0); NetNet*ps1_sig = new NetNet(scope, scope->local_symbol(), NetNet::TRI, ps1_vec); ps1_sig->local_flag(true); @@ -438,7 +716,7 @@ static NetNet* mux_selector_reduce_width(Design*des, NetScope*scope, // Create the reduction OR: | esig[M-1:N-1] NetUReduce*ered = new NetUReduce(scope, scope->local_symbol(), - NetUReduce::OR, sel_got-sel_need); + NetUReduce::OR, sel_got-sel_need+1); ered->set_line(loc); des->add_node(ered); connect(ered->pin(1), ps1_sig->pin(0)); @@ -457,17 +735,23 @@ static NetNet* mux_selector_reduce_width(Design*des, NetScope*scope, bool NetCase::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { if (type()==NetCase::EQZ || type()==NetCase::EQX) - return synth_async_casez_(des, scope, nex_map, nex_out, accumulated_nex_out); + return synth_async_casez_(des, scope, nex_map, nex_out, + enables, bitmasks); // Special case: If the case expression is constant, then this // is a pattern where the guards are non-constant and tested // against a constant case. Handle this as chained conditions // instead. if (dynamic_cast (expr_)) - return synth_async_casez_(des, scope, nex_map, nex_out, accumulated_nex_out); + return synth_async_casez_(des, scope, nex_map, nex_out, + enables, bitmasks); + + ivl_assert(*this, nex_map.size() == nex_out.pin_count()); + ivl_assert(*this, nex_map.size() == enables.pin_count()); + ivl_assert(*this, nex_map.size() == bitmasks.size()); if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async: " @@ -485,8 +769,6 @@ bool NetCase::synth_async(Design*des, NetScope*scope, << "selector width (sel_width) = " << sel_width << endl; } - ivl_assert(*this, nex_map.size() == nex_out.pin_count()); - vector mux_width (nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { mux_width[idx] = nex_map[idx].wid; @@ -497,27 +779,33 @@ bool NetCase::synth_async(Design*des, NetScope*scope, } } - // The accumulated_nex_out is taken as the input for this - // statement. Since there are collection of statements that - // start at this same point, we save all these inputs and - // reuse them for each statement. + // The incoming nex_out is taken as the input for this + // statement. Since there are collection of statements + // that start at this same point, we save all these + // inputs and reuse them for each statement. Unlink the + // nex_out now, so we can hook up the mux outputs. NetBus statement_input (scope, nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - connect(statement_input.pin(idx), accumulated_nex_out.pin(idx)); + connect(statement_input.pin(idx), nex_out.pin(idx)); + nex_out.pin(idx).unlink(); + if (debug_synth2) { + cerr << get_fileline() << ": NetCase::synth_async: " + << "statement_input.pin(" << idx << "):" << endl; + statement_input.pin(idx).dump_link(cerr, 8); + } } - /* Collect all the statements into a map of index to - statement. The guard expression it evaluated to be the - index of the mux value, and the statement is bound to that - index. */ + /* Collect all the statements into a map of index to statement. + The guard expression it evaluated to be the index of the mux + value, and the statement is bound to that index. */ unsigned long max_guard_value = 0; mapstatement_map; - NetProc*statement_default = 0; + NetProc*default_statement = 0; for (size_t item = 0 ; item < items_.size() ; item += 1) { if (items_[item].guard == 0) { - statement_default = items_[item].statement; + default_statement = items_[item].statement; continue; } @@ -547,18 +835,23 @@ bool NetCase::synth_async(Design*des, NetScope*scope, if (sel_idx > max_guard_value) max_guard_value = sel_idx; - ivl_assert(*this, items_[item].statement); - statement_map[sel_idx] = items_[item].statement; + if (items_[item].statement) { + statement_map[sel_idx] = items_[item].statement; + continue; + } + + // Handle the special case of an empty statement. + statement_map[sel_idx] = this; } // 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 // the sel_need to allow for the implicit selections. - if (statement_default && (sel_width > sel_need)) + if (default_statement && (sel_width > sel_need)) sel_need += 1; // The mux size is always an exact power of 2. @@ -587,139 +880,163 @@ bool NetCase::synth_async(Design*des, NetScope*scope, esig = mux_selector_reduce_width(des, scope, *this, esig, sel_need); } - if (!statement_default && (statement_map.size() != ((size_t)1 << sel_width))) { - cerr << get_fileline() << ": sorry: Latch inferred from " - << "incomplete case statement. This is not supported " - << "in synthesis." << endl; - des->errors += 1; - return false; + /* If there is a default clause, synthesize it once and we'll + link it in wherever it is needed. If there isn't, create + a dummy default to pass on the accumulated nex_out from + preceding statements. */ + NetBus default_out (scope, nex_out.pin_count()); + NetBus default_ena (scope, nex_out.pin_count()); + vector default_masks (nex_out.pin_count()); + + for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { + connect(default_out.pin(idx), statement_input.pin(idx)); + connect(default_ena.pin(idx), scope->tie_lo()); } - /* If there is a default clause, synthesize it once and we'll - link it in wherever it is needed. */ - NetBus default_bus (scope, nex_map.size()); - if (statement_default) { + if (default_statement) { - bool flag = synth_async_block_substatement_(des, scope, nex_map, - accumulated_nex_out, - statement_default); - if (!flag) { - return false; - } - - for (unsigned idx = 0 ; idx < default_bus.pin_count() ; idx += 1) { - connect(default_bus.pin(idx), accumulated_nex_out.pin(idx)); - accumulated_nex_out.pin(idx).unlink(); - } + bool flag = synth_async_block_substatement_(des, scope, nex_map, default_out, + default_ena, default_masks, + default_statement); + if (!flag) return false; if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async: " - << "synthesize default clause at " << statement_default->get_fileline() + << "synthesize default clause at " << default_statement->get_fileline() << " is done." << endl; } } - vector mux (mux_width.size()); - for (size_t mdx = 0 ; mdx < mux_width.size() ; mdx += 1) { - mux[mdx] = new NetMux(scope, scope->local_symbol(), - mux_width[mdx], mux_size, sel_need); - des->add_node(mux[mdx]); + vector out_mux (nex_out.pin_count()); + vector ena_mux (nex_out.pin_count()); + vector full_case (nex_out.pin_count()); + for (size_t mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + out_mux[mdx] = new NetMux(scope, scope->local_symbol(), + mux_width[mdx], mux_size, sel_need); + des->add_node(out_mux[mdx]); // The select signal is already synthesized, and is // common for every mux of this case statement. Simply // hook it up. - connect(mux[mdx]->pin_Sel(), esig->pin(0)); + connect(out_mux[mdx]->pin_Sel(), esig->pin(0)); // The outputs are in the nex_out, and connected to the // mux Result pins. - connect(mux[mdx]->pin_Result(), nex_out.pin(mdx)); + connect(out_mux[mdx]->pin_Result(), nex_out.pin(mdx)); // Make sure the output is now connected to a net. If // not, then create a fake one to carry the net-ness of // the pin. - if (mux[mdx]->pin_Result().nexus()->pick_any_net() == 0) { + if (out_mux[mdx]->pin_Result().nexus()->pick_any_net() == 0) { ivl_variable_type_t mux_data_type = IVL_VT_LOGIC; - netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0); + netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), - NetNet::TRI, tmp_vec); + NetNet::WIRE, tmp_vec); tmp->local_flag(true); ivl_assert(*this, tmp->vector_width() != 0); - connect(mux[mdx]->pin_Result(), tmp->pin(0)); + connect(out_mux[mdx]->pin_Result(), tmp->pin(0)); } + + // Create a mux for the enables, but don't hook it up + // until we know we need it. + ena_mux[mdx] = new NetMux(scope, scope->local_symbol(), + 1, mux_size, sel_need); + + // Assume a full case to start with. We'll check this as + // we synthesise each clause. + full_case[mdx] = true; } for (unsigned idx = 0 ; idx < mux_size ; idx += 1) { NetProc*stmt = statement_map[idx]; - if (stmt==0 && statement_default) { - ivl_assert(*this, default_bus.pin_count() == mux.size()); - for (size_t mdx = 0 ; mdx < mux.size() ; mdx += 1) - connect(mux[mdx]->pin_Data(idx), default_bus.pin(mdx)); - + if (stmt==0) { + ivl_assert(*this, default_out.pin_count() == out_mux.size()); + for (unsigned mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + connect(out_mux[mdx]->pin_Data(idx), default_out.pin(mdx)); + connect(ena_mux[mdx]->pin_Data(idx), default_ena.pin(mdx)); + merge_parallel_masks(bitmasks[mdx], default_masks[mdx]); + if (!default_ena.pin(mdx).is_linked(scope->tie_hi())) + full_case[mdx] = false; + } continue; } ivl_assert(*this, stmt); - - NetBus accumulated_tmp (scope, nex_map.size()); - for (unsigned pin = 0 ; pin < nex_map.size() ; pin += 1) - connect(accumulated_tmp.pin(pin), statement_input.pin(pin)); - - - synth_async_block_substatement_(des, scope, nex_map, accumulated_tmp, stmt); - - for (size_t mdx = 0 ; mdx < mux.size() ; mdx += 1) { - connect(mux[mdx]->pin_Data(idx), accumulated_tmp.pin(mdx)); - - if (mux[mdx]->pin_Data(idx).nexus()->pick_any_net()==0) { - cerr << get_fileline() << ": warning: case " << idx - << " has no input for mux slice " << mdx << "." << endl; - - ivl_variable_type_t mux_data_type = IVL_VT_LOGIC; - netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0); - NetNet*tmpn = new NetNet(scope, scope->local_symbol(), - NetNet::TRI, tmp_vec); - tmpn->local_flag(true); - ivl_assert(*this, tmpn->vector_width() != 0); - connect(mux[mdx]->pin_Data(idx), tmpn->pin(0)); + if (stmt == this) { + // Handle the special case of an empty statement. + ivl_assert(*this, statement_input.pin_count() == out_mux.size()); + for (unsigned mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + connect(out_mux[mdx]->pin_Data(idx), statement_input.pin(mdx)); + connect(ena_mux[mdx]->pin_Data(idx), scope->tie_lo()); + bitmasks[mdx] = mask_t (mux_width[mdx], false); + full_case[mdx] = false; } - ivl_assert(*this, mux[mdx]->pin_Data(idx).nexus()->pick_any_net()); + continue; + } + + NetBus tmp_out (scope, nex_out.pin_count()); + NetBus tmp_ena (scope, nex_out.pin_count()); + for (unsigned mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + connect(tmp_out.pin(mdx), statement_input.pin(mdx)); + connect(tmp_ena.pin(mdx), scope->tie_lo()); + } + vector tmp_masks (nex_out.pin_count()); + bool flag = synth_async_block_substatement_(des, scope, nex_map, tmp_out, + tmp_ena, tmp_masks, stmt); + if (!flag) return false; + + for (size_t mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + connect(out_mux[mdx]->pin_Data(idx), tmp_out.pin(mdx)); + connect(ena_mux[mdx]->pin_Data(idx), tmp_ena.pin(mdx)); + merge_parallel_masks(bitmasks[mdx], tmp_masks[mdx]); + if (!tmp_ena.pin(mdx).is_linked(scope->tie_hi())) + full_case[mdx] = false; } } + for (unsigned mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + // Optimize away the enable mux if we have a full case, + // otherwise hook it up. + if (full_case[mdx]) { + connect(enables.pin(mdx), scope->tie_hi()); + delete ena_mux[mdx]; + continue; + } + + des->add_node(ena_mux[mdx]); + + connect(ena_mux[mdx]->pin_Sel(), esig->pin(0)); + + connect(enables.pin(mdx), ena_mux[mdx]->pin_Result()); + + NetNet*tmp = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, &netvector_t::scalar_logic); + tmp->local_flag(true); + connect(ena_mux[mdx]->pin_Result(), tmp->pin(0)); + } return true; } /* * casez statements are hard to implement as a single wide mux because * the test doesn't really map to a select input. Instead, implement - * it as a chain of binary muxes. This gives the synthesizer my + * it as a chain of binary muxes. This gives the synthesizer more * flexibility, and is more typically what is desired from a casez anyhow. */ bool NetCase::synth_async_casez_(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { + ivl_assert(*this, nex_map.size() == nex_out.pin_count()); + ivl_assert(*this, nex_map.size() == enables.pin_count()); + ivl_assert(*this, nex_map.size() == bitmasks.size()); + /* Synthesize the select expression. */ NetNet*esig = expr_->synthesize(des, scope, expr_); unsigned sel_width = esig->vector_width(); ivl_assert(*this, sel_width > 0); - ivl_assert(*this, nex_map.size() == nex_out.pin_count()); - - if (debug_synth2) { - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - cerr << get_fileline() << ": NetCase::synth_async_casez_: " - << "nex_out.pin(" << idx << "):" << endl; - nex_out.pin(idx).dump_link(cerr, 8); - } - for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) { - cerr << get_fileline() << ": NetCase::synth_async_casez_: " - << "accumulated_nex_out.pin(" << idx << "):" << endl; - accumulated_nex_out.pin(idx).dump_link(cerr, 8); - } - } - vectormux_width (nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { mux_width[idx] = nex_map[idx].wid; @@ -730,42 +1047,50 @@ bool NetCase::synth_async_casez_(Design*des, NetScope*scope, } } - // The accumulated_nex_out is taken as the input for this - // statement. Since there are collection of statements that - // start at this same point, we save all these inputs and - // reuse them for each statement. + // The incoming nex_out is taken as the input for this + // statement. Since there are collection of statements + // that start at this same point, we save all these + // inputs and reuse them for each statement. Unlink the + // nex_out now, so we can hook up the mux outputs. NetBus statement_input (scope, nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - connect(statement_input.pin(idx), accumulated_nex_out.pin(idx)); + connect(statement_input.pin(idx), nex_out.pin(idx)); + nex_out.pin(idx).unlink(); + if (debug_synth2) { + cerr << get_fileline() << ": NetCase::synth_async_casez_: " + << "statement_input.pin(" << idx << "):" << endl; + statement_input.pin(idx).dump_link(cerr, 8); + } + } // Look for a default statement. - NetProc*statement_default = 0; + NetProc*default_statement = 0; for (size_t item = 0 ; item < items_.size() ; item += 1) { if (items_[item].guard != 0) continue; - ivl_assert(*this, statement_default==0); - statement_default = items_[item].statement; + ivl_assert(*this, default_statement==0); + default_statement = items_[item].statement; } - NetBus default_bus (scope, nex_out.pin_count()); - if (statement_default) { - bool flag = synth_async_block_substatement_(des, scope, nex_map, - accumulated_nex_out, - statement_default); - if (!flag) { - return false; - } + /* If there is a default clause, synthesize it once and we'll + link it in wherever it is needed. If there isn't, create + a dummy default to pass on the accumulated nex_out from + preceding statements. */ + NetBus default_out (scope, nex_out.pin_count()); - for (unsigned idx = 0 ; idx < default_bus.pin_count() ; idx += 1) { - connect(default_bus.pin(idx), accumulated_nex_out.pin(idx)); - accumulated_nex_out.pin(idx).unlink(); - } + for (unsigned idx = 0 ; idx < default_out.pin_count() ; idx += 1) + connect(default_out.pin(idx), statement_input.pin(idx)); + + if (default_statement) { + bool flag = synth_async_block_substatement_(des, scope, nex_map, default_out, + enables, bitmasks, default_statement); + if (!flag) return false; if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async_casez_: " - << "synthesize default clause at " << statement_default->get_fileline() + << "synthesize default clause at " << default_statement->get_fileline() << " is done." << endl; } } @@ -792,7 +1117,7 @@ bool NetCase::synth_async_casez_(Design*des, NetScope*scope, // the case select with the guard expression. The true input // (data1) is the current statement, and the false input is // the result of a later statement. - vectormux_prev (mux_width.size()); + vectorprev_mux (nex_out.pin_count()); for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { size_t item = items_.size()-idx-1; if (items_[item].guard == 0) @@ -808,8 +1133,8 @@ bool NetCase::synth_async_casez_(Design*des, NetScope*scope, sel_width, case_kind); des->add_node(condit_dev); condit_dev->set_line(*this); - // Note that the expression that may have windcards must - // go in the pin(2) input. This is the definiton of the + // Note that the expression that may have wildcards must + // go in the pin(2) input. This is the definition of the // NetCaseCmp statement. connect(condit_dev->pin(1), esig->pin(0)); connect(condit_dev->pin(2), guard->pin(0)); @@ -821,53 +1146,59 @@ bool NetCase::synth_async_casez_(Design*des, NetScope*scope, connect(condit_dev->pin(0), condit->pin(0)); // Synthesize the guarded statement. - NetBus true_bus (scope, nex_out.pin_count()); - for (unsigned pin = 0 ; pin < nex_map.size() ; pin += 1) - connect(true_bus.pin(pin), statement_input.pin(pin)); + NetBus tmp_out (scope, nex_out.pin_count()); + NetBus tmp_ena (scope, nex_out.pin_count()); + vector tmp_masks (nex_out.pin_count()); - synth_async_block_substatement_(des, scope, nex_map, true_bus, stmt); + for (unsigned pdx = 0 ; pdx < nex_out.pin_count() ; pdx += 1) + connect(tmp_out.pin(pdx), statement_input.pin(pdx)); - for (unsigned mdx = 0 ; mdx < mux_width.size() ; mdx += 1) { - NetMux*mux_cur = new NetMux(scope, scope->local_symbol(), - mux_width[mdx], 2, 1); - des->add_node(mux_cur); - mux_cur->set_line(*this); - connect(mux_cur->pin_Sel(), condit->pin(0)); + synth_async_block_substatement_(des, scope, nex_map, tmp_out, + tmp_ena, tmp_masks, stmt); - connect(mux_cur->pin_Data(1), true_bus.pin(mdx)); + NetBus prev_ena (scope, nex_out.pin_count()); + for (unsigned mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + NetMux*mux = new NetMux(scope, scope->local_symbol(), + mux_width[mdx], 2, 1); + des->add_node(mux); + mux->set_line(*this); + connect(mux->pin_Sel(), condit->pin(0)); + + connect(mux->pin_Data(1), tmp_out.pin(mdx)); // If there is a previous mux, then use that as the - // false clause input. Otherwise, use the - // default. But wait, if there is no default, then - // use the accumulated input. - if (mux_prev[mdx]) { - connect(mux_cur->pin_Data(0), mux_prev[mdx]->pin_Result()); - } else if (default_bus.pin(mdx).is_linked()) { - connect(mux_cur->pin_Data(0), default_bus.pin(mdx)); - - } else { - connect(mux_cur->pin_Data(0), statement_input.pin(mdx)); - } + // false clause input. Otherwise, use the default. + if (prev_mux[mdx]) + connect(mux->pin_Data(0), prev_mux[mdx]->pin_Result()); + else + connect(mux->pin_Data(0), default_out.pin(mdx)); // Make a NetNet for the result. ivl_variable_type_t mux_data_type = IVL_VT_LOGIC; - netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0); + netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), - NetNet::TRI, tmp_vec); + NetNet::WIRE, tmp_vec); tmp->local_flag(true); tmp->set_line(*this); ivl_assert(*this, tmp->vector_width() != 0); - connect(mux_cur->pin_Result(), tmp->pin(0)); + connect(mux->pin_Result(), tmp->pin(0)); // This mux becomes the "false" input to the next mux. - mux_prev[mdx] = mux_cur; + prev_mux[mdx] = mux; + + connect(prev_ena.pin(mdx), enables.pin(mdx)); + enables.pin(mdx).unlink(); + + multiplex_enables(des, scope, condit, tmp_ena.pin(mdx), + prev_ena.pin(mdx), enables.pin(mdx)); + + merge_parallel_masks(bitmasks[mdx], tmp_masks[mdx]); } } // Connect the last mux to the output. - for (size_t mdx = 0 ; mdx < mux_prev.size() ; mdx += 1) { - connect(mux_prev[mdx]->pin_Result(), nex_out.pin(mdx)); - } + for (size_t mdx = 0 ; mdx < prev_mux.size() ; mdx += 1) + connect(prev_mux[mdx]->pin_Result(), nex_out.pin(mdx)); return true; } @@ -880,45 +1211,30 @@ bool NetCase::synth_async_casez_(Design*des, NetScope*scope, */ bool NetCondit::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { - if (if_ == 0) { - return false; - } - if (else_ == 0) { - bool latch_flag = false; - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - if (! accumulated_nex_out.pin(idx).is_linked()) - latch_flag = true; - } - if (latch_flag) { - cerr << get_fileline() << ": error: Asynchronous if statement" - << " cannot synthesize missing \"else\"" - << " without generating latches." << endl; - //return false; - } - } + // Handle the unlikely case that both clauses are empty. + if ((if_ == 0) && (else_ == 0)) + return true; + + ivl_assert(*this, nex_map.size() == nex_out.pin_count()); + ivl_assert(*this, nex_map.size() == enables.pin_count()); + ivl_assert(*this, nex_map.size() == bitmasks.size()); - ivl_assert(*this, if_ != 0); // Synthesize the condition. This will act as a select signal // for a binary mux. NetNet*ssig = expr_->synthesize(des, scope, expr_); - assert(ssig); - - if (debug_synth2) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "Synthesize if clause at " << if_->get_fileline() - << endl; - for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count(); idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "accumulated_nex_out.pin(" << idx << "):" << endl; - accumulated_nex_out.pin(idx).dump_link(cerr, 8); - } - } + ivl_assert(*this, ssig); + // The incoming nex_out is taken as the input for this + // statement. Since there are two statements that start + // at this same point, we save all these inputs and reuse + // them for both statements. Unlink the nex_out now, so + // we can hook up the mux outputs. NetBus statement_input (scope, nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - connect(statement_input.pin(idx), accumulated_nex_out.pin(idx)); + connect(statement_input.pin(idx), nex_out.pin(idx)); + nex_out.pin(idx).unlink(); if (debug_synth2) { cerr << get_fileline() << ": NetCondit::synth_async: " << "statement_input.pin(" << idx << "):" << endl; @@ -926,105 +1242,85 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, } } - bool flag; - NetBus asig(scope, nex_out.pin_count()); - flag = if_->synth_async(des, scope, nex_map, asig, accumulated_nex_out); - if (!flag) { - return false; - } - - NetBus btmp(scope, nex_out.pin_count()); - NetBus bsig(scope, nex_out.pin_count()); - - if (else_==0) { - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - connect(bsig.pin(idx), accumulated_nex_out.pin(idx)); - accumulated_nex_out.pin(idx).unlink(); + NetBus a_out (scope, nex_out.pin_count()); + NetBus a_ena (scope, nex_out.pin_count()); + vector a_masks (nex_out.pin_count()); + if (if_) { + if (debug_synth2) { + cerr << get_fileline() << ": NetCondit::synth_async: " + << "Synthesize if clause at " << if_->get_fileline() + << endl; } - } else { + for (unsigned idx = 0 ; idx < a_out.pin_count() ; idx += 1) { + connect(a_out.pin(idx), statement_input.pin(idx)); + } + bool flag = synth_async_block_substatement_(des, scope, nex_map, a_out, + a_ena, a_masks, if_); + if (!flag) return false; + + } else { + for (unsigned idx = 0 ; idx < a_out.pin_count() ; idx += 1) { + connect(a_out.pin(idx), statement_input.pin(idx)); + connect(a_ena.pin(idx), scope->tie_lo()); + } + } + + NetBus b_out(scope, nex_out.pin_count()); + NetBus b_ena(scope, nex_out.pin_count()); + vector b_masks (nex_out.pin_count()); + if (else_) { if (debug_synth2) { cerr << get_fileline() << ": NetCondit::synth_async: " << "Synthesize else clause at " << else_->get_fileline() << endl; - for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count(); idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "accumulated_nex_out.pin(" << idx << "):" << endl; - accumulated_nex_out.pin(idx).dump_link(cerr, 8); - } - for (unsigned idx = 0 ; idx < statement_input.pin_count() ; idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "statement_input.pin(" << idx << "):" << endl; - statement_input.pin(idx).dump_link(cerr, 8); - } } - NetBus accumulated_btmp_out (scope, statement_input.pin_count()); - for (unsigned idx = 0 ; idx < accumulated_btmp_out.pin_count() ; idx += 1) { - if (statement_input.pin(idx).is_linked()) - connect(accumulated_btmp_out.pin(idx), statement_input.pin(idx)); - else - connect(accumulated_btmp_out.pin(idx), accumulated_nex_out.pin(idx)); - } - if (debug_synth2) { - for (unsigned idx = 0 ; idx < accumulated_btmp_out.pin_count() ; idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "accumulated_btmp_out.pin(" << idx << "):" << endl; - accumulated_btmp_out.pin(idx).dump_link(cerr, 8); - } + for (unsigned idx = 0 ; idx < b_out.pin_count() ; idx += 1) { + connect(b_out.pin(idx), statement_input.pin(idx)); } - flag = synth_async_block_substatement_(des, scope, nex_map, accumulated_btmp_out, else_); - if (!flag) { - return false; - } - if (debug_synth2) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "synthesize else clause at " << else_->get_fileline() - << " is done." << endl; - for (unsigned idx = 0 ; idx < accumulated_btmp_out.pin_count() ; idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "accumulated_btmp_out.pin(" << idx << "):" << endl; - accumulated_btmp_out.pin(idx).dump_link(cerr, 8); - } - } - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - connect(bsig.pin(idx), accumulated_btmp_out.pin(idx)); - accumulated_btmp_out.pin(idx).unlink(); - } + bool flag = synth_async_block_substatement_(des, scope, nex_map, b_out, + b_ena, b_masks, else_); + if (!flag) return false; + } else { + for (unsigned idx = 0 ; idx < b_out.pin_count() ; idx += 1) { + connect(b_out.pin(idx), statement_input.pin(idx)); + connect(b_ena.pin(idx), scope->tie_lo()); + } } - /* The nex_out output, asig input, and bsig input all have the + /* The nex_out output, a_out input, and b_out input all have the same pin count (usually, but not always 1) because they are net arrays of the same dimension. The for loop below creates a NetMux for each pin of the output. (Note that pins may be, in fact usually are, vectors.) */ - ivl_assert(*this, nex_out.pin_count()==asig.pin_count()); - ivl_assert(*this, nex_out.pin_count()==bsig.pin_count()); - - bool rc_flag = true; for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - // It should not be possible for the a (true) or b - // (false) signals to be missing. If either is, print a - // warning and clear a flag so that the rest of this - // code can find a way to cope. - bool asig_is_present = true; - if (! asig.pin(idx).nexus()->pick_any_net()) { - cerr << get_fileline() << ": warning: " - << "True clause of conditional statement might not" - << " drive all expected outputs." << endl; - asig_is_present = false; + + bool a_driven = a_out.pin(idx).nexus()->pick_any_net(); + bool b_driven = b_out.pin(idx).nexus()->pick_any_net(); + if (!a_driven && !b_driven) { + connect(nex_out.pin(idx), statement_input.pin(idx)); + continue; } - bool bsig_is_present = true; - if (! bsig.pin(idx).nexus()->pick_any_net()) { - cerr << get_fileline() << ": warning: " - << "False clause of conditional statement might not" - << " drive all expected outputs." << endl; - bsig_is_present = false; + merge_parallel_masks(bitmasks[idx], a_masks[idx]); + merge_parallel_masks(bitmasks[idx], b_masks[idx]); + + // If one clause is empty and the other clause unconditionally + // drives all bits of the vector, we can rely on the enable + // to prevent the flip-flop or latch updating when the empty + // clause is selected, and hence don't need a mux. + if (!a_driven && all_bits_driven(b_masks[idx])) { + connect(nex_out.pin(idx), b_out.pin(idx)); + continue; + } + if (!b_driven && all_bits_driven(a_masks[idx])) { + connect(nex_out.pin(idx), a_out.pin(idx)); + continue; } // Guess the mux type from the type of the output. @@ -1034,38 +1330,22 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, } unsigned mux_off = 0; - unsigned mux_width; - if (asig_is_present) - mux_width = asig.pin(idx).nexus()->vector_width(); - else if (bsig_is_present) - mux_width = bsig.pin(idx).nexus()->vector_width(); - else - mux_width = 0; + unsigned mux_width = nex_map[idx].wid; if (debug_synth2) { - if (asig_is_present) - cerr << get_fileline() << ": NetCondit::synth_async: " - << "asig_is_present," - << " asig width=" << asig.pin(idx).nexus()->vector_width() - << endl; - if (bsig_is_present) - cerr << get_fileline() << ": NetCondit::synth_async: " - << "bsig_is_present," - << " bsig width=" << bsig.pin(idx).nexus()->vector_width() - << endl; cerr << get_fileline() << ": NetCondit::synth_async: " << "Calculated mux_width=" << mux_width << endl; } - NetPartSelect*apv = detect_partselect_lval(asig.pin(idx)); + NetPartSelect*apv = detect_partselect_lval(a_out.pin(idx)); if (debug_synth2 && apv) { cerr << get_fileline() << ": NetCondit::synth_async: " << "Assign-to-part apv base=" << apv->base() << ", width=" << apv->width() << endl; } - NetPartSelect*bpv = detect_partselect_lval(bsig.pin(idx)); + NetPartSelect*bpv = detect_partselect_lval(b_out.pin(idx)); if (debug_synth2 && bpv) { cerr << get_fileline() << ": NetCondit::synth_async: " << "Assign-to-part bpv base=" << bpv->base() @@ -1082,10 +1362,10 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, // manipulates the width of the part. mux_width = apv->width(); mux_off = apv->base(); - asig.pin(idx).unlink(); - bsig.pin(idx).unlink(); - connect(asig.pin(idx), apv->pin(0)); - connect(bsig.pin(idx), bpv->pin(0)); + a_out.pin(idx).unlink(); + b_out.pin(idx).unlink(); + connect(a_out.pin(idx), apv->pin(0)); + connect(b_out.pin(idx), bpv->pin(0)); delete apv; delete bpv; } else { @@ -1094,33 +1374,10 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, bpv = 0; } - if (bsig_is_present && mux_width != bsig.pin(idx).nexus()->vector_width()) { - cerr << get_fileline() << ": internal error: " - << "NetCondit::synth_async: " - << "Mux input sizes do not match." - << " A size=" << mux_lwidth - << ", B size=" << bsig.pin(idx).nexus()->vector_width() - << endl; - cerr << get_fileline() << ": : " - << "asig node pins:" << endl; - asig.dump_node_pins(cerr, 8); - cerr << get_fileline() << ": : " - << "if_ statement:" << endl; - if_->dump(cerr, 8); - cerr << get_fileline() << ": : " - << "bsig node pins:" << endl; - bsig.dump_node_pins(cerr, 4); - if (else_) { - cerr << get_fileline() << ": : " - << "else_ statement:" << endl; - else_->dump(cerr, 8); - } - rc_flag = false; - } - NetMux*mux = new NetMux(scope, scope->local_symbol(), mux_width, 2, 1); mux->set_line(*this); + des->add_node(mux); netvector_t*tmp_type = 0; if (mux_width==1) @@ -1136,92 +1393,55 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, connect(mux->pin_Result(),otmp->pin(0)); connect(mux->pin_Sel(), ssig->pin(0)); - connect(mux->pin_Data(1), asig.pin(idx)); - connect(mux->pin_Data(0), bsig.pin(idx)); + connect(mux->pin_Data(1), a_out.pin(idx)); + connect(mux->pin_Data(0), b_out.pin(idx)); - if (! asig_is_present) { - tmp_type = new netvector_t(mux_data_type, mux_width-1,0); - NetNet*tmp = new NetNet(scope, scope->local_symbol(), - NetNet::WIRE, NetNet::not_an_array, tmp_type); - tmp->local_flag(true); - tmp->set_line(*this); - connect(mux->pin_Data(1), tmp->pin(0)); - } - - if (! bsig_is_present) { - tmp_type = new netvector_t(mux_data_type, mux_width-1,0); - NetNet*tmp = new NetNet(scope, scope->local_symbol(), - NetNet::WIRE, NetNet::not_an_array, tmp_type); - tmp->local_flag(true); - tmp->set_line(*this); - connect(mux->pin_Data(0), tmp->pin(0)); - } - - // We are only muxing a part of the output vector, so - // make a NetPartSelect::PV to widen the vector to the - // output at hand. + // If we are only muxing a part of the output vector, make a + // NetSubstitute to blend the mux output with the accumulated + // output from previous statements. if (mux_width < mux_lwidth) { tmp_type = new netvector_t(mux_data_type, mux_lwidth-1,0); + + NetNet*itmp = statement_input.pin(idx).nexus()->pick_any_net(); + if (itmp == 0) { + itmp = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, NetNet::not_an_array, tmp_type); + itmp->local_flag(true); + itmp->set_line(*this); + connect(itmp->pin(0), statement_input.pin(idx)); + } + NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp->local_flag(true); tmp->set_line(*this); - NetPartSelect*ps = new NetPartSelect(tmp, mux_off, mux_width, NetPartSelect::PV); + NetSubstitute*ps = new NetSubstitute(itmp, otmp, mux_lwidth, mux_off); des->add_node(ps); - connect(ps->pin(0), otmp->pin(0)); + connect(ps->pin(0), tmp->pin(0)); otmp = tmp; } connect(nex_out.pin(idx), otmp->pin(0)); - - // Handle the special case that this NetMux is only - // assigning to a part of the vector. If that is the - // case, then we need to blend this output with the - // already calculated input to this statement so that we - // don't accidentally disconnect the other drivers to - // other bits. - // FIXME: NEED TO CHECK THAT THESE DRIVERS DON'T - // OVERLAP. THIS CODE CURRENTLY DOESN'T DO THAT TEST. - if (mux_width < mux_lwidth && if_ && else_) { - if (debug_synth2) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "This NetMux only impacts a few bits of output," - << " so combine nex_out with statement input." - << endl; - cerr << get_fileline() << ": NetCondit::synth_async: " - << "MISSING TEST FOR CORRECTNESS OF THE BLEND!" - << endl; - } - vectormask = statement_input.pin(idx).nexus()->driven_mask(); - // If the mask is empty then there are no bits in the - // nexus to check yet. - if (! mask.empty()) { - for (size_t bit = mux_off; - bit < mux_off+mux_width; - bit += 1) { - ivl_assert(*this, mask[bit]==false); - } - } - connect(nex_out.pin(idx), statement_input.pin(idx)); - } - - des->add_node(mux); } - return rc_flag; + for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { + multiplex_enables(des, scope, ssig, a_ena.pin(idx), b_ena.pin(idx), enables.pin(idx)); + } + + return true; } bool NetEvWait::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { - bool flag = statement_->synth_async(des, scope, nex_map, nex_out, accumulated_nex_out); + bool flag = statement_->synth_async(des, scope, nex_map, nex_out, enables, bitmasks); return flag; } bool NetForLoop::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { if (debug_synth2) { cerr << get_fileline() << ": NetForLoop::synth_async: " @@ -1277,9 +1497,16 @@ bool NetForLoop::synth_async(Design*des, NetScope*scope, ivl_assert(*this, scope->loop_index_tmp.empty()); scope->loop_index_tmp = index_args; - rc = synth_async_block_substatement_(des, scope, nex_map, - accumulated_nex_out, - statement_); + NetBus tmp_ena (scope, nex_out.pin_count()); + vector tmp_masks (nex_out.pin_count()); + + rc = synth_async_block_substatement_(des, scope, nex_map, nex_out, + tmp_ena, tmp_masks, statement_); + + for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { + merge_sequential_enables(des, scope, enables.pin(idx), tmp_ena.pin(idx)); + merge_sequential_masks(bitmasks[idx], tmp_masks[idx]); + } scope->loop_index_tmp.clear(); @@ -1314,10 +1541,6 @@ bool NetForLoop::synth_async(Design*des, NetScope*scope, delete index_var.value; - // The output from the block is now the accumulated outputs. - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) - connect(nex_out.pin(idx), accumulated_nex_out.pin(idx)); - return true; } @@ -1339,32 +1562,93 @@ bool NetProcTop::synth_async(Design*des) << "Process has " << nex_set.size() << " outputs." << endl; } - NetBus nex_q (scope(), nex_set.size()); + NetBus nex_out (scope(), nex_set.size()); + NetBus enables (scope(), nex_set.size()); + vector bitmasks (nex_set.size()); + + // Save links to the initial nex_out. These will be used later + // to detect floating part-substitute and mux inputs that need + // to be tied off. + NetBus nex_in (scope(), nex_out.pin_count()); + for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) + connect(nex_in.pin(idx), nex_out.pin(idx)); + + bool flag = statement_->synth_async(des, scope(), nex_set, nex_out, enables, bitmasks); + if (!flag) return false; + + flag = tie_off_floating_inputs_(des, nex_set, nex_in, bitmasks, false); + if (!flag) return false; + for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) { - NexusSet::elem_t&item = nex_set[idx]; - if (item.base != 0 || item.wid!=item.lnk.nexus()->vector_width()) { - ivl_variable_type_t tmp_data_type = IVL_VT_LOGIC; - netvector_t*tmp_type = new netvector_t(tmp_data_type, item.lnk.nexus()->vector_width()-1,0); - NetNet*tmp_sig = new NetNet(scope(), scope()->local_symbol(), - NetNet::WIRE, NetNet::not_an_array, tmp_type); - tmp_sig->local_flag(true); - tmp_sig->set_line(*this); - - NetPartSelect*tmp = new NetPartSelect(tmp_sig, item.base, - item.wid, NetPartSelect::PV); - des->add_node(tmp); - tmp->set_line(*this); - connect(tmp->pin(0), nex_q.pin(idx)); - connect(item.lnk, tmp_sig->pin(0)); + if (enables.pin(idx).is_linked(scope()->tie_hi())) { + connect(nex_set[idx].lnk, nex_out.pin(idx)); } else { - connect(item.lnk, nex_q.pin(idx)); + cerr << get_fileline() << ": warning: " + << "A latch has been inferred for '" + << nex_set[idx].lnk.nexus()->pick_any_net()->name() + << "'." << endl; + + if (enables.pin(idx).nexus()->pick_any_net()->local_flag()) { + cerr << get_fileline() << ": warning: The latch " + "enable is connected to a synthesized " + "expression. The latch may be sensitive " + "to glitches." << endl; + } + + if (debug_synth2) { + cerr << get_fileline() << ": debug: " + << "Top level making a " + << nex_set[idx].wid << "-wide " + << "NetLatch device." << endl; + } + + NetLatch*latch = new NetLatch(scope(), scope()->local_symbol(), + nex_set[idx].wid); + des->add_node(latch); + latch->set_line(*this); + + NetNet*tmp = nex_out.pin(idx).nexus()->pick_any_net(); + tmp->set_line(*this); + assert(tmp); + + tmp = crop_to_width(des, tmp, latch->width()); + + connect(nex_set[idx].lnk, latch->pin_Q()); + connect(tmp->pin(0), latch->pin_Data()); + + assert (enables.pin(idx).is_linked()); + connect(enables.pin(idx), latch->pin_Enable()); } } - NetBus tmp_q (scope(), nex_set.size()); - bool flag = statement_->synth_async(des, scope(), nex_set, nex_q, tmp_q); - return flag; + synthesized_design_ = des; + return true; +} + + +bool NetProc::synth_sync(Design*des, NetScope*scope, + bool& /* ff_negedge */, + NetNet* /* ff_clk */, NetBus&ff_ce, + NetBus& /* ff_aclr*/, NetBus& /* ff_aset*/, + vector& /*ff_aset_value*/, + NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, + const vector&events) +{ + if (events.size() > 0) { + cerr << get_fileline() << ": error: Events are unaccounted" + << " for in process synthesis." << endl; + des->errors += 1; + } + + if (debug_synth2) { + cerr << get_fileline() << ": NetProc::synth_sync: " + << "This statement is an async input to a sync process." << endl; + } + + /* Synthesize the input to the DFF. */ + return synth_async(des, scope, nex_map, nex_out, ff_ce, bitmasks); } /* @@ -1373,18 +1657,21 @@ bool NetProcTop::synth_async(Design*des) * invoked for input like this: * * always @(posedge clk...) begin - * - * - * ... + * + * + * ... * end * * This needs to be split into a DFF bank for each statement, because - * the statements may each infer different reset and enable signals. + * the statements may each infer different reset and enables signals. */ bool NetBlock::synth_sync(Design*des, NetScope*scope, + bool&ff_negedge, NetNet*ff_clk, NetBus&ff_ce, NetBus&ff_aclr,NetBus&ff_aset, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const vector&events_in) { if (debug_synth2) { @@ -1392,58 +1679,59 @@ bool NetBlock::synth_sync(Design*des, NetScope*scope, << "Examine this block for synchronous logic." << endl; } + if (last_ == 0) { + return true; + } + bool flag = true; NetProc*cur = last_; do { cur = cur->next_; - /* Create a temporary nex_map for the substatement. */ - NexusSet tmp_set; - cur->nex_output(tmp_set); + // Create a temporary nex_map for the substatement. + NexusSet tmp_map; + cur->nex_output(tmp_map); - /* Create also a temporary net_out to collect the - output. The tmp1 and tmp2 map and out sets together - are used to collect the outputs from the substatement - for the inputs of the FF bank. */ - NetBus tmp_out (scope, tmp_set.size()); + // Create temporary variables to collect the output from the synthesis. + NetBus tmp_out (scope, tmp_map.size()); + NetBus tmp_ce (scope, tmp_map.size()); + vector tmp_masks (tmp_map.size()); - /* Create a temporary ff_ce (FF clock-enable) that - accounts for the subset of outputs that this - substatement drives. This allows for the possibility - that the substatement has CE patterns of its own. */ - NetBus tmp_ce (scope, tmp_set.size()); - for (unsigned idx = 0 ; idx < tmp_ce.pin_count() ; idx += 1) { - unsigned ptr = nex_map.find_nexus(tmp_set[idx]); + // Map (and move) the accumulated nex_out for this block + // to the version that we can pass to the next statement. + // We will move the result back later. + for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { + unsigned ptr = nex_map.find_nexus(tmp_map[idx]); ivl_assert(*this, ptr < nex_out.pin_count()); - if (ff_ce.pin(ptr).is_linked()) { - connect(tmp_ce.pin(idx), ff_ce.pin(ptr)); - ff_ce.pin(ptr).unlink(); - } + connect(tmp_out.pin(idx), nex_out.pin(ptr)); + nex_out.pin(ptr).unlink(); } /* Now go on with the synchronous synthesis for this subset of the statement. The tmp_map is the output nexa that we expect, and the tmp_out is where we want those outputs connected. */ - bool ok_flag = cur->synth_sync(des, scope, ff_clk, tmp_ce, - ff_aclr, ff_aset, - tmp_set, tmp_out, events_in); + bool ok_flag = cur->synth_sync(des, scope, + ff_negedge, ff_clk, tmp_ce, + ff_aclr, ff_aset, ff_aset_value, + tmp_map, tmp_out, tmp_masks, + events_in); flag = flag && ok_flag; if (ok_flag == false) continue; - /* Use the nex_map to link up the output from the - substatement to the output of the block as a - whole. It is occasionally possible to have outputs - beyond the input set, for example when the l-value of - an assignment is smaller than the r-value. */ + // Now map the output from the substatement back to the + // outputs for this block. for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { - unsigned ptr = nex_map.find_nexus(tmp_set[idx]); + unsigned ptr = nex_map.find_nexus(tmp_map[idx]); ivl_assert(*this, ptr < nex_out.pin_count()); connect(nex_out.pin(ptr), tmp_out.pin(idx)); - connect(ff_ce.pin(ptr), tmp_ce.pin(idx)); + + merge_sequential_enables(des, scope, ff_ce.pin(ptr), tmp_ce.pin(idx)); + + merge_sequential_masks(bitmasks[ptr], tmp_masks[idx]); } } while (cur != last_); @@ -1463,9 +1751,12 @@ bool NetBlock::synth_sync(Design*des, NetScope*scope, * expression is connected to an event, or not. */ bool NetCondit::synth_sync(Design*des, NetScope*scope, + bool&ff_negedge, NetNet*ff_clk, NetBus&ff_ce, NetBus&ff_aclr,NetBus&ff_aset, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const vector&events_in) { /* First try to turn the condition expression into an @@ -1488,22 +1779,55 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope, NetNet*rst = expr_->synthesize(des, scope, expr_); ivl_assert(*this, rst->pin_count() == 1); - /* XXXX I really should find a way to check that the - edge used on the reset input is correct. This would - involve interpreting the expression that is fed by the - reset expression. */ - ivl_assert(*this, ev->edge() == NetEvProbe::POSEDGE); + // Check that the edge used on the set/reset input is correct. + switch (ev->edge()) { + case NetEvProbe::POSEDGE: + if (ev->pin(0).nexus() != rst->pin(0).nexus()) { + cerr << get_fileline() << ": error: " + << "Condition for posedge asynchronous set/reset " + << "must exactly match the event expression." << endl; + des->errors += 1; + return false; + } + break; + case NetEvProbe::NEGEDGE: { + bool is_inverter = false; + NetNode*node = rst->pin(0).nexus()->pick_any_node(); + if (NetLogic*gate = dynamic_cast(node)) { + if (gate->type() == NetLogic::NOT) + is_inverter = true; + } + if (NetUReduce*gate = dynamic_cast(node)) { + if (gate->type() == NetUReduce::NOR) + is_inverter = true; + } + if (!is_inverter || ev->pin(0).nexus() != node->pin(1).nexus()) { + cerr << get_fileline() << ": error: " + << "Condition for negedge asynchronous set/reset must be " + << "a simple inversion of the event expression." << endl; + des->errors += 1; + return false; + } + break; + } + default: + cerr << get_fileline() << ": error: " + << "Asynchronous set/reset event must be " + << "edge triggered." << endl; + des->errors += 1; + return false; + } // Synthesize the true clause to figure out what kind of // set/reset we have. This should synthesize down to a // constant. If not, we have an asynchronous LOAD, a // very different beast. ivl_assert(*this, if_); - bool flag; NetBus tmp_out(scope, nex_out.pin_count()); - NetBus accumulated_tmp_out(scope, nex_out.pin_count()); - flag = if_->synth_async(des, scope, nex_map, tmp_out, accumulated_tmp_out); - if (! flag) return false; + NetBus tmp_ena(scope, nex_out.pin_count()); + vector tmp_masks (nex_out.pin_count()); + bool flag = if_->synth_async(des, scope, nex_map, tmp_out, tmp_ena, tmp_masks); + if (!flag) return false; ivl_assert(*this, tmp_out.pin_count() == ff_aclr.pin_count()); ivl_assert(*this, tmp_out.pin_count() == ff_aset.pin_count()); @@ -1511,25 +1835,33 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope, for (unsigned pin = 0 ; pin < tmp_out.pin_count() ; pin += 1) { Nexus*rst_nex = tmp_out.pin(pin).nexus(); - vector rst_mask = rst_nex->driven_mask(); - if (debug_synth2) { - cerr << get_fileline() << ": NetCondit::synth_sync: " - << "nex_out pin=" << pin - << ", rst_mask.size()==" << rst_mask.size() - << ", rst_nex->vector_width()=" << rst_nex->vector_width() - << endl; + if (!all_bits_driven(tmp_masks[pin])) { + cerr << get_fileline() << ": sorry: Not all bits of '" + << nex_map[idx].lnk.nexus()->pick_any_net()->name() + << "' are asynchronously set or reset. This is " + << "not currently supported in synthesis." << endl; + des->errors += 1; + return false; } - for (size_t bit = 0 ; bit < rst_mask.size() ; bit += 1) { - if (rst_mask[bit]==false) { - cerr << get_fileline() << ": sorry: " - << "Asynchronous LOAD not implemented." << endl; - return false; - } + if (! rst_nex->drivers_constant() || + ! tmp_ena.pin(pin).is_linked(scope->tie_hi()) ) { + cerr << get_fileline() << ": sorry: Asynchronous load " + << "is not currently supported in synthesis." << endl; + des->errors += 1; + return false; + } + + if (ff_aclr.pin(pin).is_linked() || + ff_aset.pin(pin).is_linked()) { + cerr << get_fileline() << ": sorry: More than " + "one asynchronous set/reset clause is " + "not currently supported in synthesis." << endl; + des->errors += 1; + return false; } verinum rst_drv = rst_nex->driven_vector(); - ivl_assert(*this, rst_drv.len()==rst_mask.size()); verinum zero (verinum::V0, rst_drv.len()); verinum ones (verinum::V1, rst_drv.len()); @@ -1541,66 +1873,33 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope, ivl_assert(*this, rst->pin_count()==1); connect(ff_aclr.pin(pin), rst->pin(0)); - } else if (rst_drv==ones) { + } else { // Don't yet support multiple asynchronous set inputs. ivl_assert(*this, ! ff_aset.pin(pin).is_linked()); ivl_assert(*this, rst->pin_count()==1); connect(ff_aset.pin(pin), rst->pin(0)); - - } else { - NetConcat *set_cc = new NetConcat(scope, - scope->local_symbol(), - rst_nex->vector_width(), - rst_drv.len(), true); - NetConcat *rst_cc = new NetConcat(scope, - scope->local_symbol(), - rst_nex->vector_width(), - rst_drv.len(), true); - ivl_variable_type_t oosig_data_type = IVL_VT_LOGIC; - netvector_t *oosig_vec = new netvector_t(oosig_data_type, 0, 0); - NetNet *oosig[2] = {new NetNet(scope, - scope->local_symbol(), - NetNet::TRI, oosig_vec), - new NetNet(scope, - scope->local_symbol(), - NetNet::TRI, oosig_vec)}; - set_cc->set_line(*this); - des->add_node(set_cc); - connect(set_cc->pin(0), oosig[0]->pin(0)); - rst_cc->set_line(*this); - des->add_node(rst_cc); - connect(rst_cc->pin(0), oosig[1]->pin(0)); - for (int i = 0; i < (int)rst_drv.len(); i += 1) { - // This is the output signal f const, osig. - ivl_variable_type_t osig_data_type = IVL_VT_LOGIC; - netvector_t*osig_vec = new netvector_t(osig_data_type, 0, 0); - NetNet *osig = new NetNet(scope, scope->local_symbol(), - NetNet::TRI, osig_vec); - NetConst *nc = new NetConst(scope, scope->local_symbol(), - verinum(verinum::V0, 1)); - connect(nc->pin(0), osig->pin(0)); - nc->set_line(*this); - des->add_node(nc); - if (rst_drv[i] == verinum::V1) { - connect(set_cc->pin(i+1), rst->pin(0)); - connect(rst_cc->pin(i+1), nc->pin(0)); - } else { - connect(set_cc->pin(i+1), nc->pin(0)); - connect(rst_cc->pin(i+1), rst->pin(0)); - } - } - connect(ff_aset.pin(pin), set_cc->pin(0)); - connect(ff_aclr.pin(pin), rst_cc->pin(0)); + if (rst_drv!=ones) + ff_aset_value[pin] = rst_drv; } } - return else_->synth_sync(des, scope, ff_clk, ff_ce, - ff_aclr, ff_aset, - nex_map, nex_out, vector(0)); + if (else_ == 0) + return true; + + vector events; + for (unsigned jdx = 0 ; jdx < events_in.size() ; jdx += 1) { + if (jdx != idx) + events.push_back(events_in[jdx]); + } + return else_->synth_sync(des, scope, + ff_negedge, ff_clk, ff_ce, + ff_aclr, ff_aset, ff_aset_value, + nex_map, nex_out, bitmasks, events); } delete expr_input; + #if 0 /* Detect the case that this is a *synchronous* set/reset. It is not asynchronous because we know the condition is not @@ -1637,7 +1936,7 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope, use the Sclr input. Otherwise, use the Aset input and save the set value. */ verinum tmp (verinum::V0, ff->width()); - for (unsigned bit = 0 ; bit < ff->width() ; bit += 1) { + for (unsigned bit = 0 ; bit < ff->width() ; bit += 1) { assert(asig->pin(bit).nexus()->drivers_constant()); tmp.set(bit, asig->pin(bit).nexus()->driven_value()); @@ -1666,6 +1965,10 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope, delete a_set; #endif +#if 0 + /* This gives a false positive for strange coding styles, + such as ivltests/conditsynth3.v. */ + /* Failed to find an asynchronous set/reset, so any events input are probably in error. */ if (events_in.size() > 0) { @@ -1673,93 +1976,18 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope, << " for in process synthesis." << endl; des->errors += 1; } +#endif - - /* If this is an if/then/else, then it is likely a - combinational if, and I should synthesize it that way. */ - if (if_ && else_) { - NetBus tmp (scope, nex_out.pin_count()); - bool flag = synth_async(des, scope, nex_map, nex_out, tmp); - return flag; - } - - ivl_assert(*this, if_); - ivl_assert(*this, !else_); - - /* Synthesize the enable expression. */ - NetNet*ce = expr_->synthesize(des, scope, expr_); - ivl_assert(*this, ce && ce->pin_count()==1 && ce->vector_width()==1); - - if (debug_synth2) { - NexusSet if_set; - if_->nex_output(if_set); - - cerr << get_fileline() << ": NetCondit::synth_sync: " - << "Found ce pattern." - << " ff_ce.pin_count()=" << ff_ce.pin_count() - << endl; - for (unsigned idx = 0 ; idx < nex_map.size() ; idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_sync: " - << "nex_map[" << idx << "]: " - << "base=" << nex_map[idx].base - << ", wid=" << nex_map[idx].wid - << endl; - nex_map[idx].lnk.dump_link(cerr, 8); - } - for (unsigned idx = 0 ; idx < if_set.size() ; idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_sync: " - << "if_set[" << idx << "]: " - << "base=" << if_set[idx].base - << ", wid=" << if_set[idx].wid - << endl; - if_set[idx].lnk.dump_link(cerr, 8); - } - } - - /* What's left, is a synchronous CE statement like this: - - if (expr_) ; - - The expr_ expression has already been synthesized to the ce - net, so we connect it here to the FF. What's left is to - synthesize the substatement as a combinational - statement. - - Watch out for the special case that there is already a CE - connected to this FF. This can be caused by code like this: - - if (a) if (b) ; - - In this case, we are working on the inner IF, so we AND the - a and b expressions to make a new CE. */ - - for (unsigned idx = 0 ; idx < ff_ce.pin_count() ; idx += 1) { - if (ff_ce.pin(idx).is_linked()) { - NetLogic*ce_and = new NetLogic(scope, - scope->local_symbol(), 3, - NetLogic::AND, 1); - des->add_node(ce_and); - connect(ff_ce.pin(idx), ce_and->pin(1)); - connect(ce->pin(0), ce_and->pin(2)); - - ff_ce.pin(idx).unlink(); - connect(ff_ce.pin(idx), ce_and->pin(0)); - - } else { - - connect(ff_ce.pin(idx), ce->pin(0)); - } - } - - bool flag = if_->synth_sync(des, scope, ff_clk, ff_ce, ff_aclr, ff_aset, nex_map, nex_out, events_in); - - return flag; + return synth_async(des, scope, nex_map, nex_out, ff_ce, bitmasks); } bool NetEvWait::synth_sync(Design*des, NetScope*scope, + bool&ff_negedge, NetNet*ff_clk, NetBus&ff_ce, NetBus&ff_aclr,NetBus&ff_aset, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const vector&events_in) { if (debug_synth2) { @@ -1802,7 +2030,7 @@ bool NetEvWait::synth_sync(Design*des, NetScope*scope, if (pclk != 0) { cerr << get_fileline() << ": error: Too many " << "clocks for synchronous logic." << endl; - cerr << get_fileline() << ": : Perhaps an" + cerr << get_fileline() << ": : Perhaps an" << " asynchronous set/reset is misused?" << endl; des->errors += 1; } @@ -1818,6 +2046,7 @@ bool NetEvWait::synth_sync(Design*des, NetScope*scope, << " are valid clock inputs." << endl; cerr << get_fileline() << ": : Perhaps the clock" << " is read by a statement or expression?" << endl; + des->errors += 1; return false; } @@ -1828,8 +2057,7 @@ bool NetEvWait::synth_sync(Design*des, NetScope*scope, connect(ff_clk->pin(0), pclk->pin(0)); if (pclk->edge() == NetEvProbe::NEGEDGE) { - perm_string polarity = perm_string::literal("Clock:LPM_Polarity"); - ff_clk->attribute(polarity, verinum("INVERT")); + ff_negedge = true; if (debug_synth2) { cerr << get_fileline() << ": debug: " @@ -1839,11 +2067,10 @@ bool NetEvWait::synth_sync(Design*des, NetScope*scope, } /* Synthesize the input to the DFF. */ - bool flag = statement_->synth_sync(des, scope, ff_clk, ff_ce, - ff_aclr, ff_aset, - nex_map, nex_out, events); - - return flag; + return statement_->synth_sync(des, scope, + ff_negedge, ff_clk, ff_ce, + ff_aclr, ff_aset, ff_aset_value, + nex_map, nex_out, bitmasks, events); } /* @@ -1861,6 +2088,7 @@ bool NetProcTop::synth_sync(Design*des) NexusSet nex_set; statement_->nex_output(nex_set); + vector aset_value(nex_set.size()); /* Make a model FF that will connect to the first item in the set, and will also take the initial connection of clocks @@ -1872,37 +2100,42 @@ bool NetProcTop::synth_sync(Design*des) clock->local_flag(true); clock->set_line(*this); -#if 0 - NetNet*ce = new NetNet(scope(), scope()->local_symbol(), - NetNet::TRI, &netvector_t::scalar_logic); - ce->local_flag(true); -#else - NetBus ce (scope(), nex_set.size()); -#endif + NetBus ce (scope(), nex_set.size()); NetBus nex_d (scope(), nex_set.size()); NetBus nex_q (scope(), nex_set.size()); - NetBus aclr (scope(), nex_set.size()); - NetBus aset (scope(), nex_set.size()); + NetBus aclr (scope(), nex_set.size()); + NetBus aset (scope(), nex_set.size()); + vector bitmasks (nex_set.size()); - /* The Q of the NetFF devices is connected to the output that - we are. The nex_q is a bundle of the outputs. We will also - pass the nex_q as a map to the statement's synth_sync - method to map it to the correct nex_d pin. */ - for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) { - connect(nex_set[idx].lnk, nex_q.pin(idx)); - } + // Save links to the initial nex_d. These will be used later + // to detect floating part-substitute and mux inputs that need + // to be tied off. + NetBus nex_in (scope(), nex_d.pin_count()); + for (unsigned idx = 0 ; idx < nex_in.pin_count() ; idx += 1) + connect(nex_in.pin(idx), nex_d.pin(idx)); - // Connect the input later. + // The Q of the NetFF devices is connected to the output that + // we are. The nex_q is a bundle of the outputs. + for (unsigned idx = 0 ; idx < nex_q.pin_count() ; idx += 1) + connect(nex_q.pin(idx), nex_set[idx].lnk); + + // Connect the D of the NetFF devices later. /* Synthesize the input to the DFF. */ - bool flag = statement_->synth_sync(des, scope(), clock, ce, aclr, aset, - nex_set, nex_d, + bool negedge = false; + bool flag = statement_->synth_sync(des, scope(), + negedge, clock, ce, + aclr, aset, aset_value, + nex_set, nex_d, bitmasks, vector()); if (! flag) { delete clock; return false; } + flag = tie_off_floating_inputs_(des, nex_set, nex_in, bitmasks, true); + if (!flag) return false; + for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) { //ivl_assert(*this, nex_set[idx].nex); @@ -1914,9 +2147,10 @@ bool NetProcTop::synth_sync(Design*des) } NetFF*ff2 = new NetFF(scope(), scope()->local_symbol(), - nex_set[idx].wid); + negedge, nex_set[idx].wid); des->add_node(ff2); ff2->set_line(*this); + ff2->aset_value(aset_value[idx]); NetNet*tmp = nex_d.pin(idx).nexus()->pick_any_net(); tmp->set_line(*this); @@ -1929,7 +2163,7 @@ bool NetProcTop::synth_sync(Design*des) connect(clock->pin(0), ff2->pin_Clock()); if (ce.pin(idx).is_linked()) - connect(ce.pin(idx), ff2->pin_Enable()); + connect(ce.pin(idx), ff2->pin_Enable()); if (aclr.pin(idx).is_linked()) connect(aclr.pin(idx), ff2->pin_Aclr()); if (aset.pin(idx).is_linked()) @@ -1942,17 +2176,16 @@ bool NetProcTop::synth_sync(Design*des) #endif } - // The "clock" and "ce" nets were just to carry the connection - // back to the flip-flop. Delete them now. The connections - // will persist. + // The "clock" net was just to carry the connection back + // to the flip-flop. Delete it now. The connection will + // persist. delete clock; -#if 0 - delete ce; -#endif + + synthesized_design_ = des; return true; } -class synth2_f : public functor_t { +class synth2_f : public functor_t { public: void process(Design*, NetProcTop*); @@ -1975,11 +2208,17 @@ void synth2_f::process(Design*des, NetProcTop*top) if (top->scope()->attribute(perm_string::literal("ivl_synthesis_cell")).len() > 0) return; + /* Create shared pullup and pulldown nodes (if they don't already + exist) for use when creating clock/gate enables. */ + top->scope()->add_tie_hi(des); + top->scope()->add_tie_lo(des); + if (top->is_synchronous()) { bool flag = top->synth_sync(des); if (! flag) { cerr << top->get_fileline() << ": error: " - << "Unable to synthesize synchronous process." << endl; + << "Unable to synthesize synchronous process." + << endl; des->errors += 1; return; } @@ -2014,8 +2253,8 @@ void synth2_f::process(Design*des, NetProcTop*top) if (! top->synth_async(des)) { cerr << top->get_fileline() << ": error: " - << "failed to synthesize asynchronous " - << "logic for this process." << endl; + << "Unable to synthesize asynchronous process." + << endl; des->errors += 1; return; } diff --git a/sys_funcs.cc b/sys_funcs.cc index 3e2b26a51..bb1ccd8bc 100644 --- a/sys_funcs.cc +++ b/sys_funcs.cc @@ -204,6 +204,17 @@ int load_sys_func_table(const char*path) continue; } + if (strcmp(stype,"vpiSysFuncString") == 0) { + cell = new struct sfunc_return_type_cell; + cell->name = lex_strings.add(name); + cell->type = IVL_VT_STRING; + cell->wid = 0; // string is a dynamic length type + cell->signed_flag = false; + cell->next = sfunc_stack; + sfunc_stack = cell; + continue; + } + fprintf(stderr, "%s:%s: Unknown type: %s\n", path, name, stype); } diff --git a/t-dll-api.cc b/t-dll-api.cc index 5a77f26eb..89b0a5aa2 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -1,6 +1,7 @@ /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2018 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) + * Copyright (c) 2016 CERN Michele Castellana (michele.castellana@cern.ch) * * 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,22 +38,28 @@ static StringHeap api_strings; extern "C" ivl_island_t ivl_branch_island(ivl_branch_t net) { + assert(net); return net->island; } extern "C" ivl_nexus_t ivl_branch_terminal(ivl_branch_t net, int idx) { + assert(net); assert(idx >= 0); - assert( idx < 2); + assert(idx < 2); return net->pins[idx]; } extern "C" const char*ivl_design_delay_sel(ivl_design_t des) { + assert(des); + assert(des->self); return des->self->get_delay_sel(); } extern "C" const char*ivl_design_flag(ivl_design_t des, const char*key) { + assert(des); + assert(des->self); return des->self->get_flag(key); } @@ -60,6 +67,7 @@ extern "C" int ivl_design_process(ivl_design_t des, ivl_process_f func, void*cd) { + assert(des); for (ivl_process_t idx = des->threads_; idx; idx = idx->next_) { int rc = (func)(idx, cd); if (rc != 0) @@ -72,8 +80,8 @@ extern "C" int ivl_design_process(ivl_design_t des, extern "C" ivl_scope_t ivl_design_root(ivl_design_t des) { cerr << "ANACHRONISM: ivl_design_root called. " - "Use ivl_design_roots instead." << endl; - + "Use ivl_design_roots instead." << endl; + assert(des); assert (des->roots.size() > 0); return des->roots[0]; } @@ -81,13 +89,11 @@ extern "C" ivl_scope_t ivl_design_root(ivl_design_t des) extern "C" void ivl_design_roots(ivl_design_t des, ivl_scope_t **scopes, unsigned int *nscopes) { + assert(des); assert (nscopes && scopes); if (des->root_scope_list.size() == 0) { size_t fill = 0; - des->root_scope_list.resize(des->root_tasks.size() + des->packages.size() + des->roots.size() + des->classes.size()); - for (map::iterator idx = des->root_tasks.begin() - ; idx != des->root_tasks.end() ; ++ idx) - des->root_scope_list[fill++] = idx->second; + des->root_scope_list.resize(des->packages.size() + des->roots.size() + des->classes.size()); for (map::iterator idx = des->classes.begin() ; idx != des->classes.end() ; ++ idx) @@ -105,16 +111,19 @@ extern "C" void ivl_design_roots(ivl_design_t des, ivl_scope_t **scopes, extern "C" int ivl_design_time_precision(ivl_design_t des) { + assert(des); return des->time_precision; } extern "C" unsigned ivl_design_consts(ivl_design_t des) { + assert(des); return des->consts.size(); } extern "C" ivl_net_const_t ivl_design_const(ivl_design_t des, unsigned idx) { + assert(des); assert(idx < des->consts.size()); return des->consts[idx]; } @@ -134,21 +143,25 @@ extern "C" ivl_discipline_t ivl_design_discipline(ivl_design_t des, unsigned idx extern "C" ivl_dis_domain_t ivl_discipline_domain(ivl_discipline_t net) { + assert(net); return net->domain(); } extern "C" ivl_nature_t ivl_discipline_flow(ivl_discipline_t net) { + assert(net); return net->flow(); } extern "C" const char* ivl_discipline_name(ivl_discipline_t net) { + assert(net); return net->name(); } extern "C" ivl_nature_t ivl_discipline_potential(ivl_discipline_t net) { + assert(net); return net->potential(); } @@ -171,13 +184,6 @@ extern "C" unsigned ivl_expr_lineno(ivl_expr_t net) return net->lineno; } -inline static const char *basename(ivl_scope_t scope, const char *inst) -{ - inst += strlen(ivl_scope_name(scope)); - assert(*inst == '.'); - return inst+1; -} - extern "C" ivl_variable_type_t ivl_const_type(ivl_net_const_t net) { assert(net); @@ -204,6 +210,7 @@ extern "C" const char*ivl_const_bits(ivl_net_const_t net) extern "C" ivl_expr_t ivl_const_delay(ivl_net_const_t net, unsigned transition) { + assert(net); assert(transition < 3); return net->delay[transition]; } @@ -303,6 +310,7 @@ extern "C" unsigned ivl_enum_lineno(ivl_enumtype_t net) extern "C" const char* ivl_event_name(ivl_event_t net) { + assert(net); static char*name_buffer = 0; static unsigned name_size = 0; @@ -327,6 +335,7 @@ extern "C" const char* ivl_event_name(ivl_event_t net) extern "C" const char* ivl_event_basename(ivl_event_t net) { + assert(net); return net->name; } @@ -344,6 +353,7 @@ extern "C" unsigned ivl_event_lineno(ivl_event_t net) extern "C" ivl_scope_t ivl_event_scope(ivl_event_t net) { + assert(net); return net->scope; } @@ -388,13 +398,15 @@ extern "C" ivl_nexus_t ivl_event_pos(ivl_event_t net, unsigned idx) extern "C" const char* ivl_expr_bits(ivl_expr_t net) { - assert(net && (net->type_ == IVL_EX_NUMBER)); + assert(net); + assert(net->type_ == IVL_EX_NUMBER); return net->u_.number_.bits_; } extern "C" ivl_branch_t ivl_expr_branch(ivl_expr_t net) { - assert(net && (net->type_ == IVL_EX_BACCESS)); + assert(net); + assert(net->type_ == IVL_EX_BACCESS); return net->u_.branch_.branch; } @@ -416,29 +428,34 @@ extern "C" ivl_scope_t ivl_expr_def(ivl_expr_t net) extern "C" uint64_t ivl_expr_delay_val(ivl_expr_t net) { + assert(net); assert(net->type_ == IVL_EX_DELAY); return net->u_.delay_.value; } extern "C" double ivl_expr_dvalue(ivl_expr_t net) { + assert(net); assert(net->type_ == IVL_EX_REALNUM); return net->u_.real_.value; } extern "C" ivl_enumtype_t ivl_expr_enumtype(ivl_expr_t net) { + assert(net); assert(net->type_ == IVL_EX_ENUMTYPE); return net->u_.enumtype_.type; } extern "C" ivl_type_t ivl_expr_net_type(ivl_expr_t net) { + assert(net); return net->net_type; } extern "C" const char* ivl_expr_name(ivl_expr_t net) { + assert(net); switch (net->type_) { case IVL_EX_SFUNC: @@ -462,7 +479,8 @@ extern "C" const char* ivl_expr_name(ivl_expr_t net) extern "C" ivl_nature_t ivl_expr_nature(ivl_expr_t net) { - assert(net && (net->type_ == IVL_EX_BACCESS)); + assert(net); + assert(net->type_ == IVL_EX_BACCESS); return net->u_.branch_.nature; } @@ -692,12 +710,14 @@ extern "C" int ivl_expr_sized(ivl_expr_t net) extern "C" const char* ivl_expr_string(ivl_expr_t net) { + assert(net); assert(net->type_ == IVL_EX_STRING); return net->u_.string_.value_; } extern "C" unsigned long ivl_expr_uvalue(ivl_expr_t net) { + assert(net); switch (net->type_) { case IVL_EX_ULONG: @@ -729,7 +749,7 @@ extern "C" ivl_variable_type_t ivl_expr_value(ivl_expr_t net) extern "C" unsigned ivl_expr_width(ivl_expr_t net) { -// assert(net); + assert(net); return net->width_; } @@ -798,6 +818,7 @@ extern "C" unsigned ivl_file_table_size() extern "C" int ivl_island_flag_set(ivl_island_t net, unsigned flag, int value) { + assert(net); if (flag >= net->flags.size()) { if (value == 0) return 0; @@ -812,6 +833,7 @@ extern "C" int ivl_island_flag_set(ivl_island_t net, unsigned flag, int value) extern "C" int ivl_island_flag_test(ivl_island_t net, unsigned flag) { + assert(net); if (flag >= net->flags.size()) return 0; else @@ -854,12 +876,14 @@ extern "C" const char* ivl_logic_attr(ivl_net_logic_t net, const char*key) extern "C" unsigned ivl_logic_attr_cnt(ivl_net_logic_t net) { + assert(net); return net->nattr; } extern "C" ivl_attribute_t ivl_logic_attr_val(ivl_net_logic_t net, unsigned idx) { + assert(net); assert(idx < net->nattr); return net->attr + idx; } @@ -919,22 +943,26 @@ extern "C" ivl_scope_t ivl_logic_scope(ivl_net_logic_t net) extern "C" ivl_logic_t ivl_logic_type(ivl_net_logic_t net) { + assert(net); return net->type_; } extern "C" unsigned ivl_logic_pins(ivl_net_logic_t net) { + assert(net); return net->npins_; } extern "C" ivl_nexus_t ivl_logic_pin(ivl_net_logic_t net, unsigned pin) { + assert(net); assert(pin < net->npins_); return net->pins_[pin]; } extern "C" ivl_udp_t ivl_logic_udp(ivl_net_logic_t net) { + assert(net); assert(net->type_ == IVL_LO_UDP); assert(net->udp); return net->udp; @@ -942,6 +970,7 @@ extern "C" ivl_udp_t ivl_logic_udp(ivl_net_logic_t net) extern "C" ivl_expr_t ivl_logic_delay(ivl_net_logic_t net, unsigned transition) { + assert(net); assert(transition < 3); return net->delay[transition]; } @@ -954,21 +983,25 @@ extern "C" unsigned ivl_logic_width(ivl_net_logic_t net) extern "C" int ivl_udp_sequ(ivl_udp_t net) { + assert(net); return net->sequ; } extern "C" unsigned ivl_udp_nin(ivl_udp_t net) { + assert(net); return net->nin; } extern "C" char ivl_udp_init(ivl_udp_t net) { + assert(net); return net->init; } extern "C" const char* ivl_udp_port(ivl_udp_t net, unsigned idx) { + assert(net); assert(idx <= net->nin); assert(net->ports); assert(net->ports[idx].c_str()); @@ -977,6 +1010,7 @@ extern "C" const char* ivl_udp_port(ivl_udp_t net, unsigned idx) extern "C" const char* ivl_udp_row(ivl_udp_t net, unsigned idx) { + assert(net); assert(idx < net->nrows); assert(net->table); assert(net->table[idx]); @@ -985,27 +1019,32 @@ extern "C" const char* ivl_udp_row(ivl_udp_t net, unsigned idx) extern "C" unsigned ivl_udp_rows(ivl_udp_t net) { + assert(net); return net->nrows; } extern "C" const char* ivl_udp_name(ivl_udp_t net) { + assert(net); assert(net->name); return net->name; } extern "C" const char* ivl_udp_file(ivl_udp_t net) { + assert(net); return net->file.str(); } extern "C" unsigned ivl_udp_lineno(ivl_udp_t net) { + assert(net); return net->lineno; } extern "C" const char* ivl_lpm_basename(ivl_lpm_t net) { + assert(net); return net->name; } @@ -1035,6 +1074,7 @@ extern "C" ivl_nexus_t ivl_lpm_sync_clr(ivl_lpm_t net) extern "C" ivl_expr_t ivl_lpm_delay(ivl_lpm_t net, unsigned transition) { + assert(net); assert(transition < 3); return net->delay[transition]; } @@ -1090,6 +1130,18 @@ extern "C" unsigned ivl_lpm_base(ivl_lpm_t net) } } +extern "C" unsigned ivl_lpm_negedge(ivl_lpm_t net) +{ + assert(net); + switch (net->type) { + case IVL_LPM_FF: + return net->u_.ff.negedge_flag; + default: + assert(0); + return 0; + } +} + extern "C" ivl_nexus_t ivl_lpm_clk(ivl_lpm_t net) { assert(net); @@ -1143,6 +1195,8 @@ extern "C" ivl_nexus_t ivl_lpm_enable(ivl_lpm_t net) switch (net->type) { case IVL_LPM_FF: return net->u_.ff.we; + case IVL_LPM_LATCH: + return net->u_.latch.e; default: assert(0); return 0; @@ -1151,11 +1205,13 @@ extern "C" ivl_nexus_t ivl_lpm_enable(ivl_lpm_t net) extern "C" const char* ivl_lpm_file(ivl_lpm_t net) { + assert(net); return net->file.str(); } extern "C" unsigned ivl_lpm_lineno(ivl_lpm_t net) { + assert(net); return net->lineno; } @@ -1179,6 +1235,8 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx) case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_MULT: @@ -1215,6 +1273,9 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx) case IVL_LPM_FF: assert(idx == 0); return net->u_.ff.d.pin; + case IVL_LPM_LATCH: + assert(idx == 0); + return net->u_.latch.d.pin; case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: @@ -1292,6 +1353,7 @@ extern "C" ivl_nexus_t ivl_lpm_datab(ivl_lpm_t net, unsigned idx) */ extern "C" const char* ivl_lpm_name(ivl_lpm_t net) { + assert(net); static char*name_buffer = 0; static unsigned name_size = 0; @@ -1330,6 +1392,8 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net) case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_NEE: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_MULT: @@ -1339,6 +1403,8 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net) case IVL_LPM_FF: return net->u_.ff.q.pin; + case IVL_LPM_LATCH: + return net->u_.latch.q.pin; case IVL_LPM_MUX: return net->u_.mux.q; @@ -1427,6 +1493,7 @@ extern "C" ivl_scope_t ivl_lpm_scope(ivl_lpm_t net) extern "C" ivl_nexus_t ivl_lpm_select(ivl_lpm_t net) { + assert(net); switch (net->type) { case IVL_LPM_MUX: @@ -1443,6 +1510,7 @@ extern "C" ivl_nexus_t ivl_lpm_select(ivl_lpm_t net) extern "C" unsigned ivl_lpm_selects(ivl_lpm_t net) { + assert(net); switch (net->type) { case IVL_LPM_MUX: return net->u_.mux.swid; @@ -1452,6 +1520,7 @@ extern "C" unsigned ivl_lpm_selects(ivl_lpm_t net) case IVL_LPM_CONCATZ: cerr << "error: ivl_lpm_selects() is no longer supported for " "IVL_LPM_CONCAT, use ivl_lpm_size() instead." << endl; + // fallthrough default: assert(0); return 0; @@ -1476,6 +1545,8 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net) case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_MULT: @@ -1519,6 +1590,7 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net) extern "C" unsigned ivl_lpm_size(ivl_lpm_t net) { + assert(net); switch (net->type) { case IVL_LPM_MUX: return net->u_.mux.size; @@ -1571,12 +1643,14 @@ extern "C" unsigned ivl_lpm_size(ivl_lpm_t net) extern "C" const char* ivl_lpm_string(ivl_lpm_t net) { + assert(net); assert(net->type == IVL_LPM_SFUNC); return net->u_.sfunc.fun_name; } extern "C" ivl_lpm_type_t ivl_lpm_type(ivl_lpm_t net) { + assert(net); return net->type; } @@ -1664,6 +1738,7 @@ extern "C" ivl_lval_t ivl_lval_nest(ivl_lval_t net) extern "C" const char* ivl_nature_name(ivl_nature_t net) { + assert(net); return net->name(); } @@ -1873,16 +1948,19 @@ extern ivl_scope_t ivl_path_scope(ivl_delaypath_t obj) extern ivl_nexus_t ivl_path_source(ivl_delaypath_t net) { + assert(net); return net->src; } extern int ivl_path_source_posedge(ivl_delaypath_t net) { + assert(net); return net->posedge ? 1 : 0; } extern int ivl_path_source_negedge(ivl_delaypath_t net) { + assert(net); return net->negedge ? 1 : 0; } @@ -1900,32 +1978,38 @@ extern "C" unsigned ivl_process_lineno(ivl_process_t net) extern "C" ivl_process_type_t ivl_process_type(ivl_process_t net) { + assert(net); return net->type_; } extern "C" int ivl_process_analog(ivl_process_t net) { + assert(net); return net->analog_flag != 0; } extern "C" ivl_scope_t ivl_process_scope(ivl_process_t net) { + assert(net); return net->scope_; } extern "C" ivl_statement_t ivl_process_stmt(ivl_process_t net) { + assert(net); return net->stmt_; } extern "C" unsigned ivl_process_attr_cnt(ivl_process_t net) { + assert(net); return net->nattr; } extern "C" ivl_attribute_t ivl_process_attr_val(ivl_process_t net, unsigned idx) { + assert(net); assert(idx < net->nattr); return net->attr + idx; } @@ -1939,6 +2023,7 @@ extern "C" unsigned ivl_scope_attr_cnt(ivl_scope_t net) extern "C" ivl_attribute_t ivl_scope_attr_val(ivl_scope_t net, unsigned idx) { + assert(net); assert(idx < net->nattr); return net->attr + idx; } @@ -1946,7 +2031,6 @@ extern "C" ivl_attribute_t ivl_scope_attr_val(ivl_scope_t net, extern "C" const char* ivl_scope_basename(ivl_scope_t net) { assert(net); - return net->name_; } @@ -1966,24 +2050,28 @@ extern "C" int ivl_scope_children(ivl_scope_t net, extern "C" size_t ivl_scope_childs(ivl_scope_t net) { + assert(net); assert(net->child.size() == net->children.size()); return net->child.size(); } extern "C" ivl_scope_t ivl_scope_child(ivl_scope_t net, size_t idx) { - assert(net && idx < net->child.size()); + assert(net); + assert(idx < net->child.size()); return net->child[idx]; } extern "C" ivl_type_t ivl_scope_class(ivl_scope_t net, unsigned idx) { + assert(net); assert(idx < net->classes.size()); return net->classes[idx]; } extern "C" unsigned ivl_scope_classes(ivl_scope_t net) { + assert(net); return net->classes.size(); } @@ -2038,6 +2126,29 @@ extern "C" const char*ivl_scope_file(ivl_scope_t net) return net->file.str(); } +extern "C" ivl_variable_type_t ivl_scope_func_type(ivl_scope_t net) +{ + assert(net); + assert(net->type_ == IVL_SCT_FUNCTION); + return net->func_type; +} + +extern "C" int ivl_scope_func_signed(ivl_scope_t net) +{ + assert(net); + assert(net->type_==IVL_SCT_FUNCTION); + assert(net->func_type==IVL_VT_LOGIC || net->func_type==IVL_VT_BOOL); + return net->func_signed? 1 : 0; +} + +extern "C" unsigned ivl_scope_func_width(ivl_scope_t net) +{ + assert(net); + assert(net->type_ == IVL_SCT_FUNCTION); + assert(net->func_type==IVL_VT_LOGIC || net->func_type==IVL_VT_BOOL); + return net->func_width; +} + extern "C" unsigned ivl_scope_is_auto(ivl_scope_t net) { assert(net); @@ -2094,6 +2205,7 @@ static unsigned scope_name_len(ivl_scope_t net) static void push_scope_basename(ivl_scope_t net, char*buf) { + assert(net); if (net->parent == 0) { strcpy(buf, net->name_); return; @@ -2106,6 +2218,7 @@ static void push_scope_basename(ivl_scope_t net, char*buf) extern "C" const char* ivl_scope_name(ivl_scope_t net) { + assert(net); static char*name_buffer = 0; static unsigned name_size = 0; @@ -2148,21 +2261,22 @@ extern "C" ivl_scope_t ivl_scope_parent(ivl_scope_t net) extern "C" unsigned ivl_scope_mod_module_ports(ivl_scope_t net) { assert(net); - assert (net->type_ == IVL_SCT_MODULE ); + assert(net->type_ == IVL_SCT_MODULE ); return static_cast(net->module_ports_info.size()); } extern "C" const char *ivl_scope_mod_module_port_name(ivl_scope_t net, unsigned idx ) { assert(net); - assert (net->type_ == IVL_SCT_MODULE ); - assert( idx < net->module_ports_info.size()); + assert(net->type_ == IVL_SCT_MODULE ); + assert(idx < net->module_ports_info.size()); return net->module_ports_info[idx].name; } extern "C" ivl_signal_port_t ivl_scope_mod_module_port_type(ivl_scope_t net, unsigned idx ) { + assert(net); switch( net->module_ports_info[idx].type ) { case PortType::PINPUT : return IVL_SIP_INPUT; @@ -2174,6 +2288,7 @@ extern "C" ivl_signal_port_t ivl_scope_mod_module_port_type(ivl_scope_t net, uns extern "C" unsigned ivl_scope_mod_module_port_width(ivl_scope_t net, unsigned idx ) { + assert(net); return net->module_ports_info[idx].width; } @@ -2256,31 +2371,37 @@ extern "C" const char* ivl_scope_tname(ivl_scope_t net) extern "C" int ivl_signal_array_base(ivl_signal_t net) { + assert(net); return net->array_base; } extern "C" unsigned ivl_signal_array_count(ivl_signal_t net) { + assert(net); return net->array_words; } extern "C" unsigned ivl_signal_array_addr_swapped(ivl_signal_t net) { + assert(net); return net->array_addr_swapped; } extern "C" unsigned ivl_signal_dimensions(ivl_signal_t net) { + assert(net); return net->array_dimensions_; } extern "C" ivl_discipline_t ivl_signal_discipline(ivl_signal_t net) { + assert(net); return net->discipline; } extern "C" const char* ivl_signal_attr(ivl_signal_t net, const char*key) { + assert(net); if (net->nattr == 0) return 0; @@ -2296,22 +2417,26 @@ extern "C" const char* ivl_signal_attr(ivl_signal_t net, const char*key) extern "C" unsigned ivl_signal_attr_cnt(ivl_signal_t net) { + assert(net); return net->nattr; } extern "C" ivl_attribute_t ivl_signal_attr_val(ivl_signal_t net, unsigned idx) { + assert(net); assert(idx < net->nattr); return net->attr + idx; } extern "C" const char* ivl_signal_basename(ivl_signal_t net) { + assert(net); return net->name_; } extern "C" const char* ivl_signal_name(ivl_signal_t net) { + assert(net); static char*name_buffer = 0; static unsigned name_size = 0; @@ -2332,6 +2457,7 @@ extern "C" const char* ivl_signal_name(ivl_signal_t net) extern "C" ivl_nexus_t ivl_signal_nex(ivl_signal_t net, unsigned word) { + assert(net); assert(word < net->array_words); if (net->array_words > 1) { if (net->pins) { @@ -2348,23 +2474,27 @@ extern "C" ivl_nexus_t ivl_signal_nex(ivl_signal_t net, unsigned word) extern "C" unsigned ivl_signal_packed_dimensions(ivl_signal_t net) { + assert(net); return net->packed_dims.size(); } extern "C" int ivl_signal_packed_msb(ivl_signal_t net, unsigned dim) { + assert(net); assert(dim < net->packed_dims.size()); return net->packed_dims[dim].get_msb(); } extern "C" int ivl_signal_packed_lsb(ivl_signal_t net, unsigned dim) { + assert(net); assert(dim < net->packed_dims.size()); return net->packed_dims[dim].get_lsb(); } extern "C" int ivl_signal_msb(ivl_signal_t net) { + assert(net); if (net->packed_dims.empty()) return 0; @@ -2374,6 +2504,7 @@ extern "C" int ivl_signal_msb(ivl_signal_t net) extern "C" int ivl_signal_lsb(ivl_signal_t net) { + assert(net); if (net->packed_dims.empty()) return 0; @@ -2389,31 +2520,39 @@ extern "C" ivl_scope_t ivl_signal_scope(ivl_signal_t net) extern "C" unsigned ivl_signal_width(ivl_signal_t net) { + assert(net); + assert(net->net_type); return net->net_type->packed_width(); } extern "C" ivl_signal_port_t ivl_signal_port(ivl_signal_t net) { + assert(net); return net->port_; } extern "C" int ivl_signal_module_port_index(ivl_signal_t net) { + assert(net); return net->module_port_index_; } extern "C" int ivl_signal_local(ivl_signal_t net) { + assert(net); return net->local_; } extern "C" int ivl_signal_signed(ivl_signal_t net) { + assert(net); + assert(net->net_type); return net->net_type->get_signed()? 1 : 0; } extern "C" unsigned ivl_signal_forced_net(ivl_signal_t net) { + assert(net); return net->forced_net_; } @@ -2431,6 +2570,7 @@ extern "C" unsigned ivl_signal_lineno(ivl_signal_t net) extern "C" int ivl_signal_integer(ivl_signal_t net) { + assert(net); if (const netvector_t*vec = dynamic_cast (net->net_type)) return vec->get_isint()? 1 : 0; else if (const netenum_t*enm = dynamic_cast (net->net_type)) @@ -2441,47 +2581,57 @@ extern "C" int ivl_signal_integer(ivl_signal_t net) extern "C" ivl_variable_type_t ivl_signal_data_type(ivl_signal_t net) { + assert(net); + assert(net->net_type); return net->net_type->base_type(); } extern "C" ivl_type_t ivl_signal_net_type(ivl_signal_t net) { + assert(net); return net->net_type; } extern "C" unsigned ivl_signal_npath(ivl_signal_t net) { + assert(net); return net->npath; } extern "C" ivl_delaypath_t ivl_signal_path(ivl_signal_t net, unsigned idx) { + assert(net); assert(idx < net->npath); return net->path + idx; } extern "C" ivl_signal_type_t ivl_signal_type(ivl_signal_t net) { + assert(net); return net->type_; } extern "C" ivl_statement_type_t ivl_statement_type(ivl_statement_t net) { + assert(net); return net->type_; } extern "C" const char* ivl_stmt_file(ivl_statement_t net) { + assert(net); return net->file.str(); } extern "C" unsigned ivl_stmt_lineno(ivl_statement_t net) { + assert(net); return net->lineno; } extern "C" ivl_scope_t ivl_stmt_block_scope(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_BLOCK: case IVL_ST_FORK: @@ -2496,6 +2646,7 @@ extern "C" ivl_scope_t ivl_stmt_block_scope(ivl_statement_t net) extern "C" unsigned ivl_stmt_block_count(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_BLOCK: case IVL_ST_FORK: @@ -2511,6 +2662,7 @@ extern "C" unsigned ivl_stmt_block_count(ivl_statement_t net) extern "C" ivl_statement_t ivl_stmt_block_stmt(ivl_statement_t net, unsigned i) { + assert(net); switch (net->type_) { case IVL_ST_BLOCK: case IVL_ST_FORK: @@ -2525,6 +2677,7 @@ extern "C" ivl_statement_t ivl_stmt_block_stmt(ivl_statement_t net, extern "C" ivl_scope_t ivl_stmt_call(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ALLOC: return net->u_.alloc_.scope; @@ -2545,6 +2698,7 @@ extern "C" ivl_scope_t ivl_stmt_call(ivl_statement_t net) extern "C" unsigned ivl_stmt_case_count(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_CASE: case IVL_ST_CASER: @@ -2559,6 +2713,7 @@ extern "C" unsigned ivl_stmt_case_count(ivl_statement_t net) extern "C" ivl_expr_t ivl_stmt_case_expr(ivl_statement_t net, unsigned idx) { + assert(net); switch (net->type_) { case IVL_ST_CASE: case IVL_ST_CASER: @@ -2575,6 +2730,7 @@ extern "C" ivl_expr_t ivl_stmt_case_expr(ivl_statement_t net, unsigned idx) extern "C" ivl_statement_t ivl_stmt_case_stmt(ivl_statement_t net, unsigned idx) { + assert(net); switch (net->type_) { case IVL_ST_CASE: case IVL_ST_CASER: @@ -2591,6 +2747,7 @@ extern "C" ivl_statement_t ivl_stmt_case_stmt(ivl_statement_t net, unsigned idx) extern "C" ivl_expr_t ivl_stmt_cond_expr(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN_NB: return net->u_.assign_.count; @@ -2617,6 +2774,7 @@ extern "C" ivl_expr_t ivl_stmt_cond_expr(ivl_statement_t net) extern "C" ivl_statement_t ivl_stmt_cond_false(ivl_statement_t net) { + assert(net); assert(net->type_ == IVL_ST_CONDIT); if (net->u_.condit_.stmt_[1].type_ == IVL_ST_NONE) return 0; @@ -2626,6 +2784,7 @@ extern "C" ivl_statement_t ivl_stmt_cond_false(ivl_statement_t net) extern "C" ivl_statement_t ivl_stmt_cond_true(ivl_statement_t net) { + assert(net); assert(net->type_ == IVL_ST_CONDIT); if (net->u_.condit_.stmt_[0].type_ == IVL_ST_NONE) return 0; @@ -2635,6 +2794,7 @@ extern "C" ivl_statement_t ivl_stmt_cond_true(ivl_statement_t net) extern "C" ivl_expr_t ivl_stmt_delay_expr(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN: case IVL_ST_ASSIGN_NB: @@ -2651,12 +2811,24 @@ extern "C" ivl_expr_t ivl_stmt_delay_expr(ivl_statement_t net) extern "C" uint64_t ivl_stmt_delay_val(ivl_statement_t net) { + assert(net); assert(net->type_ == IVL_ST_DELAY); return net->u_.delay_.value; } +extern "C" unsigned ivl_stmt_needs_t0_trigger(ivl_statement_t net) +{ + assert(net); + if (net->type_ == IVL_ST_WAIT) { + return net->u_.wait_.needs_t0_trigger; + } else { + return 0; + } +} + extern "C" unsigned ivl_stmt_nevent(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN_NB: return net->u_.assign_.nevent; @@ -2675,6 +2847,7 @@ extern "C" unsigned ivl_stmt_nevent(ivl_statement_t net) extern "C" ivl_event_t ivl_stmt_events(ivl_statement_t net, unsigned idx) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN_NB: assert(idx < net->u_.assign_.nevent); @@ -2702,6 +2875,7 @@ extern "C" ivl_event_t ivl_stmt_events(ivl_statement_t net, unsigned idx) extern "C" ivl_expr_t ivl_stmt_lexp(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_CONTRIB: return net->u_.contrib_.lval; @@ -2713,6 +2887,7 @@ extern "C" ivl_expr_t ivl_stmt_lexp(ivl_statement_t net) extern "C" ivl_lval_t ivl_stmt_lval(ivl_statement_t net, unsigned idx) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN: case IVL_ST_ASSIGN_NB: @@ -2731,6 +2906,7 @@ extern "C" ivl_lval_t ivl_stmt_lval(ivl_statement_t net, unsigned idx) extern "C" unsigned ivl_stmt_lvals(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN: case IVL_ST_ASSIGN_NB: @@ -2748,6 +2924,7 @@ extern "C" unsigned ivl_stmt_lvals(ivl_statement_t net) extern "C" unsigned ivl_stmt_lwidth(ivl_statement_t net) { + assert(net); assert((net->type_ == IVL_ST_ASSIGN) || (net->type_ == IVL_ST_ASSIGN_NB) || (net->type_ == IVL_ST_CASSIGN) @@ -2780,6 +2957,7 @@ extern "C" unsigned ivl_stmt_lwidth(ivl_statement_t net) extern "C" const char* ivl_stmt_name(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_STASK: return net->u_.stask_.name_; @@ -2792,6 +2970,7 @@ extern "C" const char* ivl_stmt_name(ivl_statement_t net) extern "C" char ivl_stmt_opcode(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN: return net->u_.assign_.oper; @@ -2803,6 +2982,7 @@ extern "C" char ivl_stmt_opcode(ivl_statement_t net) extern "C" ivl_expr_t ivl_stmt_parm(ivl_statement_t net, unsigned idx) { + assert(net); switch (net->type_) { case IVL_ST_STASK: assert(idx < net->u_.stask_.nparm_); @@ -2816,6 +2996,7 @@ extern "C" ivl_expr_t ivl_stmt_parm(ivl_statement_t net, unsigned idx) extern "C" unsigned ivl_stmt_parm_count(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_STASK: return net->u_.stask_.nparm_; @@ -2827,6 +3008,7 @@ extern "C" unsigned ivl_stmt_parm_count(ivl_statement_t net) extern "C" ivl_expr_t ivl_stmt_rval(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN: case IVL_ST_ASSIGN_NB: @@ -2844,6 +3026,7 @@ extern "C" ivl_expr_t ivl_stmt_rval(ivl_statement_t net) extern "C" ivl_sfunc_as_task_t ivl_stmt_sfunc_as_task(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_STASK: return net->u_.stask_.sfunc_as_task_; @@ -2856,6 +3039,7 @@ extern "C" ivl_sfunc_as_task_t ivl_stmt_sfunc_as_task(ivl_statement_t net) extern "C" ivl_statement_t ivl_stmt_sub_stmt(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_DELAY: return net->u_.delay_.stmt_; @@ -2878,67 +3062,80 @@ extern "C" ivl_statement_t ivl_stmt_sub_stmt(ivl_statement_t net) extern "C" const char*ivl_switch_basename(ivl_switch_t net) { + assert(net); return net->name; } extern "C" ivl_scope_t ivl_switch_scope(ivl_switch_t net) { + assert(net); return net->scope; } extern "C" ivl_switch_type_t ivl_switch_type(ivl_switch_t net) { + assert(net); return net->type; } extern "C" ivl_nexus_t ivl_switch_a(ivl_switch_t net) { + assert(net); return net->pins[0]; } extern "C" ivl_nexus_t ivl_switch_b(ivl_switch_t net) { + assert(net); return net->pins[1]; } extern "C" ivl_nexus_t ivl_switch_enable(ivl_switch_t net) { + assert(net); return net->pins[2]; } extern "C" unsigned ivl_switch_width(ivl_switch_t net) { + assert(net); return net->width; } extern "C" unsigned ivl_switch_part(ivl_switch_t net) { + assert(net); return net->part; } extern "C" unsigned ivl_switch_offset(ivl_switch_t net) { + assert(net); return net->offset; } extern "C" ivl_expr_t ivl_switch_delay(ivl_switch_t net, unsigned transition) { + assert(net); assert(transition < 3); return net->delay[transition]; } extern "C" const char* ivl_switch_file(ivl_switch_t net) { + assert(net); return net->file; } extern "C" ivl_island_t ivl_switch_island(ivl_switch_t net) { + assert(net); return net->island; } extern "C" unsigned ivl_switch_lineno(ivl_switch_t net) { + assert(net); return net->lineno; } @@ -2959,12 +3156,14 @@ extern "C" ivl_type_t ivl_type_element(ivl_type_t net) extern "C" unsigned ivl_type_packed_dimensions(ivl_type_t net) { + assert(net); vector slice = net->slice_dimensions(); return slice.size(); } extern "C" int ivl_type_packed_lsb(ivl_type_t net, unsigned dim) { + assert(net); vector slice = net->slice_dimensions(); assert(dim < slice.size()); return slice[dim].get_lsb(); @@ -2972,6 +3171,7 @@ extern "C" int ivl_type_packed_lsb(ivl_type_t net, unsigned dim) extern "C" int ivl_type_packed_msb(ivl_type_t net, unsigned dim) { + assert(net); vector slice = net->slice_dimensions(); assert(dim < slice.size()); return slice[dim].get_msb(); @@ -2979,9 +3179,8 @@ extern "C" int ivl_type_packed_msb(ivl_type_t net, unsigned dim) extern "C" const char* ivl_type_name(ivl_type_t net) { - if (const netclass_t*class_type = dynamic_cast(net)) { + if (const netclass_t*class_type = dynamic_cast(net)) return class_type->get_name(); - } return 0; } @@ -3013,5 +3212,6 @@ extern "C" ivl_type_t ivl_type_prop_type(ivl_type_t net, int idx) extern "C" int ivl_type_signed(ivl_type_t net) { + assert(net); return net->get_signed()? 1 : 0; } diff --git a/t-dll-expr.cc b/t-dll-expr.cc index 1231ea86f..edd0ecf7c 100644 --- a/t-dll-expr.cc +++ b/t-dll-expr.cc @@ -68,7 +68,7 @@ void dll_target::sub_off_from_expr_(long off) off >>= 1; } - /* Now make the subtractor (x-4 in the above example) + /* Now make the subtracter (x-4 in the above example) that has as input A the index expression and input B the constant to subtract. */ ivl_expr_t tmps = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); @@ -104,7 +104,7 @@ void dll_target::mul_expr_by_const_(long val) val >>= 1; } - /* Now make the subtractor (x-4 in the above example) + /* Now make the subtracter (x-4 in the above example) that has as input A the index expression and input B the constant to subtract. */ ivl_expr_t tmps = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); diff --git a/t-dll-proc.cc b/t-dll-proc.cc index e668919eb..ba0d4b1a0 100644 --- a/t-dll-proc.cc +++ b/t-dll-proc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 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 @@ -847,12 +847,15 @@ bool dll_target::proc_wait(const NetEvWait*net) /* This is a wait fork statement. */ if ((net->nevents() == 1) && (net->event(0) == 0)) { + stmt_cur_->u_.wait_.needs_t0_trigger = 0; stmt_cur_->u_.wait_.event = 0; stmt_cur_->type_ = IVL_ST_WAIT; stmt_cur_->u_.wait_.stmt_->type_ = IVL_ST_NOOP; return true; } + stmt_cur_->u_.wait_.needs_t0_trigger = net->has_t0_trigger(); + // This event processing code is also in the NB assign above. if (net->nevents() > 1) { stmt_cur_->u_.wait_.events = (ivl_event_t*) diff --git a/t-dll.cc b/t-dll.cc index 3c0b9fa2d..ada97415f 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2018 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -103,6 +103,13 @@ inline const char*dlerror(void) { return strerror( errno ); } #endif +ivl_scope_s::ivl_scope_s() +{ + func_type = IVL_VT_NO_TYPE; + func_signed = false; + func_width = 0; +} + /* * The custom new operator for the ivl_nexus_s type allows us to * allocate nexus objects in blocks. There are generally lots of them @@ -151,13 +158,6 @@ void ivl_net_const_s::operator delete(void*, size_t) static StringHeapLex net_const_strings; -inline static const char *basename(ivl_scope_t scope, const char *inst) -{ - inst += strlen(ivl_scope_name(scope)); - assert(*inst == '.'); - return inst+1; -} - static perm_string make_scope_name(const hname_t&name) { if (! name.has_numbers()) @@ -261,12 +261,6 @@ ivl_scope_t dll_target::find_scope(ivl_design_s &des, const NetScope*cur) return tmp; } - if (cur->type()==NetScope::TASK || cur->type()==NetScope::FUNC) { - map::const_iterator idx = des.root_tasks.find(cur); - if (idx != des.root_tasks.end()) - return idx->second; - } - for (unsigned idx = 0; idx < des.roots.size(); idx += 1) { assert(des.roots[idx]); ivl_scope_t scope = find_scope_from_root(des.roots[idx], cur); @@ -596,6 +590,28 @@ void dll_target::make_scope_param_expr(ivl_parameter_t cur_par, NetExpr*etmp) expr_ = 0; } +static void fill_in_scope_function(ivl_scope_t scope, const NetScope*net) +{ + scope->type_ = IVL_SCT_FUNCTION; + const NetFuncDef*def = net->func_def(); + assert(def); + + const NetNet*return_sig = def->return_sig(); + if (return_sig == 0) { + // Special case: If there is no return signal, this is + // apparently a VOID function. + scope->func_type = IVL_VT_VOID; + scope->func_signed = 0; + scope->func_width = 0; + } else { + scope->func_type = return_sig->data_type(); + scope->func_signed = return_sig->get_signed(); + scope->func_width = return_sig->vector_width(); + } + + scope->tname_ = def->scope()->basename(); +} + void dll_target::add_root(const NetScope *s) { ivl_scope_t root_ = new struct ivl_scope_s; @@ -628,22 +644,6 @@ void dll_target::add_root(const NetScope *s) case NetScope::CLASS: root_->type_ = IVL_SCT_CLASS; break; - case NetScope::TASK: { - const NetTaskDef*def = s->task_def(); - if (def == 0) { - cerr << "?:?" << ": internal error: " - << "task " << root_->name_ - << " has no definition." << endl; - } - assert(def); - root_->type_ = IVL_SCT_TASK; - root_->tname_ = def->scope()->basename(); - break; - } - break; - case NetScope::FUNC: - root_->type_ = IVL_SCT_FUNCTION; - break; default: assert(0); } @@ -672,11 +672,6 @@ void dll_target::add_root(const NetScope *s) des_.classes[s] = root_; break; - case NetScope::TASK: - case NetScope::FUNC: - des_.root_tasks[s] = root_; - break; - default: assert(0); break; @@ -718,11 +713,7 @@ bool dll_target::start_design(const Design*des) } assert(idx == des_.disciplines.size()); - list scope_list = des->find_roottask_scopes(); - for (list::const_iterator cur = scope_list.begin() - ; cur != scope_list.end() ; ++ cur) { - add_root(*cur); - } + list scope_list; scope_list = des->find_package_scopes(); for (list::const_iterator cur = scope_list.begin() @@ -1253,6 +1244,12 @@ void dll_target::net_case_cmp(const NetCaseCmp*net) case NetCaseCmp::NEQ: obj->type = IVL_LPM_CMP_NEE; break; + case NetCaseCmp::WEQ: + obj->type = IVL_LPM_CMP_WEQ; + break; + case NetCaseCmp::WNE: + obj->type = IVL_LPM_CMP_WNE; + break; case NetCaseCmp::XEQ: obj->type = IVL_LPM_CMP_EQX; break; @@ -1938,6 +1935,9 @@ void dll_target::lpm_ff(const NetFF*net) const Nexus*nex; + /* Set the clock polarity. */ + obj->u_.ff.negedge_flag = net->is_negedge(); + /* Set the clk signal to point to the nexus, and the nexus to point back to this device. */ nex = net->pin_Clock().nexus(); @@ -1976,7 +1976,10 @@ void dll_target::lpm_ff(const NetFF*net) nexus_lpm_add(obj->u_.ff.aset, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); verinum tmp = net->aset_value(); - obj->u_.ff.aset_value = expr_from_value_(tmp); + if (tmp.len() > 0) + obj->u_.ff.aset_value = expr_from_value_(tmp); + else + obj->u_.ff.aset_value = 0; } else { obj->u_.ff.aset = 0; @@ -2001,7 +2004,10 @@ void dll_target::lpm_ff(const NetFF*net) nexus_lpm_add(obj->u_.ff.sset, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); verinum tmp = net->sset_value(); - obj->u_.ff.sset_value = expr_from_value_(tmp); + if (tmp.len() > 0) + obj->u_.ff.sset_value = expr_from_value_(tmp); + else + obj->u_.ff.sset_value = 0; } else { obj->u_.ff.sset = 0; @@ -2020,6 +2026,39 @@ void dll_target::lpm_ff(const NetFF*net) nexus_lpm_add(obj->u_.ff.d.pin, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); } +void dll_target::lpm_latch(const NetLatch*net) +{ + ivl_lpm_t obj = new struct ivl_lpm_s; + obj->type = IVL_LPM_LATCH; + obj->name = net->name(); + obj->scope = find_scope(des_, net->scope()); + assert(obj->scope); + FILE_NAME(obj, net); + + obj->width = net->width(); + + scope_add_lpm(obj->scope, obj); + + const Nexus*nex; + + nex = net->pin_Enable().nexus(); + assert(nex->t_cookie()); + obj->u_.latch.e = nex->t_cookie(); + assert(obj->u_.latch.e); + nexus_lpm_add(obj->u_.latch.e, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); + + nex = net->pin_Q().nexus(); + assert(nex->t_cookie()); + obj->u_.latch.q.pin = nex->t_cookie(); + nexus_lpm_add(obj->u_.latch.q.pin, obj, 0, + IVL_DR_STRONG, IVL_DR_STRONG); + + nex = net->pin_Data().nexus(); + assert(nex->t_cookie()); + obj->u_.latch.d.pin = nex->t_cookie(); + nexus_lpm_add(obj->u_.latch.d.pin, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); +} + /* * Make the NetMult object into an IVL_LPM_MULT node. */ @@ -2480,6 +2519,7 @@ void dll_target::scope(const NetScope*net) case NetScope::PACKAGE: cerr << "?:?" << ": internal error: " << "Package scopes should not have parents." << endl; + // fallthrough case NetScope::MODULE: scop->type_ = IVL_SCT_MODULE; scop->tname_ = net->module_name(); @@ -2506,8 +2546,7 @@ void dll_target::scope(const NetScope*net) break; } case NetScope::FUNC: - scop->type_ = IVL_SCT_FUNCTION; - scop->tname_ = net->func_def()->scope()->basename(); + fill_in_scope_function(scop, net); break; case NetScope::BEGIN_END: scop->type_ = IVL_SCT_BEGIN; diff --git a/t-dll.h b/t-dll.h index 918a95bde..879a5c966 100644 --- a/t-dll.h +++ b/t-dll.h @@ -1,7 +1,7 @@ #ifndef IVL_t_dll_H #define IVL_t_dll_H /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -76,6 +76,7 @@ struct dll_target : public target_t, public expr_scan_t { void lpm_compare(const NetCompare*); void lpm_divide(const NetDivide*); void lpm_ff(const NetFF*); + void lpm_latch(const NetLatch*); void lpm_modulo(const NetModulo*); void lpm_mult(const NetMult*); void lpm_mux(const NetMux*); @@ -370,6 +371,7 @@ struct ivl_lpm_s { union { struct ivl_lpm_ff_s { + unsigned negedge_flag :1; ivl_nexus_t clk; ivl_nexus_t we; ivl_nexus_t aclr; @@ -387,6 +389,17 @@ struct ivl_lpm_s { ivl_expr_t aset_value; ivl_expr_t sset_value; } ff; + struct ivl_lpm_latch_s { + ivl_nexus_t e; + union { + ivl_nexus_t*pins; + ivl_nexus_t pin; + } q; + union { + ivl_nexus_t*pins; + ivl_nexus_t pin; + } d; + } latch; struct ivl_lpm_mux_s { unsigned size; @@ -632,7 +645,7 @@ struct ivl_parameter_s { * that generally only matters for VPI calls. */ struct ivl_process_s { - ivl_process_type_t type_ : 2; + ivl_process_type_t type_ : 3; unsigned int analog_flag : 1; ivl_scope_t scope_; ivl_statement_t stmt_; @@ -652,6 +665,8 @@ struct ivl_process_s { * there. */ struct ivl_scope_s { + ivl_scope_s(); + ivl_scope_t parent; std::map children; // This is just like the children map above, but in vector @@ -685,6 +700,9 @@ struct ivl_scope_s { /* Scopes that are tasks/functions have a definition. */ ivl_statement_t def; unsigned is_auto; + ivl_variable_type_t func_type; + bool func_signed; + unsigned func_width; unsigned is_cell; @@ -844,6 +862,7 @@ struct ivl_statement_s { } utask_; struct { /* IVL_ST_TRIGGER IVL_ST_WAIT */ + unsigned needs_t0_trigger; unsigned nevent; union { ivl_event_t event; diff --git a/target.cc b/target.cc index a4fe8b44a..15cc27c5c 100644 --- a/target.cc +++ b/target.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2013 Stephen Williams + * Copyright (c) 1998-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 @@ -183,6 +183,12 @@ void target_t::lpm_ff(const NetFF*) "Unhandled NetFF." << endl; } +void target_t::lpm_latch(const NetLatch*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled NetLatch." << endl; +} + void target_t::lpm_mult(const NetMult*) { cerr << "target (" << typeid(*this).name() << "): " diff --git a/target.h b/target.h index ec834418d..be39f7ca4 100644 --- a/target.h +++ b/target.h @@ -1,7 +1,7 @@ #ifndef IVL_target_H #define IVL_target_H /* - * 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 @@ -95,6 +95,7 @@ struct target_t { virtual void lpm_divide(const NetDivide*); virtual void lpm_modulo(const NetModulo*); virtual void lpm_ff(const NetFF*); + virtual void lpm_latch(const NetLatch*); virtual void lpm_mult(const NetMult*); virtual void lpm_mux(const NetMux*); virtual void lpm_pow(const NetPow*); diff --git a/tgt-blif/Makefile.in b/tgt-blif/Makefile.in index 88a984b04..850666ad0 100644 --- a/tgt-blif/Makefile.in +++ b/tgt-blif/Makefile.in @@ -44,7 +44,8 @@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ LDFLAGS = @LDFLAGS@ O = blif.o constants.o logic_gate.o lpm.o lpm_add.o lpm_cmp_eq.o lpm_cmp_gt.o \ - lpm_ff.o lpm_mux.o lpm_part_vp.o lpm_re_logic.o nex_data.o + lpm_ff.o lpm_mux.o lpm_part_vp.o lpm_re_logic.o lpm_shift.o lpm_sign_ext.o \ + nex_data.o all: dep blif.tgt diff --git a/tgt-blif/logic_gate.cc b/tgt-blif/logic_gate.cc index 44a9ce0a9..7088febfa 100644 --- a/tgt-blif/logic_gate.cc +++ b/tgt-blif/logic_gate.cc @@ -96,6 +96,20 @@ static int do_print_logic_gate(FILE*fd, ivl_net_logic_t net, unsigned bit) fprintf(fd, "0 1\n"); break; + case IVL_LO_PULLDOWN: + assert(ivl_logic_pins(net)==1); + fprintf(fd, "0\n"); + break; + case IVL_LO_PULLUP: + assert(ivl_logic_pins(net)==1); + fprintf(fd, "1\n"); + break; + + case IVL_LO_BUFZ: + assert(ivl_logic_pins(net)==2); + fprintf(fd, "1 1\n"); + break; + default: fprintf(fd, "# ERROR: Logic type %d not handled\n", ivl_logic_type(net)); rc += 1; diff --git a/tgt-blif/lpm.cc b/tgt-blif/lpm.cc index 3b0a4fba6..3abe846fd 100644 --- a/tgt-blif/lpm.cc +++ b/tgt-blif/lpm.cc @@ -67,8 +67,9 @@ static int print_concat(FILE*fd, ivl_lpm_t net) int print_lpm(FILE*fd, ivl_lpm_t net) { int rc = 0; + ivl_lpm_type_t type = ivl_lpm_type(net); - switch (ivl_lpm_type(net)) { + switch (type) { case IVL_LPM_ADD: rc += print_lpm_add(fd, net); break; @@ -108,6 +109,13 @@ int print_lpm(FILE*fd, ivl_lpm_t net) case IVL_LPM_SUB: rc += print_lpm_sub(fd, net); break; + case IVL_LPM_SHIFTL: + case IVL_LPM_SHIFTR: + rc += print_lpm_shift(fd, net, type == IVL_LPM_SHIFTL); + break; + case IVL_LPM_SIGN_EXT: + rc += print_lpm_sign_ext(fd, net); + break; default: fprintf(fd, "# XXXX ivl_lpm_type(net) --> %d\n", ivl_lpm_type(net)); fprintf(stderr, "%s:%u: sorry: ivl_lpm_type(net)==%d not implemented.\n", diff --git a/tgt-blif/lpm_cmp_gt.cc b/tgt-blif/lpm_cmp_gt.cc index 331aab0db..831c9a1c9 100644 --- a/tgt-blif/lpm_cmp_gt.cc +++ b/tgt-blif/lpm_cmp_gt.cc @@ -51,7 +51,7 @@ static int print_lpm_cmp_gt_s(FILE*fd, ivl_lpm_t net) return 0; } - fprintf(stderr, "%s:%u: sorry: blif: Signed agnitude compare not implemented yet\n", + fprintf(stderr, "%s:%u: sorry: blif: Signed magnitude compare not implemented yet\n", ivl_lpm_file(net), ivl_lpm_lineno(net)); return 1; diff --git a/tgt-blif/lpm_shift.cc b/tgt-blif/lpm_shift.cc new file mode 100644 index 000000000..17c3f4071 --- /dev/null +++ b/tgt-blif/lpm_shift.cc @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2016 Yury Gribov (tetra2005@gmail.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 "priv.h" +# include "nex_data.h" +# include + +/* + * Implement IVL_LPM_SHIFT devices (via standard layered barrel shifter design) + */ +int print_lpm_shift(FILE*fd, ivl_lpm_t net, bool left) +{ + fprintf(fd, "# %s:%u: IVL_LPM_SHIFT%c: width=%u\n", + ivl_lpm_file(net), ivl_lpm_lineno(net), left ? 'L' : 'R', + ivl_lpm_width(net)); + + ivl_nexus_t q_nex = ivl_lpm_q(net); + ivl_nexus_t d_nex = ivl_lpm_data(net,0); + ivl_nexus_t s_nex = ivl_lpm_data(net,1); + + blif_nex_data_t*q_ned = blif_nex_data_t::get_nex_data(q_nex); + blif_nex_data_t*d_ned = blif_nex_data_t::get_nex_data(d_nex); + blif_nex_data_t*s_ned = blif_nex_data_t::get_nex_data(s_nex); + + unsigned dataw = ivl_lpm_width(net); + size_t shiftw = s_ned->get_width(); + bool signed_ = ivl_lpm_signed(net); + + assert(dataw == q_ned->get_width()); + assert(dataw == d_ned->get_width()); + + // TODO: output width can be larger than data + + // TODO: optimizations: + // * too large shift widths (more than data size) + // * shift width of 1 + // * data width of 1 + + for (size_t lvl = 0 ; lvl < shiftw ; lvl += 1) { + for (unsigned idx = 0 ; idx < dataw ; idx += 1) { + unsigned idx_2 = left ? idx - (1 << lvl) : idx + (1 << lvl); + bool borrow = idx_2 >= dataw; + bool signed_borrow = borrow && !left && signed_; + + // First arg (shift) + fprintf(fd, ".names %s%s", + s_ned->get_name(), s_ned->get_name_index(lvl)); + + // Multiplexed bits + if (!borrow) { + if (lvl == 0) { + fprintf(fd, " %s%s %s%s", + d_ned->get_name(), d_ned->get_name_index(idx), + d_ned->get_name(), d_ned->get_name_index(idx_2)); + } else { + fprintf(fd, " %s/%zu/%u %s/%zu/%u", + q_ned->get_name(), lvl - 1, idx, + q_ned->get_name(), lvl - 1, idx_2); + } + } else if (signed_borrow) { + if (lvl == 0) { + fprintf(fd, " %s%s %s%s", + d_ned->get_name(), d_ned->get_name_index(idx), + d_ned->get_name(), d_ned->get_name_index(dataw - 1)); + } else { + fprintf(fd, " %s/%zu/%u %s%s", + q_ned->get_name(), lvl - 1, idx, + d_ned->get_name(), d_ned->get_name_index(dataw - 1)); + } + } else { + if (lvl == 0) { + fprintf(fd, " %s%s", + d_ned->get_name(), d_ned->get_name_index(idx)); + } else { + fprintf(fd, " %s/%zu/%u", + q_ned->get_name(), lvl - 1, idx); + } + } + + // Output + if (lvl == shiftw - 1) { + fprintf(fd, " %s%s\n", + q_ned->get_name(), q_ned->get_name_index(idx)); + } else { + fprintf(fd, " %s/%zu/%u\n", + q_ned->get_name(), lvl, idx); + } + + if (!borrow || signed_borrow) + fputs("1-1 1\n" + "01- 1\n", + fd); + else + fputs("01 1\n", fd); + } + } + + return 0; +} + diff --git a/tgt-blif/lpm_sign_ext.cc b/tgt-blif/lpm_sign_ext.cc new file mode 100644 index 000000000..311eddee6 --- /dev/null +++ b/tgt-blif/lpm_sign_ext.cc @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016 Yury Gribov (tetra2005@gmail.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 "priv.h" +# include "nex_data.h" +# include + +/* + * Implement IVL_LPM_SIGN_EXT devices + */ +int print_lpm_sign_ext(FILE*fd, ivl_lpm_t net) +{ + fprintf(fd, "# %s:%u: IVL_LPM_SIGN_EXT: width=%u\n", + ivl_lpm_file(net), ivl_lpm_lineno(net), ivl_lpm_width(net)); + + ivl_nexus_t q_nex = ivl_lpm_q(net); + ivl_nexus_t d_nex = ivl_lpm_data(net,0); + + blif_nex_data_t*q_ned = blif_nex_data_t::get_nex_data(q_nex); + blif_nex_data_t*d_ned = blif_nex_data_t::get_nex_data(d_nex); + + unsigned inw = d_ned->get_width(); + unsigned outw = ivl_lpm_width(net); + +//printf("Shift: LPM width = %u, output width = %zd, input width = %u\n", outw, q_ned->get_width(), inw); + + assert(outw == q_ned->get_width()); + assert(inw < outw); + + for (unsigned idx = 0 ; idx < outw ; idx += 1) { + unsigned idx_in = idx < inw ? idx : inw - 1; + + fprintf(fd, ".names %s%s %s%s\n", + d_ned->get_name(), d_ned->get_name_index(idx_in), + q_ned->get_name(), q_ned->get_name_index(idx)); + fprintf(fd, "1 1\n"); + } + + return 0; +} + diff --git a/tgt-blif/nex_data.h b/tgt-blif/nex_data.h index 0614e28de..5f599a258 100644 --- a/tgt-blif/nex_data.h +++ b/tgt-blif/nex_data.h @@ -32,7 +32,7 @@ class blif_nex_data_t { private: // The constructors are private. Only the get_nex_data() // function can create these objects. - blif_nex_data_t(ivl_nexus_t nex); + explicit blif_nex_data_t(ivl_nexus_t nex); ~blif_nex_data_t(); public: diff --git a/tgt-blif/priv.h b/tgt-blif/priv.h index 3bd6a1f02..8f961175b 100644 --- a/tgt-blif/priv.h +++ b/tgt-blif/priv.h @@ -40,6 +40,8 @@ extern int print_lpm_cmp_ne(FILE*fd, ivl_lpm_t net); extern int print_lpm_mux(FILE*fd, ivl_lpm_t net); extern int print_lpm_part_vp(FILE*fd, ivl_lpm_t net); extern int print_lpm_re_logic(FILE*fd, ivl_lpm_t net); +extern int print_lpm_shift(FILE*fd, ivl_lpm_t net, bool left); +extern int print_lpm_sign_ext(FILE*fd, ivl_lpm_t net); /* * Emit all the constants for a model. This works by scanning the diff --git a/tgt-null/null.c b/tgt-null/null.c index 184fba111..10e170e3d 100644 --- a/tgt-null/null.c +++ b/tgt-null/null.c @@ -30,7 +30,7 @@ static const char*version_string = "Icarus Verilog NULL Code Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com)\n\n" +"Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" diff --git a/tgt-pcb/fp.lex b/tgt-pcb/fp.lex index af3292252..3536f51ae 100644 --- a/tgt-pcb/fp.lex +++ b/tgt-pcb/fp.lex @@ -6,7 +6,7 @@ %{ /* - * Copyright (C) 2011-2013 Stephen Williams (steve@icarus.com) + * Copyright (C) 2011-2017 Stephen Williams (steve@icarus.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 @@ -79,11 +79,14 @@ void init_fp_lexor(FILE*fd) yyrestart(fd); } +/* + * Modern version of flex (>=2.5.9) can clean up the scanner data. + */ void destroy_fp_lexor() { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 -# if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 +# if YY_FLEX_MINOR_VERSION > 5 || defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif diff --git a/tgt-stub/constant.c b/tgt-stub/constant.c index 4074e071a..4d99dc924 100644 --- a/tgt-stub/constant.c +++ b/tgt-stub/constant.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2013-2015 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 @@ -25,13 +25,26 @@ void show_constant(ivl_net_const_t net) { - unsigned idx; - const char*bits = ivl_const_bits(net); assert(net); fprintf(out, "constant "); - for (idx = 0 ; idx < ivl_const_width(net) ; idx += 1) - fprintf(out, "%c", bits[idx]); - + switch (ivl_const_type(net)) { + case IVL_VT_BOOL: + case IVL_VT_LOGIC: + { + const char*bits = ivl_const_bits(net); + unsigned idx; + assert(bits); + for (idx = 0 ; idx < ivl_const_width(net) ; idx += 1) + fprintf(out, "%c", bits[idx]); + } + break; + case IVL_VT_REAL: + fprintf(out, "%f", ivl_const_real(net)); + break; + default: + fprintf(out, ""); + break; + } fprintf(out, " at %s:%u\n", ivl_const_file(net), ivl_const_lineno(net)); } diff --git a/tgt-stub/cppcheck.sup b/tgt-stub/cppcheck.sup index e72becfc0..a4a512adb 100644 --- a/tgt-stub/cppcheck.sup +++ b/tgt-stub/cppcheck.sup @@ -2,7 +2,7 @@ // are not used here. // target_design() -unusedFunction:stub.c:1773 +unusedFunction:stub.c:1774 // target_query() -unusedFunction:stub.c:1843 +unusedFunction:stub.c:1844 diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index 451da0441..800c87f82 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 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,7 +36,7 @@ static const char*version_string = "Icarus Verilog STUB Code Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com)\n\n" +"Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" @@ -394,6 +394,12 @@ static void show_lpm_cmp_eeq(ivl_lpm_t net) case IVL_LPM_CMP_NEE: str = "NEE"; break; + case IVL_LPM_CMP_WEQ: + str = "WEQ"; + break; + case IVL_LPM_CMP_WNE: + str = "WNE"; + break; default: assert(0); break; @@ -497,10 +503,11 @@ static void show_lpm_concat(ivl_lpm_t net) static void show_lpm_ff(ivl_lpm_t net) { ivl_nexus_t nex; + char*edge = ivl_lpm_negedge(net) ? "negedge" : "posedge"; unsigned width = ivl_lpm_width(net); - fprintf(out, " LPM_FF %s: \n", - ivl_lpm_basename(net), width); + fprintf(out, " LPM_FF %s: \n", + ivl_lpm_basename(net), edge, width); nex = ivl_lpm_clk(net); fprintf(out, " clk: %p\n", nex); @@ -558,6 +565,39 @@ static void show_lpm_ff(ivl_lpm_t net) } +static void show_lpm_latch(ivl_lpm_t net) +{ + ivl_nexus_t nex; + unsigned width = ivl_lpm_width(net); + + fprintf(out, " LPM_LATCH %s: \n", + ivl_lpm_basename(net), width); + + nex = ivl_lpm_enable(net); + fprintf(out, " E: %p\n", nex); + if (width_of_nexus(nex) != 1) { + fprintf(out, " E: ERROR: Nexus width is %u\n", + width_of_nexus(nex)); + stub_errors += 1; + } + + nex = ivl_lpm_data(net,0); + fprintf(out, " D: %p\n", nex); + if (width_of_nexus(nex) != width) { + fprintf(out, " D: ERROR: Nexus width is %u\n", + width_of_nexus(nex)); + stub_errors += 1; + } + + nex = ivl_lpm_q(net); + fprintf(out, " Q: %p\n", nex); + if (width_of_nexus(nex) != width) { + fprintf(out, " Q: ERROR: Nexus width is %u\n", + width_of_nexus(nex)); + stub_errors += 1; + } +} + static void show_lpm_mod(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); @@ -1009,6 +1049,8 @@ static void show_lpm(ivl_lpm_t net) case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_NEE: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: show_lpm_cmp_eeq(net); break; @@ -1016,6 +1058,10 @@ static void show_lpm(ivl_lpm_t net) show_lpm_ff(net); break; + case IVL_LPM_LATCH: + show_lpm_latch(net); + break; + case IVL_LPM_CMP_GE: show_lpm_cmp_ge(net); break; @@ -1120,6 +1166,24 @@ static int show_process(ivl_process_t net, void*x) else fprintf(out, "always\n"); break; + case IVL_PR_ALWAYS_COMB: + if (ivl_process_analog(net)) + assert(0); + else + fprintf(out, "always_comb\n"); + break; + case IVL_PR_ALWAYS_FF: + if (ivl_process_analog(net)) + assert(0); + else + fprintf(out, "always_ff\n"); + break; + case IVL_PR_ALWAYS_LATCH: + if (ivl_process_analog(net)) + assert(0); + else + fprintf(out, "always_latch\n"); + break; case IVL_PR_FINAL: if (ivl_process_analog(net)) fprintf(out, "analog final\n"); diff --git a/tgt-verilog/verilog.c b/tgt-verilog/verilog.c index 333277a59..62616dba0 100644 --- a/tgt-verilog/verilog.c +++ b/tgt-verilog/verilog.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 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 @@ -393,6 +393,9 @@ static int show_process(ivl_process_t net, void*x) fprintf(out, " initial\n"); break; case IVL_PR_ALWAYS: + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: fprintf(out, " always\n"); break; case IVL_PR_FINAL: diff --git a/tgt-vhdl/state.cc b/tgt-vhdl/state.cc index 43e448828..a6a23c12c 100644 --- a/tgt-vhdl/state.cc +++ b/tgt-vhdl/state.cc @@ -144,7 +144,7 @@ ivl_signal_t find_signal_named(const std::string &name, const vhdl_scope *scope) // Compare the name of an entity against a string struct cmp_ent_name { - cmp_ent_name(const string& n) : name_(n) {} + explicit cmp_ent_name(const string& n) : name_(n) {} bool operator()(const vhdl_entity* ent) const { diff --git a/tgt-vhdl/stmt.cc b/tgt-vhdl/stmt.cc index bb4a3ae40..51ac8bb20 100644 --- a/tgt-vhdl/stmt.cc +++ b/tgt-vhdl/stmt.cc @@ -1,7 +1,7 @@ /* * VHDL code generation for statements. * - * Copyright (C) 2008-2013 Nick Gasson (nick@nickg.me.uk) + * Copyright (C) 2008-2018 Nick Gasson (nick@nickg.me.uk) * * 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 @@ -683,8 +683,10 @@ static void get_nexuses_from_expr(ivl_expr_t expr, set &out) break; case IVL_EX_TERNARY: get_nexuses_from_expr(ivl_expr_oper3(expr), out); + // fallthrough case IVL_EX_BINARY: get_nexuses_from_expr(ivl_expr_oper2(expr), out); + // fallthrough case IVL_EX_UNARY: get_nexuses_from_expr(ivl_expr_oper1(expr), out); break; @@ -1180,7 +1182,7 @@ static long get_number_as_long(ivl_expr_t expr) } if (ivl_expr_signed(expr) && bits[nbits-1] == '1' && - nbits < 8*sizeof(long)) imm |= -1L << nbits; + nbits < 8*sizeof(long)) imm |= -1UL << nbits; } break; } @@ -1248,6 +1250,7 @@ static void process_number(vhdl_binop_expr *all, vhdl_var_ref *test, switch (bits[i]) { case 'x': if (is_casez) break; + // fallthrough case '?': case 'z': continue; // Ignore these. diff --git a/tgt-vhdl/support.hh b/tgt-vhdl/support.hh index 063234be1..b42683c7a 100644 --- a/tgt-vhdl/support.hh +++ b/tgt-vhdl/support.hh @@ -41,7 +41,7 @@ enum support_function_t { class support_function : public vhdl_function { public: - support_function(support_function_t type) + explicit support_function(support_function_t type) : vhdl_function(function_name(type), function_type(type)), type_(type) {} void emit(std::ostream &of, int level) const; diff --git a/tgt-vhdl/vhdl.conf b/tgt-vhdl/vhdl.conf index 0d382d395..a331b8ca5 100644 --- a/tgt-vhdl/vhdl.conf +++ b/tgt-vhdl/vhdl.conf @@ -1,3 +1,4 @@ functor:cprop functor:nodangle flag:DLL=vhdl.tgt +flag:DISABLE_CONCATZ_GENERATION=true diff --git a/tgt-vhdl/vhdl_element.cc b/tgt-vhdl/vhdl_element.cc index e7e0957b6..1b5c45191 100644 --- a/tgt-vhdl/vhdl_element.cc +++ b/tgt-vhdl/vhdl_element.cc @@ -108,7 +108,7 @@ void vhdl_element::print() const // Trap allocations of vhdl_element subclasses. // This records the pointer allocated in a static field of vhdl_element // so we can delete it just before the code generator exits. -void* vhdl_element::operator new(size_t size) throw (bad_alloc) +void* vhdl_element::operator new(size_t size) { // Let the default new handle the allocation void* ptr = ::operator new(size); @@ -171,4 +171,3 @@ int vhdl_element::free_all_objects() return freed; } - diff --git a/tgt-vhdl/vhdl_element.hh b/tgt-vhdl/vhdl_element.hh index a324e227d..8cf11d4cf 100644 --- a/tgt-vhdl/vhdl_element.hh +++ b/tgt-vhdl/vhdl_element.hh @@ -46,7 +46,7 @@ class vhdl_element { public: virtual ~vhdl_element() {} - void* operator new(size_t size) throw (std::bad_alloc); + void* operator new(size_t size); void operator delete(void* ptr); virtual void emit(std::ostream &of, int level=0) const = 0; @@ -74,4 +74,3 @@ std::string nl_string(int level); void blank_line(std::ostream &of, int level); #endif - diff --git a/tgt-vhdl/vhdl_syntax.hh b/tgt-vhdl/vhdl_syntax.hh index 4e09971ce..7efecbaf8 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -178,7 +178,7 @@ private: class vhdl_const_string : public vhdl_expr { public: - vhdl_const_string(const string& value) + explicit vhdl_const_string(const string& value) : vhdl_expr(vhdl_type::string(), true), value_(value) {} void emit(std::ostream &of, int level) const; @@ -207,7 +207,7 @@ private: class vhdl_const_bit : public vhdl_expr { public: - vhdl_const_bit(char bit) + explicit vhdl_const_bit(char bit) : vhdl_expr(vhdl_type::std_logic(), true), bit_(bit) {} void emit(std::ostream &of, int level) const; vhdl_expr *to_boolean(); @@ -237,7 +237,7 @@ private: class vhdl_const_int : public vhdl_expr { public: - vhdl_const_int(int64_t value) + explicit vhdl_const_int(int64_t value) : vhdl_expr(vhdl_type::integer(), true), value_(value) {} void emit(std::ostream &of, int level) const; vhdl_expr *to_vector(vhdl_type_name_t name, int w); @@ -247,7 +247,7 @@ private: class vhdl_const_bool : public vhdl_expr { public: - vhdl_const_bool(bool value) + explicit vhdl_const_bool(bool value) : vhdl_expr(vhdl_type::boolean(), true), value_(value) {} void emit(std::ostream &of, int level) const; private: @@ -478,7 +478,7 @@ private: class vhdl_assert_stmt : public vhdl_report_stmt { public: - vhdl_assert_stmt(const char *reason); + explicit vhdl_assert_stmt(const char *reason); void emit(ostream &of, int level) const; }; @@ -486,7 +486,7 @@ public: class vhdl_if_stmt : public vhdl_seq_stmt { public: - vhdl_if_stmt(vhdl_expr *test); + explicit vhdl_if_stmt(vhdl_expr *test); ~vhdl_if_stmt(); stmt_container *get_then_container() { return &then_part_; } @@ -513,7 +513,7 @@ private: class vhdl_case_branch : public vhdl_element { friend class vhdl_case_stmt; public: - vhdl_case_branch(vhdl_expr *when) : when_(when) {} + explicit vhdl_case_branch(vhdl_expr *when) : when_(when) {} ~vhdl_case_branch(); stmt_container *get_container() { return &stmts_; } @@ -527,7 +527,7 @@ typedef std::list case_branch_list_t; class vhdl_case_stmt : public vhdl_seq_stmt { public: - vhdl_case_stmt(vhdl_expr *test) : test_(test) {} + explicit vhdl_case_stmt(vhdl_expr *test) : test_(test) {} ~vhdl_case_stmt(); void add_branch(vhdl_case_branch *b) { branches_.push_back(b); } @@ -554,7 +554,7 @@ private: class vhdl_while_stmt : public vhdl_loop_stmt { public: - vhdl_while_stmt(vhdl_expr *test) : test_(test) {} + explicit vhdl_while_stmt(vhdl_expr *test) : test_(test) {} ~vhdl_while_stmt(); void emit(std::ostream &of, int level) const; @@ -584,7 +584,7 @@ private: */ class vhdl_pcall_stmt : public vhdl_seq_stmt { public: - vhdl_pcall_stmt(const char *name) : name_(name) {} + explicit vhdl_pcall_stmt(const char *name) : name_(name) {} void emit(std::ostream &of, int level) const; void add_expr(vhdl_expr *e) { exprs_.add_expr(e); } @@ -658,7 +658,7 @@ public: void emit(std::ostream &of, int level) const; private: - vhdl_component_decl(const char *name); + explicit vhdl_component_decl(const char *name); decl_list_t ports_; }; @@ -852,7 +852,7 @@ private: class vhdl_forward_fdecl : public vhdl_decl { public: - vhdl_forward_fdecl(const vhdl_function *f) + explicit vhdl_forward_fdecl(const vhdl_function *f) : vhdl_decl((f->get_name() + "_Forward").c_str()), f_(f) {} void emit(std::ostream &of, int level) const; @@ -863,7 +863,7 @@ private: class vhdl_process : public vhdl_conc_stmt, public vhdl_procedural { public: - vhdl_process(const char *name = "") : name_(name) {} + explicit vhdl_process(const char *name = "") : name_(name) {} void emit(std::ostream &of, int level) const; void add_sensitivity(const std::string &name); diff --git a/tgt-vlog95/event.c b/tgt-vlog95/event.c index f4b3d370d..0a1c0b881 100644 --- a/tgt-vlog95/event.c +++ b/tgt-vlog95/event.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2017 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 @@ -21,6 +21,24 @@ # include "config.h" # include "vlog95_priv.h" +static unsigned need_ivl_top_module = 0; + +void emit_icarus_generated_top_module() +{ + if (need_ivl_top_module) { + fprintf(vlog_out, +"\n" +"/*\n" +" * This module is used to trigger any always_comb or always_latch processes\n" +" * at time zero to make sure all the outputs have the correct values.\n" +"*/\n" +"module IVL_top_priv_module;\n" +" event IVL_T0_trigger_event;\n" +" initial #0 -> IVL_T0_trigger_event;\n" +"endmodule /* IVL_top_priv_module */\n"); + } +} + void emit_event(ivl_scope_t scope, ivl_statement_t stmt) { unsigned eidx, nevents, first = 1; @@ -68,4 +86,12 @@ void emit_event(ivl_scope_t scope, ivl_statement_t stmt) emit_id(ivl_event_basename(event)); } } + + /* If this is an always_comb/latch then we need to add a trigger to + * get the correct functionality. */ + if (ivl_stmt_needs_t0_trigger(stmt)) { + assert(first == 0); + fprintf(vlog_out, " or IVL_top_priv_module.IVL_T0_trigger_event"); + need_ivl_top_module = 1; + }; } diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index f516a7a9e..c8c16c00d 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2018 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 @@ -44,8 +44,10 @@ static expr_sign_t expr_get_binary_sign_type(ivl_expr_t expr) switch (ivl_expr_opcode(expr)) { case 'E': case 'e': + case 'w': case 'N': case 'n': + case 'W': case '<': case 'L': case '>': @@ -417,8 +419,10 @@ static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid, case 'p': oper = "**"; break; case 'E': oper = "==="; break; case 'e': oper = "=="; break; + case 'w': oper = "==?"; break; case 'N': oper = "!=="; break; case 'n': oper = "!="; break; + case 'W': oper = "!=?"; break; case '<': oper = "<"; break; case 'L': oper = "<="; break; case '>': oper = ">"; break; @@ -447,6 +451,7 @@ static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid, ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; } + //fallthrough case '+': case '-': case '*': @@ -465,6 +470,14 @@ static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid, fprintf(vlog_out, " %s ", oper); emit_expr(scope, oper2, wid, 0, can_skip_unsigned, is_full_prec); break; + case 'w': + case 'W': + fprintf(stderr, "%s:%u: vlog95 error: The wild equality operators " + "cannot be converted.\n", + ivl_expr_file(expr), + ivl_expr_lineno(expr)); + vlog_errors += 1; + // fallthrough case 'E': case 'e': case 'N': @@ -492,6 +505,7 @@ static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid, ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; } + // fallthrough case 'l': case 'r': emit_expr(scope, oper1, wid, 0, 0, 0); diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index 7e390d809..202f3ec39 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2018 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 @@ -31,7 +31,8 @@ typedef enum lpm_sign_e { } lpm_sign_t; static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, - int*base, unsigned*array_word); + int*msb, int*lsb, unsigned*array_word, + ivl_net_const_t*accept_const); /* * Look to see if the nexus driver is signed. @@ -68,12 +69,12 @@ static int nexus_driver_is_signed(ivl_scope_t scope, ivl_nexus_t nex) * local signal to get the sign information. */ if (ivl_logic_type(t_nlogic) == IVL_LO_BUFZ) { unsigned array_word = 0; - int base = 0; + int msb = 0, lsb = 0; ivl_signal_t sig; assert(ivl_logic_pins(t_nlogic) == 2); sig = nexus_is_signal(scope, ivl_logic_pin(t_nlogic, 0), - &base, &array_word); + &msb, &lsb, &array_word, 0); assert(sig); is_signed = ivl_signal_signed(sig); } @@ -88,6 +89,19 @@ static int nexus_driver_is_signed(ivl_scope_t scope, ivl_nexus_t nex) return is_signed; } +static unsigned get_nexus_width(ivl_nexus_t nex) +{ + unsigned idx, count = ivl_nexus_ptrs(nex); + + for (idx = 0; idx < count; idx += 1) { + ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); + ivl_signal_t sig = ivl_nexus_ptr_sig(nex_ptr); + if (sig) return ivl_signal_width(sig); + } + assert(0); + return 0; +} + static lpm_sign_t lpm_get_sign_type(ivl_lpm_t lpm, unsigned can_skip_unsigned) { @@ -196,6 +210,14 @@ static void emit_gate_strength(ivl_net_logic_t nlogic, unsigned strength_type) "gate", ivl_logic_file(nlogic), ivl_logic_lineno(nlogic)); } +static void emit_part_selector(int msb, int lsb) +{ + if (msb != lsb) + fprintf(vlog_out, "[%d:%d]", msb, lsb); + else + fprintf(vlog_out, "[%d]", lsb); +} + /* * Look for a single driver behind an LPM that passes strength information * and get the real drive information from it. @@ -285,6 +307,44 @@ static void emit_delay(ivl_scope_t scope, ivl_expr_t rise, ivl_expr_t fall, fprintf(vlog_out, ")"); } +static void emit_driver_delay(ivl_scope_t scope, ivl_nexus_t nex) +{ + ivl_expr_t rise = 0; + ivl_expr_t fall = 0; + ivl_expr_t decay = 0; + unsigned idx, count = ivl_nexus_ptrs(nex); + + for (idx = 0; idx < count; idx += 1) { + ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); + ivl_lpm_t t_lpm = ivl_nexus_ptr_lpm(nex_ptr); + ivl_net_const_t t_net_const = ivl_nexus_ptr_con(nex_ptr); + ivl_net_logic_t t_nlogic = ivl_nexus_ptr_log(nex_ptr); + ivl_drive_t cur_drive1 = ivl_nexus_ptr_drive1(nex_ptr); + ivl_drive_t cur_drive0 = ivl_nexus_ptr_drive0(nex_ptr); + if ((cur_drive1 == IVL_DR_HiZ) && + (cur_drive0 == IVL_DR_HiZ)) continue; + /* Only one driver can set the delay. */ + assert( ! rise); + if (t_lpm) { + rise = ivl_lpm_delay(t_lpm, 0); + fall = ivl_lpm_delay(t_lpm, 1); + decay = ivl_lpm_delay(t_lpm, 2); + } + if (t_net_const) { + rise = ivl_const_delay(t_net_const, 0); + fall = ivl_const_delay(t_net_const, 1); + decay = ivl_const_delay(t_net_const, 2); + } + if (t_nlogic) { + rise = ivl_logic_delay(t_nlogic, 0); + fall = ivl_logic_delay(t_nlogic, 1); + decay = ivl_logic_delay(t_nlogic, 2); + } + } + + emit_delay(scope, rise, fall, decay, 3); +} + static unsigned is_local_nexus(ivl_scope_t scope, ivl_nexus_t nex) { unsigned idx, count = ivl_nexus_ptrs(nex); @@ -338,10 +398,13 @@ static ivl_nexus_t get_lpm_output(ivl_scope_t scope, ivl_lpm_t lpm) case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: case IVL_LPM_DIVIDE: case IVL_LPM_FF: + case IVL_LPM_LATCH: case IVL_LPM_MOD: case IVL_LPM_MULT: case IVL_LPM_MUX: @@ -585,7 +648,8 @@ void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex, unsigned allow_UD, } else if (sig) { // HERE: should these be allow_UD? if (must_be_sig) { - emit_nexus_as_ca(scope, ivl_signal_nex(sig, word), + emit_nexus_as_ca(ivl_signal_scope(sig), + ivl_signal_nex(sig, word), 0, 0); } else emit_name_of_nexus(scope, nex, 0); // HERE: The assert causes pr1703959 to fail. @@ -758,7 +822,8 @@ static ivl_signal_t find_output_signal(ivl_scope_t scope, ivl_nexus_t nex, } static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, - int*base, unsigned*array_word) + int*msb, int*lsb, unsigned*array_word, + ivl_net_const_t*accept_const) { unsigned idx, count = ivl_nexus_ptrs(nex); ivl_lpm_t lpm = 0; @@ -767,10 +832,16 @@ static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, ivl_signal_t sig; /* Look for a signal in the local scope first. */ sig = find_local_signal(scope, nex, array_word); - if (sig) return sig; + if (sig) { + get_sig_msb_lsb(sig, msb, lsb); + return sig; + } /* Now look for an output signal driving into the local scope. */ sig = find_output_signal(scope, nex, array_word); - if (sig) return sig; + if (sig) { + get_sig_msb_lsb(sig, msb, lsb); + return sig; + } /* Now scan the nexus looking for a driver. */ for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); @@ -785,11 +856,18 @@ static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, /* The real signal could be hidden behind a select. */ if (ivl_lpm_type(t_lpm) == IVL_LPM_PART_VP) { t_sig = nexus_is_signal(scope, ivl_lpm_data(t_lpm, 0), - base, array_word); + msb, lsb, array_word, 0); } - if (t_sig) *base += ivl_lpm_base(t_lpm); - else lpm = t_lpm; + if (t_sig) { + if (*msb >= *lsb) { + *lsb += ivl_lpm_base(t_lpm); + *msb = *lsb + (int)ivl_lpm_width(t_lpm) - 1; + } else { + *lsb -= ivl_lpm_base(t_lpm); + *msb = *lsb - (int)ivl_lpm_width(t_lpm) + 1; + } + } else lpm = t_lpm; } if (t_net_const) { assert(! net_const); @@ -802,7 +880,7 @@ static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, assert(ivl_logic_pins(t_nlogic) == 2); t_sig = nexus_is_signal(scope, ivl_logic_pin(t_nlogic, 1), - base, array_word); + msb, lsb, array_word, 0); } else nlogic = t_nlogic; } if (t_sig) { @@ -812,7 +890,14 @@ static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, *array_word = ivl_nexus_ptr_pin(nex_ptr); } } - return sig; + if (sig) return sig; + + if (accept_const && net_const) { + *lsb = 0; + *msb = ivl_const_width(net_const) - 1; + *accept_const = net_const; + } + return 0; } static void emit_lpm_part_select(ivl_scope_t scope, ivl_lpm_t lpm, @@ -821,9 +906,9 @@ static void emit_lpm_part_select(ivl_scope_t scope, ivl_lpm_t lpm, unsigned width = ivl_lpm_width(lpm); unsigned array_word = 0; int base = ivl_lpm_base(lpm); - int msb, lsb; + int msb = 0, lsb = 0; ivl_signal_t sig = nexus_is_signal(scope, ivl_lpm_data(lpm, 0), - &base, &array_word); + &msb, &lsb, &array_word, 0); if (sign_extend && !allow_signed) { fprintf(stderr, "%s:%u: vlog95 error: >>> operator is not " @@ -854,9 +939,9 @@ static void emit_lpm_part_select(ivl_scope_t scope, ivl_lpm_t lpm, fprintf(vlog_out, "[%d]", array_idx); } - get_sig_msb_lsb(sig, &msb, &lsb); if (sign_extend) { assert(base != lsb); +// HERE: This looks wrong. if (msb >= lsb) base += lsb; else base = lsb - base; fprintf(vlog_out, " >>> %d)", base); @@ -880,7 +965,7 @@ static void emit_lpm_part_select(ivl_scope_t scope, ivl_lpm_t lpm, } } else { /* Skip a select of the entire bit. */ - if ((msb == lsb) && (lsb == base)) return; + if ((msb == lsb) && (base == 0)) return; fprintf(vlog_out, "["); if (msb >= lsb) base += lsb; else base = lsb - base; @@ -926,6 +1011,120 @@ static void emit_lpm_func(ivl_scope_t scope, ivl_lpm_t lpm) } } +static void emit_mux_select_bit(ivl_scope_t scope, ivl_lpm_t lpm, unsigned bit) +{ + unsigned array_word = 0; + int msb = 0, lsb = 0; + + ivl_signal_t sig = nexus_is_signal(scope, ivl_lpm_select(lpm), + &msb, &lsb, &array_word, 0); + assert(sig); + emit_scope_call_path(scope, ivl_signal_scope(sig)); + emit_id(ivl_signal_basename(sig)); + if (ivl_signal_dimensions(sig)) { + int array_idx = (int) array_word + ivl_signal_array_base(sig); + fprintf(vlog_out, "[%d]", array_idx); + } + if (msb >= lsb) { + fprintf(vlog_out, "[%d]", lsb + (int)bit); + } else { + fprintf(vlog_out, "[%d]", lsb - (int)bit); + } +} + +static void emit_lpm_mux(ivl_scope_t scope, ivl_lpm_t lpm, + unsigned sel_bit, unsigned offset) +{ + assert(sel_bit < sizeof(unsigned)*8); + fprintf(vlog_out, "("); + emit_mux_select_bit(scope, lpm, sel_bit); + fprintf(vlog_out, " ? "); + if (sel_bit > 0) { + emit_lpm_mux(scope, lpm, sel_bit - 1, offset + (1U << sel_bit)); + fprintf(vlog_out, " : "); + emit_lpm_mux(scope, lpm, sel_bit - 1, offset); + } else { + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, offset + 1), 0, 0); + fprintf(vlog_out, " : "); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, offset + 0), 0, 0); + } + fprintf(vlog_out, ")"); +} + +static void emit_lpm_substitute(ivl_scope_t scope, ivl_lpm_t lpm) +{ + unsigned array_word = 0; + int msb = 0, lsb = 0; + unsigned base = ivl_lpm_base(lpm); + unsigned width; + int psb; + int wid; + + /* Find the wider signal. Accept a constant if there's no signal. */ + ivl_net_const_t net_const = 0; + ivl_signal_t sig = nexus_is_signal(scope, ivl_lpm_data(lpm, 0), + &msb, &lsb, &array_word, + &net_const); + assert(sig || net_const); + + // Get the width of the part being substituted. + width = get_nexus_width(ivl_lpm_data(lpm, 1)); + + fprintf(vlog_out, "{"); + + if (msb >= lsb) { + psb = lsb + base + width; wid = 1 + msb - psb; + } else { + psb = lsb - base - width; wid = 1 + psb - msb; + } + if (wid > 0) { + if (sig) { + emit_scope_call_path(scope, ivl_signal_scope(sig)); + emit_id(ivl_signal_basename(sig)); + if (ivl_signal_dimensions(sig)) { + int array_idx = (int) array_word + ivl_signal_array_base(sig); + fprintf(vlog_out, "[%d]", array_idx); + } + emit_part_selector(msb, psb); + } else { + assert (msb >= psb); + emit_number(ivl_const_bits(net_const) + psb, wid, 0, + ivl_const_file(net_const), + ivl_const_lineno(net_const)); + } + + fprintf(vlog_out, ", "); + } + + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); + + if (msb >= lsb) { + psb = lsb + base - 1; wid = 1 + psb - lsb; + } else { + psb = lsb - base + 1; wid = 1 + lsb - psb; + } + if (wid > 0) { + fprintf(vlog_out, ", "); + + if (sig) { + emit_scope_call_path(scope, ivl_signal_scope(sig)); + emit_id(ivl_signal_basename(sig)); + if (ivl_signal_dimensions(sig)) { + int array_idx = (int) array_word + ivl_signal_array_base(sig); + fprintf(vlog_out, "[%d]", array_idx); + } + emit_part_selector(psb, lsb); + } else { + assert (psb >= lsb); + emit_number(ivl_const_bits(net_const), wid, 0, + ivl_const_file(net_const), + ivl_const_lineno(net_const)); + } + } + + fprintf(vlog_out, "}"); +} + static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, unsigned sign_extend) { @@ -980,7 +1179,7 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, case IVL_LPM_CAST_INT: case IVL_LPM_CAST_INT2: case IVL_LPM_CAST_REAL: - emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 1, 0); break; case IVL_LPM_CMP_EEQ: fprintf(vlog_out, "("); @@ -997,19 +1196,19 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, fprintf(vlog_out, ")"); break; case IVL_LPM_CMP_EQX: -// HERE: Need to heck that this is not a real nexus. +// HERE: Need to check that this is not a real nexus. fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " ==? "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); - fprintf(stderr, "%s:%u: vlog95 error: Compare wildcard equal " + fprintf(stderr, "%s:%u: vlog95 error: Compare wildcard equal (caseX) " "operator is not supported.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; break; case IVL_LPM_CMP_EQZ: -// HERE: Need to heck that this is not a real nexus. +// HERE: Need to check that this is not a real nexus. fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " == "); @@ -1048,6 +1247,28 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; + case IVL_LPM_CMP_WEQ: + fprintf(vlog_out, "("); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); + fprintf(vlog_out, " ==? "); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); + fprintf(vlog_out, ")"); + fprintf(stderr, "%s:%u: vlog95 error: Wild equality " + "operator is not supported.\n", + ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); + vlog_errors += 1; + break; + case IVL_LPM_CMP_WNE: + fprintf(vlog_out, "("); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); + fprintf(vlog_out, " !=? "); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); + fprintf(vlog_out, ")"); + fprintf(stderr, "%s:%u: vlog95 error: Wild inequality " + "operator is not supported.\n", + ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); + vlog_errors += 1; + break; /* A concat-Z should never be generated, but report it as an * error if one is generated. */ case IVL_LPM_CONCATZ: @@ -1055,6 +1276,7 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, "should not be generated.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; + // fallthrough case IVL_LPM_CONCAT: emit_lpm_concat(scope, lpm); break; @@ -1087,13 +1309,17 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, fprintf(vlog_out, ")"); break; case IVL_LPM_MUX: - fprintf(vlog_out, "("); - emit_nexus_as_ca(scope, ivl_lpm_select(lpm), 0, 0); - fprintf(vlog_out, " ? "); - emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); - fprintf(vlog_out, " : "); - emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); - fprintf(vlog_out, ")"); + if (ivl_lpm_selects(lpm) > 1) { + emit_lpm_mux(scope, lpm, ivl_lpm_selects(lpm) - 1, 0); + } else { + fprintf(vlog_out, "("); + emit_nexus_as_ca(scope, ivl_lpm_select(lpm), 0, 0); + fprintf(vlog_out, " ? "); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); + fprintf(vlog_out, " : "); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); + fprintf(vlog_out, ")"); + } break; case IVL_LPM_PART_PV: emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 1, 0); @@ -1188,11 +1414,7 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, fprintf(vlog_out, ")"); break; case IVL_LPM_SUBSTITUTE: - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 sorry: Substitute LPMs are " - "not currently translated.\n", - ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); - vlog_errors += 1; + emit_lpm_substitute(scope, lpm); break; case IVL_LPM_UFUNC: emit_scope_path(scope, ivl_lpm_define(lpm)); @@ -1245,7 +1467,62 @@ static void emit_posedge_dff_prim(void) fprintf(vlog_out, "endprimitive\n"); } +static void emit_negedge_dff_prim(void) +{ + fprintf(vlog_out, "\n"); + fprintf(vlog_out, "/* Icarus generated UDP to represent a synthesized " + "negative edge D-FF. */\n"); + fprintf(vlog_out, "primitive IVL_negedge_DFF " + "(q, clk, en, d, clr, set);\n"); + fprintf(vlog_out, "%*coutput q;\n", indent_incr, ' '); + fprintf(vlog_out, "%*cinput clk, en, d, clr, set;\n", indent_incr, ' '); + fprintf(vlog_out, "%*creg q;\n", indent_incr, ' '); + fprintf(vlog_out, "%*ctable\n", indent_incr, ' '); + fprintf(vlog_out, "%*cf 1 0 0 0 : ? : 0 ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*cf 1 1 0 0 : ? : 1 ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*cn 1 0 0 0 : 0 : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*cn 1 1 0 0 : 1 : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*cn x 0 0 0 : 0 : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*cn x 1 0 0 : 1 : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*cp ? ? 0 0 : ? : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c* 0 ? 0 0 : ? : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c? * ? ? ? : ? : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c? ? * ? ? : ? : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c? ? ? * ? : ? : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c? ? ? ? * : ? : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c? ? ? 0 1 : ? : 1 ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c? ? ? 0 x : 1 : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c? ? ? 0 x : 0 : x ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c? ? ? 1 ? : ? : 0 ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c? ? ? x 0 : 0 : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c? ? ? x 0 : 1 : x ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c? ? ? x x : ? : x ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c? ? ? x 1 : ? : x ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*cendtable\n", indent_incr, ' '); + fprintf(vlog_out, "endprimitive\n"); +} + +static void emit_latch_prim(void) +{ + fprintf(vlog_out, "\n"); + fprintf(vlog_out, "/* Icarus generated UDP to represent a synthesized " + "LATCH. */\n"); + fprintf(vlog_out, "primitive IVL_LATCH " + "(q, en, d);\n"); + fprintf(vlog_out, "%*coutput q;\n", indent_incr, ' '); + fprintf(vlog_out, "%*cinput en, d;\n", indent_incr, ' '); + fprintf(vlog_out, "%*creg q;\n", indent_incr, ' '); + fprintf(vlog_out, "%*ctable\n", indent_incr, ' '); + fprintf(vlog_out, "%*c 1 0 : ? : 0 ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c 1 1 : ? : 1 ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c 0 ? : ? : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*cendtable\n", indent_incr, ' '); + fprintf(vlog_out, "endprimitive\n"); +} + static unsigned need_posedge_dff_prim = 0; +static unsigned need_negedge_dff_prim = 0; +static unsigned need_latch_prim = 0; /* * Synthesis creates a D-FF LPM object. To allow this to be simulated as @@ -1263,14 +1540,14 @@ void emit_icarus_generated_udps() { /* Emit the copyright information and LGPL note and then emit any * needed primitives. */ - if (need_posedge_dff_prim) { + if (need_posedge_dff_prim || need_negedge_dff_prim || need_latch_prim) { fprintf(vlog_out, "\n" "/*\n" " * This is the copyright information for the following primitive(s)\n" " * (library elements).\n" " *\n" -" * Copyright (C) 2011 Cary R. (cygcary@yahoo.com)\n" +" * Copyright (C) 2011-2016 Cary R. (cygcary@yahoo.com)\n" " *\n" " * This library is free software; you can redistribute it and/or\n" " * modify it under the terms of the GNU Lesser General Public\n" @@ -1290,13 +1567,13 @@ void emit_icarus_generated_udps() "NOTE: vlog95: Adding LGPL 2.1 primitive(s) at the end of the output file.\n"); } if (need_posedge_dff_prim) emit_posedge_dff_prim(); + if (need_negedge_dff_prim) emit_negedge_dff_prim(); + if (need_latch_prim) emit_latch_prim(); } static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm) { -// HERE: No support for lpm attributes and hence polarity information. -// ivl_attribute_t clock_pol = find_lpm_attr(lpm, "Clock:LPM_Polarity"); - ivl_attribute_t clock_pol = 0; + unsigned negedge = ivl_lpm_negedge(lpm); ivl_expr_t aset_expr = ivl_lpm_aset_value(lpm); ivl_expr_t sset_expr = ivl_lpm_sset_value(lpm); ivl_nexus_t nex; @@ -1305,7 +1582,13 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm) const char *sset_bits = 0; /* For now we only support a width of 1 for these bits. */ if (aset_expr) { - assert(ivl_expr_width(aset_expr) == 1); + if (ivl_expr_width(aset_expr) != 1) { + fprintf(stderr, "%s:%u: vlog95 sorry: FF LPMs with " + "multi-bit asynchronous set values are not " + "currently translated.\n", + ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); + vlog_errors += 1; + } aset_bits = ivl_expr_bits(aset_expr); } if (sset_expr) { @@ -1314,9 +1597,7 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm) } fprintf(vlog_out, "%*c", indent, ' '); - /* If there is a clock polarity attribute then we have a negative - * edge D-FF. */ - if (clock_pol) { + if (negedge) { fprintf(vlog_out, "IVL_negedge_DFF"); } else { fprintf(vlog_out, "IVL_posedge_DFF"); @@ -1368,7 +1649,7 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm) } nex = ivl_lpm_sync_set(lpm); if (nex) { - if (! sset_bits || (sset_bits && (sset_bits[0] == '1'))) { + if (! sset_bits || (sset_bits[0] == '1')) { emit_nexus_as_ca(scope, nex, 0, 0); if (have_data) fprintf(vlog_out, " | "); } else { @@ -1389,7 +1670,7 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm) emitted = 1; } nex = ivl_lpm_async_set(lpm); - if (aset_bits && (aset_bits[0] != '0')) nex = 0; + if (!aset_bits || (aset_bits[0] != '0')) nex = 0; if (nex) { if (emitted) fprintf(vlog_out, " | "); emit_nexus_as_ca(scope, nex, 0, 0); @@ -1404,7 +1685,45 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm) else fprintf(vlog_out, "1'b0"); fprintf(vlog_out, ");\n"); /* We need to emit a primitive for this instance. */ - need_posedge_dff_prim = 1; + if (negedge) + need_negedge_dff_prim = 1; + else + need_posedge_dff_prim = 1; +} + +static void emit_lpm_latch(ivl_scope_t scope, ivl_lpm_t lpm) +{ + ivl_nexus_t nex; + unsigned emitted; + + fprintf(vlog_out, "%*c", indent, ' '); + fprintf(vlog_out, "IVL_LATCH"); + emit_lpm_strength(lpm); + /* The lpm LATCH does not support any delays. */ + /* The LATCH name is a temporary so we don't bother to print it unless + * we have a range. Then we need to use a made up name. */ + if (ivl_lpm_width(lpm) > 1) { + fprintf(vlog_out, " synth_%p [%u:0]", lpm, ivl_lpm_width(lpm)-1U); + } + fprintf(vlog_out, " ("); + /* Emit the q pin. */ + emit_name_of_nexus(scope, ivl_lpm_q(lpm), 0); + fprintf(vlog_out, ", "); + /* Emit the enable pin expression(s) if needed. */ + emitted = 0; + nex = ivl_lpm_enable(lpm); + if (nex) { + emit_nexus_as_ca(scope, nex, 0, 0); + emitted = 1; + } + if (!emitted) fprintf(vlog_out, "1'b1"); + fprintf(vlog_out, ", "); + /* Emit the data pin expression(s). */ + nex = ivl_lpm_data(lpm, 0); + assert (nex); + emit_nexus_as_ca(scope, nex, 0, 0); + fprintf(vlog_out, ");\n"); + need_latch_prim = 1; } static ivl_signal_t get_output_from_nexus(ivl_scope_t scope, ivl_nexus_t nex, @@ -1530,19 +1849,28 @@ void emit_lpm(ivl_scope_t scope, ivl_lpm_t lpm) emit_lpm_ff(scope, lpm); return; } + if (type == IVL_LPM_LATCH) { + emit_lpm_latch(scope, lpm); + return; + } // HERE: Look for a select passed to a pull device (pr2019553). /* Skip assignments to a module instantiation input. */ if (output_is_module_instantiation_input(scope, output)) return; fprintf(vlog_out, "%*cassign", indent, ' '); emit_lpm_strength(lpm); - emit_delay(scope, - ivl_lpm_delay(lpm, 0), - ivl_lpm_delay(lpm, 1), - ivl_lpm_delay(lpm, 2), - 3); - fprintf(vlog_out, " "); - if (type == IVL_LPM_PART_PV) emit_lpm_part_pv(scope, lpm); - else emit_name_of_nexus(scope, output, 0); + if (type == IVL_LPM_PART_PV) { + emit_driver_delay(scope, ivl_lpm_data(lpm, 0)); + fprintf(vlog_out, " "); + emit_lpm_part_pv(scope, lpm); + } else { + emit_delay(scope, + ivl_lpm_delay(lpm, 0), + ivl_lpm_delay(lpm, 1), + ivl_lpm_delay(lpm, 2), + 3); + fprintf(vlog_out, " "); + emit_name_of_nexus(scope, output, 0); + } fprintf(vlog_out, " = "); emit_lpm_as_ca(scope, lpm, 0); fprintf(vlog_out, ";"); @@ -1900,7 +2228,7 @@ void emit_signal_net_const_as_ca(ivl_scope_t scope, ivl_signal_t sig) { ivl_nexus_t nex = ivl_signal_nex(sig, 0); unsigned idx, count = ivl_nexus_ptrs(nex); - unsigned long emitted = (unsigned long) ivl_nexus_get_private(nex); + unsigned long emitted = (uintptr_t) ivl_nexus_get_private(nex); for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_net_const_t net_const = ivl_nexus_ptr_con(nex_ptr); @@ -1930,7 +2258,7 @@ void emit_signal_net_const_as_ca(ivl_scope_t scope, ivl_signal_t sig) fprintf(vlog_out, "\n"); /* Increment the emitted constant count by one. */ ivl_nexus_set_private(nex, - (void *) ((unsigned long) ivl_nexus_get_private(nex) + 1U)); + (void *) ((uintptr_t) ivl_nexus_get_private(nex) + 1U)); return; } /* We must find the constant in the nexus. */ @@ -2000,6 +2328,7 @@ void dump_nexus_information(ivl_scope_t scope, ivl_nexus_t nex) case IVL_LPM_CMP_NEE: fprintf(stderr, "nee"); break; case IVL_LPM_DIVIDE: fprintf(stderr, "divide"); break; case IVL_LPM_FF: fprintf(stderr, "dff"); break; + case IVL_LPM_LATCH: fprintf(stderr, "latch"); break; case IVL_LPM_MOD: fprintf(stderr, "mod"); break; case IVL_LPM_MULT: fprintf(stderr, "mult"); break; case IVL_LPM_MUX: fprintf(stderr, "mux"); break; @@ -2125,10 +2454,9 @@ void dump_nexus_information(ivl_scope_t scope, ivl_nexus_t nex) case IVL_VT_BOOL: fprintf(stderr, " bool"); break; case IVL_VT_LOGIC: fprintf(stderr, " logic"); break; case IVL_VT_STRING: fprintf(stderr, " string"); break; - case IVL_VT_DARRAY: fprintf(stderr, " dynamic array"); - case IVL_VT_CLASS: fprintf(stderr, " class"); - case IVL_VT_QUEUE: fprintf(stderr, " queue"); - break; + case IVL_VT_DARRAY: fprintf(stderr, " dynamic array"); break; + case IVL_VT_CLASS: fprintf(stderr, " class"); break; + case IVL_VT_QUEUE: fprintf(stderr, " queue"); break; } if (ivl_signal_signed(sig)) fprintf(stderr, " "); } else fprintf(stderr, "Error: No/missing information!"); 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 e5043fa77..ee0372b80 100644 --- a/tgt-vlog95/scope.c +++ b/tgt-vlog95/scope.c @@ -1,5 +1,5 @@ /* - * 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 @@ -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) { @@ -432,10 +405,13 @@ static void emit_sig_type(ivl_signal_t sig) } /* Check to see if we have a variable (reg) or a net. */ if (type == IVL_SIT_REG) { + /* The variable data type will be declared later, so here + we just want to declare the range and whether or not it + is signed. */ if (ivl_signal_integer(sig)) { - fprintf(vlog_out, " integer"); + /* nothing to do */ } else if (ivl_signal_data_type(sig) == IVL_VT_REAL) { - fprintf(vlog_out, " real"); + /* nothing to do */ } else { int msb, lsb; get_sig_msb_lsb(sig, &msb, &lsb); @@ -703,17 +679,17 @@ static void emit_named_block_scope(ivl_scope_t scope) } /* - * In SystemVerilog a task or function can have a process to initialize - * variables. In reality SystemVerilog requires this to be before the - * initial/always blocks are processed, but that's not how it is currently - * implemented in Icarus! + * In SystemVerilog a task, function, or block can have a process to + * initialize variables. SystemVerilog requires this to be before the + * initial/always blocks are processed, but there's no way to express + * this in Verilog-95. */ -static int find_tf_process(ivl_process_t proc, ivl_scope_t scope) +static int find_tfb_process(ivl_process_t proc, ivl_scope_t scope) { if (scope == ivl_process_scope(proc)) { ivl_scope_t mod_scope = scope; - /* A task or function can only have initial processes that - * are used to set local variables. */ + /* A task or function or named block can only have initial + * processes that are used to set local variables. */ assert(ivl_process_type(proc) == IVL_PR_INITIAL); /* Find the module scope for this task/function. */ while (ivl_scope_type(mod_scope) != IVL_SCT_MODULE) { @@ -728,15 +704,16 @@ static int find_tf_process(ivl_process_t proc, ivl_scope_t scope) } /* - * Emit any initial blocks for the tasks or functions in a module. + * Emit any initial blocks for the tasks/functions/named blocks in a module. */ -static int emit_tf_process(ivl_scope_t scope, ivl_scope_t parent) +static int emit_tfb_process(ivl_scope_t scope, ivl_scope_t parent) { ivl_scope_type_t sc_type = ivl_scope_type(scope); (void)parent; /* Parameter is not used. */ - if ((sc_type == IVL_SCT_FUNCTION) || (sc_type == IVL_SCT_TASK)) { + if ((sc_type == IVL_SCT_FUNCTION) || (sc_type == IVL_SCT_TASK) || + (sc_type == IVL_SCT_BEGIN) || (sc_type == IVL_SCT_FORK)) { /* Output the initial/always blocks for this module. */ - ivl_design_process(design, (ivl_process_f)find_tf_process, scope); + ivl_design_process(design, (ivl_process_f)find_tfb_process, scope); } return 0; } @@ -1169,9 +1146,10 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent) emit_tran(scope, ivl_scope_switch(scope, idx)); } - /* Output any initial blocks for tasks or functions defined - * in this module. Used to initialize local variables. */ - ivl_scope_children(scope, (ivl_scope_f*) emit_tf_process, scope); + /* Output any initial blocks for tasks or functions or named + * blocks defined in this module. Used to initialize local + * variables. */ + ivl_scope_children(scope, (ivl_scope_f*) emit_tfb_process, scope); /* Output the initial/always blocks for this module. */ ivl_design_process(design, (ivl_process_f)find_process, scope); diff --git a/tgt-vlog95/stmt.c b/tgt-vlog95/stmt.c index 4fc67135f..8d2a98d5b 100644 --- a/tgt-vlog95/stmt.c +++ b/tgt-vlog95/stmt.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2017 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 @@ -1606,6 +1606,9 @@ void emit_process(ivl_scope_t scope, ivl_process_t proc) fprintf(vlog_out, "initial"); break; case IVL_PR_ALWAYS: + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: fprintf(vlog_out, "always"); break; case IVL_PR_FINAL: diff --git a/tgt-vlog95/vlog95-s.conf b/tgt-vlog95/vlog95-s.conf index 04e49e471..0229840ee 100644 --- a/tgt-vlog95/vlog95-s.conf +++ b/tgt-vlog95/vlog95-s.conf @@ -1,5 +1,7 @@ functor:synth2 functor:synth functor:syn-rules +functor:nodangle +functor:exposenodes flag:DLL=vlog95.tgt flag:DISABLE_CONCATZ_GENERATION=true diff --git a/tgt-vlog95/vlog95.c b/tgt-vlog95/vlog95.c index 1765b9468..8fe45fb10 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-2017 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 @@ -30,7 +30,7 @@ static const char*version_string = "Icarus Verilog VLOG95 Code Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (C) 2010-2013 Cary R. (cygcary@yahoo.com)\n\n" +"Copyright (C) 2010-2017 Cary R. (cygcary@yahoo.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" @@ -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,25 +189,29 @@ 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 objets. */ + /* Emit the rest of the scope objects. */ for (idx = 0; idx < nroots; idx += 1) emit_scope(roots[idx], 0); free_emitted_scope_list(); @@ -216,9 +219,12 @@ int target_design(ivl_design_t des) /* Emit any UDP definitions that the design used. */ emit_udp_list(); - /* Emit any UDPs that are Icarus generated (D-FF). */ + /* Emit any UDPs that are Icarus generated (D-FF or latch). */ emit_icarus_generated_udps(); + /* Emit the Icarus top module used to trigger translated always_comb/latch processes at T0. */ + emit_icarus_generated_top_module(); + /* If there were errors then add this information to the output. */ if (vlog_errors) { fprintf(vlog_out, "\n"); diff --git a/tgt-vlog95/vlog95_priv.h b/tgt-vlog95/vlog95_priv.h index fb632d95e..36e3cf543 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-2017 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 @@ -106,7 +106,6 @@ extern void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex, extern void emit_nexus_port_driver_as_ca(ivl_scope_t scope, ivl_nexus_t nex); extern void emit_const_nexus(ivl_scope_t scope, ivl_net_const_t const_net); extern void emit_signal_net_const_as_ca(ivl_scope_t scope, ivl_signal_t sig); -extern void emit_icarus_generated_udps(void); extern void add_udp_to_list(ivl_udp_t udp); extern void emit_udp_list(void); @@ -118,6 +117,9 @@ extern void emit_number(const char *bits, unsigned nbits, unsigned is_signed, const char *file, unsigned lineno); extern void emit_string(const char *string); +extern void emit_icarus_generated_udps(void); +extern void emit_icarus_generated_top_module(void); + /* * Find the enclosing module scope. */ @@ -145,6 +147,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/README.txt b/tgt-vvp/README.txt index 4242b1b09..f07323021 100644 --- a/tgt-vvp/README.txt +++ b/tgt-vvp/README.txt @@ -3,7 +3,7 @@ THE VVP TARGET SYMBOL NAME CONVENTIONS -There are some naming conventions that the vp target uses for +There are some naming conventions that the vvp target uses for generating symbol names. * wires and regs @@ -30,4 +30,4 @@ the drivers are first fed into a resolver (or a tree of resolvers) to form a single output that is the nexus. The nexus, then, feeds its output to the inputs of other gates, or to -the .net objects in the design. \ No newline at end of file +the .net objects in the design. diff --git a/tgt-vvp/cppcheck.sup b/tgt-vvp/cppcheck.sup index 3c7cc0938..9f6d9483e 100644 --- a/tgt-vvp/cppcheck.sup +++ b/tgt-vvp/cppcheck.sup @@ -2,7 +2,7 @@ // are not used here. // target_design() -unusedFunction:vvp.c:149 +unusedFunction:vvp.c:148 // target_query() unusedFunction:vvp.c:246 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..16d793d73 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)); @@ -119,7 +83,7 @@ static void draw_lpm_mux_nest(ivl_lpm_t net, const char*muxz) net, select_input); for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 2) { - fprintf(vvp_out, "L_%p/0/%d .functor %s %u", + fprintf(vvp_out, "L_%p/0/%u .functor %s %u", net, idx/2, muxz, width); fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,idx+0))); fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,idx+1))); @@ -131,10 +95,10 @@ static void draw_lpm_mux_nest(ivl_lpm_t net, const char*muxz) net, level, select_input, level, level); for (idx = 0 ; idx < (ivl_lpm_size(net) >> level); idx += 2) { - fprintf(vvp_out, "L_%p/%u/%d .functor %s %u", + fprintf(vvp_out, "L_%p/%u/%u .functor %s %u", net, level, idx/2, muxz, width); - fprintf(vvp_out, ", L_%p/%d/%d", net, level-1, idx+0); - fprintf(vvp_out, ", L_%p/%d/%d", net, level-1, idx+1); + fprintf(vvp_out, ", L_%p/%u/%u", net, level-1, idx+0); + fprintf(vvp_out, ", L_%p/%u/%u", net, level-1, idx+1); fprintf(vvp_out, ", L_%p/%us", net, level); fprintf(vvp_out, ", C4<>;\n"); } @@ -142,14 +106,14 @@ static void draw_lpm_mux_nest(ivl_lpm_t net, const char*muxz) } - fprintf(vvp_out, "L_%p/%ds .part %s, %d, 1; Bit %d of the select\n", + fprintf(vvp_out, "L_%p/%us .part %s, %u, 1; Bit %u of the select\n", net, swidth-1, select_input, swidth-1, swidth-1); fprintf(vvp_out, "L_%p .functor %s %u", net, muxz, width); - fprintf(vvp_out, ", L_%p/%d/0", net, swidth-2); - fprintf(vvp_out, ", L_%p/%d/1", net, swidth-2); - fprintf(vvp_out, ", L_%p/%ds", net, swidth-1); + fprintf(vvp_out, ", L_%p/%u/0", net, swidth-2); + fprintf(vvp_out, ", L_%p/%u/1", net, swidth-2); + fprintf(vvp_out, ", L_%p/%us", net, swidth-1); fprintf(vvp_out, ", C4<>;\n"); free(select_input); diff --git a/tgt-vvp/draw_net_input.c b/tgt-vvp/draw_net_input.c index 906be39a0..86ec41657 100644 --- a/tgt-vvp/draw_net_input.c +++ b/tgt-vvp/draw_net_input.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2017 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,10 +26,6 @@ # include # include "ivl_alloc.h" -#ifdef __MINGW32__ /* MinGW has inconsistent %p output. */ -#define snprintf _snprintf -#endif - static ivl_signal_type_t signal_type_of_nexus(ivl_nexus_t nex) { unsigned idx; @@ -334,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); @@ -372,74 +370,24 @@ 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); if (lpm) switch (ivl_lpm_type(lpm)) { case IVL_LPM_FF: + case IVL_LPM_LATCH: case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_ARRAY: @@ -450,6 +398,8 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr) case IVL_LPM_CONCATZ: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_GE: @@ -703,17 +653,20 @@ static void draw_net_input_x(ivl_nexus_t nex, /* If the nexus has no drivers, then send a constant HiZ or 0.0 into the net. */ if (ndrivers == 0) { + unsigned wid = width_of_nexus(nex); /* For real nets put 0.0. */ if (signal_data_type_of_nexus(nex) == IVL_VT_REAL) { nex_private = draw_Cr_to_string(0.0); } else { - unsigned jdx, wid = width_of_nexus(nex); + unsigned jdx; char*tmp = malloc(wid + 5); nex_private = tmp; strcpy(tmp, "C4<"); 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'; @@ -731,18 +684,17 @@ static void draw_net_input_x(ivl_nexus_t nex, } *tmp++ = '>'; *tmp = 0; - - /* Create an "open" driver to hold the HiZ. We - need to do this so that .nets have something to - hang onto. */ - char buf[64]; - snprintf(buf, sizeof buf, "o%p", nex); - fprintf(vvp_out, "%s .functor BUFZ %u, %s; HiZ drive\n", - buf, wid, nex_private); - nex_private = realloc(nex_private, strlen(buf)+1); - strcpy(nex_private, buf); } + /* Create an "open" driver to hold the HiZ or 0.0. We need + to do this so that .nets have something to hang onto. */ + char buf[64]; + snprintf(buf, sizeof buf, "o%p", nex); + fprintf(vvp_out, "%s .functor BUFZ %u, %s; HiZ drive\n", + buf, wid, nex_private); + nex_private = realloc(nex_private, strlen(buf)+1); + strcpy(nex_private, buf); + if (island) { char*tmp2 = draw_island_port(island, island_input_flag, nex, nex_data, nex_private); free(nex_private); diff --git a/tgt-vvp/draw_switch.c b/tgt-vvp/draw_switch.c index b53d214b0..fa0391810 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-2018 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,30 +55,33 @@ 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); } switch (ivl_switch_type(sw)) { + case IVL_SW_RTRAN: + fprintf(vvp_out, " .rtran"); + break; + case IVL_SW_RTRANIF0: + fprintf(vvp_out, " .rtranif0"); + break; + case IVL_SW_RTRANIF1: + fprintf(vvp_out, " .rtranif1"); + break; case IVL_SW_TRAN: fprintf(vvp_out, " .tran"); break; @@ -106,8 +97,7 @@ void draw_switch_in_scope(ivl_switch_t sw) break; default: - fprintf(stderr, "%s:%u: tgt-vvp sorry: resistive switch modeling " - "is not currently supported.\n", + fprintf(stderr, "%s:%u: tgt-vvp error: unrecognised switch type.\n", ivl_switch_file(sw), ivl_switch_lineno(sw)); vvp_errors += 1; return; diff --git a/tgt-vvp/draw_ufunc.c b/tgt-vvp/draw_ufunc.c index 572de9b7a..7f6fd1aaf 100644 --- a/tgt-vvp/draw_ufunc.c +++ b/tgt-vvp/draw_ufunc.c @@ -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 @@ -130,10 +130,36 @@ static void draw_ufunc_preamble(ivl_expr_t expr) } /* Call the function */ - fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); - fprintf(vvp_out, ", S_%p;\n", def); - fprintf(vvp_out, " %%join;\n"); - + switch (ivl_expr_value(expr)) { + case IVL_VT_VOID: + fprintf(vvp_out, " %%callf/void TD_%s", vvp_mangle_id(ivl_scope_name(def))); + fprintf(vvp_out, ", S_%p;\n", def); + break; + case IVL_VT_REAL: + fprintf(vvp_out, " %%callf/real TD_%s", vvp_mangle_id(ivl_scope_name(def))); + fprintf(vvp_out, ", S_%p;\n", def); + break; + case IVL_VT_BOOL: + case IVL_VT_LOGIC: + fprintf(vvp_out, " %%callf/vec4 TD_%s", vvp_mangle_id(ivl_scope_name(def))); + fprintf(vvp_out, ", S_%p;\n", def); + break; + case IVL_VT_STRING: + fprintf(vvp_out, " %%callf/str TD_%s", vvp_mangle_id(ivl_scope_name(def))); + fprintf(vvp_out, ", S_%p;\n", def); + break; + case IVL_VT_CLASS: + case IVL_VT_DARRAY: + case IVL_VT_QUEUE: + fprintf(vvp_out, " %%callf/obj TD_%s", vvp_mangle_id(ivl_scope_name(def))); + fprintf(vvp_out, ", S_%p;\n", def); + break; + default: + fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); + fprintf(vvp_out, ", S_%p;\n", def); + fprintf(vvp_out, " %%join;\n"); + break; + } } static void draw_ufunc_epilogue(ivl_expr_t expr) @@ -159,48 +185,33 @@ static void draw_ufunc_epilogue(ivl_expr_t expr) void draw_ufunc_vec4(ivl_expr_t expr) { - ivl_scope_t def = ivl_expr_def(expr); - ivl_signal_t retval = ivl_scope_port(def, 0); /* Take in arguments to function and call function code. */ draw_ufunc_preamble(expr); - assert(ivl_signal_dimensions(retval) == 0); - fprintf(vvp_out, " %%load/vec4 v%p_0;\n", retval); - draw_ufunc_epilogue(expr); } void draw_ufunc_real(ivl_expr_t expr) { - ivl_scope_t def = ivl_expr_def(expr); - ivl_signal_t retval = ivl_scope_port(def, 0); /* Take in arguments to function and call the function code. */ draw_ufunc_preamble(expr); - /* Return value signal cannot be an array. */ - assert(ivl_signal_dimensions(retval) == 0); - - /* Load the result into a word. */ - fprintf(vvp_out, " %%load/real v%p_0;\n", retval); + /* The %callf/real function emitted by the preamble leaves + the result in the stack for us. */ draw_ufunc_epilogue(expr); } void draw_ufunc_string(ivl_expr_t expr) { - ivl_scope_t def = ivl_expr_def(expr); - ivl_signal_t retval = ivl_scope_port(def, 0); /* Take in arguments to function and call the function code. */ draw_ufunc_preamble(expr); - /* Return value signal cannot be an array. */ - assert(ivl_signal_dimensions(retval) == 0); - - /* Load the result into a word. */ - fprintf(vvp_out, " %%load/str v%p_0;\n", retval); + /* The %callf/str function emitted by the preamble leaves + the result in the stack for us. */ draw_ufunc_epilogue(expr); } diff --git a/tgt-vvp/draw_vpi.c b/tgt-vvp/draw_vpi.c index 8e7f21642..b8503a537 100644 --- a/tgt-vvp/draw_vpi.c +++ b/tgt-vvp/draw_vpi.c @@ -23,10 +23,6 @@ # include # include "ivl_alloc.h" -#ifdef __MINGW32__ /* MinGW has inconsistent %p output. */ -#define snprintf _snprintf -#endif - struct args_info { char*text; /* True ('s' or 'u' if this argument is a calculated vec4. */ @@ -103,6 +99,12 @@ static int get_vpi_taskfunc_signal_arg(struct args_info *result, /* This should never happen since we have IVL_EX_SELECT. */ return 0; + } else if (signal_is_return_value(ivl_expr_signal(expr))) { + /* If the signal is the return value of a function, + then this can't be handled as a true signal, so + fall back on general expression processing. */ + return 0; + } else if (ivl_expr_signed(expr) != ivl_signal_signed(ivl_expr_signal(expr))) { return 0; @@ -517,3 +519,14 @@ void draw_vpi_rfunc_call(ivl_expr_t fnet) draw_vpi_taskfunc_args(call_string, 0, fnet); } + +void draw_vpi_sfunc_call(ivl_expr_t fnet) +{ + char call_string[1024]; + + sprintf(call_string, " %%vpi_func/s %u %u \"%s\"", + ivl_file_table_index(ivl_expr_file(fnet)), + ivl_expr_lineno(fnet), ivl_expr_name(fnet)); + + draw_vpi_taskfunc_args(call_string, 0, fnet); +} diff --git a/tgt-vvp/eval_condit.c b/tgt-vvp/eval_condit.c index d4cd104a6..2e46894ef 100644 --- a/tgt-vvp/eval_condit.c +++ b/tgt-vvp/eval_condit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2014-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 @@ -76,8 +76,8 @@ static int draw_condition_binary_compare(ivl_expr_t expr) %cmp instruction. */ if (ivl_expr_width(le)==use_wid && test_immediate_vec4_ok(le)) { ivl_expr_t tmp = le; - re = le; - le = tmp; + le = re; + re = tmp; } draw_eval_vec4(le); @@ -189,21 +189,48 @@ static int draw_condition_binary_le(ivl_expr_t expr) char use_opcode = ivl_expr_opcode(expr); char s_flag = (ivl_expr_signed(le) && ivl_expr_signed(re)) ? 's' : 'u'; - /* If this is a > or >=, then convert it to < or <= by - swapping the operands. Adjust the opcode to match. */ - switch (use_opcode) { - case 'G': + if (test_immediate_vec4_ok(le) && !test_immediate_vec4_ok(re)) { tmp = le; le = re; re = tmp; - use_opcode = 'L'; - break; - case '>': - tmp = le; - le = re; - re = tmp; - use_opcode = '<'; - break; + + switch (use_opcode) { + case 'G': + use_opcode = 'L'; + break; + case 'L': + use_opcode = 'G'; + break; + case '>': + use_opcode = '<'; + break; + case '<': + use_opcode = '>'; + break; + default: + assert(0); + break; + } + + } else if (!test_immediate_vec4_ok(re)) { + /* If this is a > or >=, then convert it to < or <= by + swapping the operands. Adjust the opcode to match. Do + this because the instruction really only supports < + and <= and we can avoid a %flag_inv instruction. */ + switch (use_opcode) { + case 'G': + tmp = le; + le = re; + re = tmp; + use_opcode = 'L'; + break; + case '>': + tmp = le; + le = re; + re = tmp; + use_opcode = '<'; + break; + } } /* NOTE: I think I would rather the elaborator handle the @@ -231,6 +258,13 @@ static int draw_condition_binary_le(ivl_expr_t expr) } switch (use_opcode) { + case '>': + fprintf(vvp_out, " %%flag_or 5, 4; GT is !LE\n"); + fprintf(vvp_out, " %%flag_inv 5;\n"); + return 5; + case 'G': + fprintf(vvp_out, " %%flag_inv 5; GE is !LT\n"); + return 5; case '<': return 5; case 'L': diff --git a/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index 4b54bdb50..6a78f58fd 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -139,7 +139,7 @@ long get_number_immediate(ivl_expr_t expr) assert(0); } if (ivl_expr_signed(expr) && bits[nbits-1]=='1' && - nbits < 8*sizeof(long)) imm |= -1L << nbits; + nbits < 8*sizeof(long)) imm |= -1UL << nbits; break; } diff --git a/tgt-vvp/eval_object.c b/tgt-vvp/eval_object.c index fb6240d17..7a307b150 100644 --- a/tgt-vvp/eval_object.c +++ b/tgt-vvp/eval_object.c @@ -82,6 +82,7 @@ static int eval_darray_new(ivl_expr_t ex) unsigned idx; switch (ivl_type_base(element_type)) { case IVL_VT_BOOL: + case IVL_VT_LOGIC: for (idx = 0 ; idx < ivl_expr_parms(init_expr) ; idx += 1) { draw_eval_vec4(ivl_expr_parm(init_expr,idx)); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); @@ -110,6 +111,11 @@ static int eval_darray_new(ivl_expr_t ex) errors += 1; break; } + } else if (init_expr && (ivl_expr_value(init_expr) == IVL_VT_DARRAY)) { + ivl_signal_t sig = ivl_expr_signal(init_expr); + fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); + fprintf(vvp_out, " %%scopy;\n"); + } else if (init_expr && number_is_immediate(size_expr,32,0)) { /* In this case, there is an init expression, the expression is NOT an array_pattern, and the size diff --git a/tgt-vvp/eval_real.c b/tgt-vvp/eval_real.c index 7a60bbe55..d7f1cfb4b 100644 --- a/tgt-vvp/eval_real.c +++ b/tgt-vvp/eval_real.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2017 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 @@ -57,6 +57,8 @@ static void draw_binary_real(ivl_expr_t expr) switch (ivl_expr_opcode(expr)) { case 'E': case 'N': + case 'w': + case 'W': case 'l': case 'r': case 'R': @@ -247,19 +249,7 @@ static void draw_real_logic_expr(ivl_expr_t expr) { draw_eval_vec4(expr); const char*sign_flag = ivl_expr_signed(expr)? "/s" : ""; - - if (ivl_expr_width(expr) > 64) { - fprintf(vvp_out, " %%cvt/rv%s;\n", sign_flag); - } else { - int res = allocate_word(); - fprintf(vvp_out, " %%ix/vec4%s %d;\n", sign_flag, res); - - if (ivl_expr_signed(expr)) - fprintf(vvp_out, " %%cvt/rs %d;\n", res); - else - fprintf(vvp_out, " %%cvt/ru %d;\n", res); - clr_word(res); - } + fprintf(vvp_out, " %%cvt/rv%s;\n", sign_flag); } static void draw_select_real(ivl_expr_t expr) @@ -309,6 +299,16 @@ static void draw_signal_real_real(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); + /* Special Case: If the signal is the return value of the function, + then use a different opcode to get the value. */ + if (signal_is_return_value(sig)) { + assert(ivl_signal_dimensions(sig) == 0); + fprintf(vvp_out, " %%retload/real 0; Load %s (draw_signal_real_real)\n", + ivl_signal_basename(sig)); + return; + } + + if (ivl_signal_dimensions(sig) == 0) { fprintf(vvp_out, " %%load/real v%p_0;\n", sig); return; @@ -535,13 +535,7 @@ void draw_eval_real(ivl_expr_t expr) if (ivl_expr_value(expr) == IVL_VT_VECTOR) { draw_eval_vec4(expr); const char*sign_flag = ivl_expr_signed(expr)? "/s" : ""; - - int res = allocate_word(); - - fprintf(vvp_out, " %%ix/vec4%s %d;\n", sign_flag, res); - fprintf(vvp_out, " %%cvt/rs %d;\n", res); - - clr_word(res); + fprintf(vvp_out, " %%cvt/rv%s;\n", sign_flag); } else { fprintf(stderr, "XXXX Evaluate real expression (%d)\n", diff --git a/tgt-vvp/eval_string.c b/tgt-vvp/eval_string.c index 92d702058..d3bc1362d 100644 --- a/tgt-vvp/eval_string.c +++ b/tgt-vvp/eval_string.c @@ -76,6 +76,15 @@ static void string_ex_signal(ivl_expr_t expr) return; } + /* Special Case: If the signal is the return value of the + function, then use a different opcode to get the value. */ + if (signal_is_return_value(sig)) { + assert(ivl_signal_dimensions(sig) == 0); + fprintf(vvp_out, " %%retload/str 0; Load %s (string_ex_signal)\n", + ivl_signal_basename(sig)); + return; + } + /* Simple case: This is a simple variable. Generate a load statement to load the string into the stack. */ if (ivl_signal_dimensions(sig) == 0) { @@ -163,6 +172,12 @@ static void string_ex_pop(ivl_expr_t expr) fprintf(vvp_out, " %%qpop/%s/str v%p_0;\n", fb, ivl_expr_signal(arg)); } +static void draw_sfunc_string(ivl_expr_t expr) +{ + assert(ivl_expr_value(expr) == IVL_VT_STRING); + draw_vpi_sfunc_call(expr); +} + void draw_eval_string(ivl_expr_t expr) { @@ -195,7 +210,7 @@ void draw_eval_string(ivl_expr_t expr) else if (strcmp(ivl_expr_name(expr), "$ivl_darray_method$pop_front")==0) string_ex_pop(expr); else - fallback_eval(expr); + draw_sfunc_string(expr); break; case IVL_EX_UFUNC: diff --git a/tgt-vvp/eval_vec4.c b/tgt-vvp/eval_vec4.c index e38f7238a..c4516029e 100644 --- a/tgt-vvp/eval_vec4.c +++ b/tgt-vvp/eval_vec4.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2013-2017 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 @@ -227,8 +227,8 @@ static void draw_binary_vec4_compare_real(ivl_expr_t expr) break; case 'n': /* != */ fprintf(vvp_out, " %%cmp/wr;\n"); + fprintf(vvp_out, " %%flag_inv 4;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); - fprintf(vvp_out, " %%inv;\n"); break; default: assert(0); @@ -247,8 +247,8 @@ static void draw_binary_vec4_compare_string(ivl_expr_t expr) break; case 'n': /* != */ fprintf(vvp_out, " %%cmp/str;\n"); + fprintf(vvp_out, " %%flag_inv 4;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); - fprintf(vvp_out, " %%inv;\n"); break; default: assert(0); @@ -298,9 +298,9 @@ static void draw_binary_vec4_compare_class(ivl_expr_t expr) fprintf(vvp_out, " %%test_nul/a v%p, %d;\n", sig, word_ix); clr_word(word_ix); } - fprintf(vvp_out, " %%flag_get/vec4 4;\n"); if (ivl_expr_opcode(expr) == 'n') - fprintf(vvp_out, " %%inv;\n"); + fprintf(vvp_out, " %%flag_inv 4;\n"); + fprintf(vvp_out, " %%flag_get/vec4 4;\n"); return; } @@ -320,9 +320,9 @@ static void draw_binary_vec4_compare_class(ivl_expr_t expr) fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%test_nul/prop %u, %d;\n", pidx, idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); - fprintf(vvp_out, " %%flag_get/vec4 4;\n"); if (ivl_expr_opcode(expr) == 'n') - fprintf(vvp_out, " %%inv;\n"); + fprintf(vvp_out, " %%flag_inv 4;\n"); + fprintf(vvp_out, " %%flag_get/vec4 4;\n"); if (idx != 0) clr_word(idx); return; @@ -385,18 +385,24 @@ static void draw_binary_vec4_compare(ivl_expr_t expr) fprintf(vvp_out, " %%flag_get/vec4 4;\n"); break; case 'n': /* != */ - fprintf(vvp_out, " %%cmp/e;\n"); + fprintf(vvp_out, " %%cmp/ne;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); - fprintf(vvp_out, " %%inv;\n"); break; case 'E': /* === */ fprintf(vvp_out, " %%cmp/e;\n"); fprintf(vvp_out, " %%flag_get/vec4 6;\n"); break; case 'N': /* !== */ - fprintf(vvp_out, " %%cmp/e;\n"); + fprintf(vvp_out, " %%cmp/ne;\n"); fprintf(vvp_out, " %%flag_get/vec4 6;\n"); - fprintf(vvp_out, " %%inv;\n"); + break; + case 'w': /* ==? */ + fprintf(vvp_out, " %%cmp/we;\n"); + fprintf(vvp_out, " %%flag_get/vec4 4;\n"); + break; + case 'W': /* !=? */ + fprintf(vvp_out, " %%cmp/wne;\n"); + fprintf(vvp_out, " %%flag_get/vec4 4;\n"); break; default: assert(0); @@ -415,7 +421,7 @@ static void draw_binary_vec4_land(ivl_expr_t expr) fprintf(vvp_out, " %%or/r;\n"); /* Now push the right expression. Again, reduce to a single - bit if necessasry. */ + bit if necessary. */ draw_eval_vec4(re); if (ivl_expr_width(re) > 1) fprintf(vvp_out, " %%or/r;\n"); @@ -615,7 +621,7 @@ static void draw_binary_vec4_lor(ivl_expr_t expr) fprintf(vvp_out, " %%or/r;\n"); /* Now push the right expression. Again, reduce to a single - bit if necessasry. */ + bit if necessary. */ draw_eval_vec4(re); if (ivl_expr_width(re) > 1) fprintf(vvp_out, " %%or/r;\n"); @@ -689,8 +695,10 @@ static void draw_binary_vec4(ivl_expr_t expr) case 'e': /* == */ case 'E': /* === */ - case 'n': /* !== */ + case 'n': /* != */ case 'N': /* !== */ + case 'w': /* ==? */ + case 'W': /* !=? */ draw_binary_vec4_compare(expr); break; @@ -952,7 +960,7 @@ static void draw_select_pad_vec4(ivl_expr_t expr) } /* - * This function handles the speical case of a call to the internal + * This function handles the special case of a call to the internal * functions $ivl_darray_method$pop_back et al. The first (and only) * argument is the signal that represents a dynamic queue. Generate a * %qpop instruction to pop a value and push it to the vec4 stack. @@ -1005,6 +1013,15 @@ static void draw_signal_vec4(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); + /* Special Case: If the signal is the return value of the function, + then use a different opcode to get the value. */ + if (signal_is_return_value(sig)) { + assert(ivl_signal_dimensions(sig) == 0); + fprintf(vvp_out, " %%retload/vec4 0; Load %s (draw_signal_vec4)\n", + ivl_signal_basename(sig)); + return; + } + /* Handle the simple case, a signal expression that is a simple vector, no array dimensions. */ if (ivl_signal_dimensions(sig) == 0) { @@ -1032,7 +1049,7 @@ static void draw_string_vec4(ivl_expr_t expr) for (unsigned idx = 0 ; idx < wid ; idx += 8) { tmp <<= 8; - tmp |= *p; + tmp |= (unsigned long)*p; p += 1; tmp_wid += 8; if (tmp_wid == 32) { diff --git a/tgt-vvp/modpath.c b/tgt-vvp/modpath.c index 4c904496d..bdaf72e93 100644 --- a/tgt-vvp/modpath.c +++ b/tgt-vvp/modpath.c @@ -23,10 +23,6 @@ # include # include "ivl_alloc.h" -#ifdef __MINGW32__ /* MinGW has inconsistent %p output. */ -#define snprintf _snprintf -#endif - static ivl_signal_t find_path_source_port(ivl_delaypath_t path) { unsigned idx; diff --git a/tgt-vvp/stmt_assign.c b/tgt-vvp/stmt_assign.c index e2456ae25..f10291e20 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 @@ -22,11 +22,6 @@ # include # include -#ifdef __MINGW32__ /* MinGW has inconsistent %p output. */ -#define snprintf _snprintf -#endif - - /* * These functions handle the blocking assignment. Use the %set * instruction to perform the actual assignment, and calculate any @@ -116,7 +111,12 @@ static void get_vec_from_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice slice->type = SLICE_SIMPLE_VECTOR; slice->u_.simple_vector.use_word = use_word; - fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); + if (signal_is_return_value(sig)) { + assert(use_word==0); + fprintf(vvp_out, " %%retload/vec4 0;\n"); + } else { + fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); + } } else if (ivl_signal_dimensions(sig)==0 && part_off_ex==0 && word_ix==0) { @@ -125,7 +125,12 @@ static void get_vec_from_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice slice->type = SLICE_PART_SELECT_STATIC; slice->u_.part_select_static.part_off = part_off; - fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); + if (signal_is_return_value(sig)) { + assert(use_word==0); + fprintf(vvp_out, " %%retload/vec4 0;\n"); + } else { + fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); + } fprintf(vvp_out, " %%pushi/vec4 %lu, 0, 32;\n", part_off); fprintf(vvp_out, " %%part/u %u;\n", wid); @@ -133,6 +138,7 @@ static void get_vec_from_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice assert(use_word == 0); assert(part_off == 0); + assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED slice->type = SLICE_PART_SELECT_DYNAMIC; @@ -148,6 +154,8 @@ static void get_vec_from_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice } else if (ivl_signal_dimensions(sig) > 0 && word_ix == 0) { + assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED + slice->type = SLICE_MEMORY_WORD_STATIC; slice->u_.memory_word_static.use_word = use_word; if (use_word < ivl_signal_array_count(sig)) { @@ -161,6 +169,7 @@ static void get_vec_from_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice } else if (ivl_signal_dimensions(sig) > 0 && word_ix != 0) { + assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED slice->type = SLICE_MEMORY_WORD_DYNAMIC; slice->u_.memory_word_dynamic.word_idx_reg = allocate_word(); @@ -188,17 +197,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"); } @@ -207,6 +216,49 @@ static void get_vec_from_lval(ivl_statement_t net, struct vec_slice_info*slices) } +static void put_vec_to_ret_slice(ivl_signal_t sig, struct vec_slice_info*slice, + unsigned wid) +{ + int part_off_idx; + + /* If the slice of the l-value is a BOOL variable, then cast + the data to a BOOL vector so that the stores can be valid. */ + if (ivl_signal_data_type(sig) == IVL_VT_BOOL) { + fprintf(vvp_out, " %%cast2;\n"); + } + + switch (slice->type) { + default: + fprintf(vvp_out, " ; XXXX slice->type=%d\n", slice->type); + assert(0); + break; + + case SLICE_SIMPLE_VECTOR: + assert(slice->u_.simple_vector.use_word == 0); + fprintf(vvp_out, " %%ret/vec4 0, 0, %u;\n", wid); + break; + + case SLICE_PART_SELECT_STATIC: + part_off_idx = allocate_word(); + fprintf(vvp_out, " %%ix/load %d, %lu, 0;\n", + part_off_idx, slice->u_.part_select_static.part_off); + fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); + fprintf(vvp_out, " %%ret/vec4 0, %d, %u;\n", part_off_idx, wid); + clr_word(part_off_idx); + break; + + case SLICE_PART_SELECT_DYNAMIC: + fprintf(vvp_out, " %%flag_mov 4, %u;\n", + slice->u_.part_select_dynamic.x_flag); + fprintf(vvp_out, " %%ret/vec4 0, %d, %u;\n", + slice->u_.part_select_dynamic.word_idx_reg, wid); + clr_word(slice->u_.part_select_dynamic.word_idx_reg); + clr_flag(slice->u_.part_select_dynamic.x_flag); + break; + + } +} + static void put_vec_to_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice, unsigned wid) { @@ -214,6 +266,15 @@ static void put_vec_to_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice, ivl_signal_t sig = ivl_lval_sig(lval); int part_off_idx; + + /* Special Case: If the l-value signal is named after its scope, + and the scope is a function, then this is an assign to a return + value and should be handled differently. */ + if (signal_is_return_value(sig)) { + put_vec_to_ret_slice(sig, slice, wid); + return; + } + /* If the slice of the l-value is a BOOL variable, then cast the data to a BOOL vector so that the stores can be valid. */ if (ivl_signal_data_type(sig) == IVL_VT_BOOL) { @@ -380,7 +441,10 @@ static void store_vec4_to_lval(ivl_statement_t net) draw_eval_expr_into_integer(part_off_ex, offset_index); /* Note that flag4 is set by the eval above. */ assert(lsig); - if (ivl_signal_type(lsig)==IVL_SIT_UWIRE) { + if (signal_is_return_value(lsig)) { + fprintf(vvp_out, " %%ret/vec4 0, %d, %u; Assign to %s (store_vec4_to_lval)\n", + offset_index, lwid, ivl_signal_basename(lsig)); + } else if (ivl_signal_type(lsig)==IVL_SIT_UWIRE) { fprintf(vvp_out, " %%force/vec4/off v%p_0, %d;\n", lsig, offset_index); } else { @@ -395,6 +459,7 @@ static void store_vec4_to_lval(ivl_statement_t net) member. We will use a property assign function. */ assert(!lsig); + ivl_type_t sub_type = draw_lval_expr(nest); assert(ivl_type_base(sub_type) == IVL_VT_CLASS); fprintf(vvp_out, " %%store/prop/v %d, %u;\n", @@ -405,7 +470,13 @@ static void store_vec4_to_lval(ivl_statement_t net) /* No offset expression, so use simpler store function. */ assert(lsig); assert(lwid == ivl_signal_width(lsig)); - fprintf(vvp_out, " %%store/vec4 v%p_0, 0, %u;\n", lsig, lwid); + if (signal_is_return_value(lsig)) { + fprintf(vvp_out, " %%ret/vec4 0, 0, %u; Assign to %s (store_vec4_to_lval)\n", + lwid, ivl_signal_basename(lsig)); + } else { + fprintf(vvp_out, " %%store/vec4 v%p_0, 0, %u;\n", + lsig, lwid); + } } } } @@ -422,6 +493,7 @@ static int show_stmt_assign_vector(ivl_statement_t net) of the l-value. We need these values as part of the r-value calculation. */ if (ivl_stmt_opcode(net) != 0) { + fprintf(vvp_out, " ; show_stmt_assign_vector: Get l-value for compressed %c= operand\n", ivl_stmt_opcode(net)); slices = calloc(ivl_stmt_lvals(net), sizeof(struct vec_slice_info)); get_vec_from_lval(net, slices); } @@ -560,28 +632,159 @@ 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; + if (signal_is_return_value(sig)) { + assert(use_word==0); + fprintf(vvp_out, " %%retload/real 0;\n"); + } else { + fprintf(vvp_out, " %%load/real v%p_%lu;\n", sig, use_word); + } + + } else if (ivl_signal_dimensions(sig) > 0 && word_ix == 0) { + + assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED + + 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) { + + assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED + 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); + + /* Special Case: If the l-value signal is named after its scope, + and the scope is a function, then this is an assign to a return + value and should be handled differently. */ + if (signal_is_return_value(sig)) { + assert(slice->u_.simple_word.use_word == 0); + fprintf(vvp_out, " %%ret/real 0;\n"); + return; + } + + 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); + /* Special Case: If the l-value signal is named after its scope, + and the scope is a function, then this is an assign to a return + value and should be handled differently. */ + ivl_scope_t sig_scope = ivl_signal_scope(var); + if ((ivl_scope_type(sig_scope) == IVL_SCT_FUNCTION) + && (strcmp(ivl_signal_basename(var), ivl_scope_basename(sig_scope)) == 0)) { + assert(ivl_signal_dimensions(var) == 0); + fprintf(vvp_out, " %%ret/real 0; Assign to %s\n", + ivl_signal_basename(var)); + return; + } + 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. @@ -617,7 +820,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; } @@ -632,6 +894,19 @@ static int show_stmt_assign_sig_string(ivl_statement_t net) assert(ivl_stmt_lvals(net) == 1); assert(ivl_stmt_opcode(net) == 0); + /* Special case: If the l-value signal (string) is named after + its scope, and the scope is a function, then this is an + assign to a return value and should be handled + differently. */ + if (signal_is_return_value(var)) { + assert(ivl_signal_dimensions(var) == 0); + assert(part == 0 && aidx == 0); + draw_eval_string(rval); + fprintf(vvp_out, " %%ret/str 0; Assign to %s\n", + ivl_signal_basename(var)); + return 0; + } + /* Simplest case: no mux. Evaluate the r-value as a string and store the result into the variable. Note that the %store/str opcode pops the string result. */ diff --git a/tgt-vvp/vvp.c b/tgt-vvp/vvp.c index 9eb5003ba..004adaddf 100644 --- a/tgt-vvp/vvp.c +++ b/tgt-vvp/vvp.c @@ -28,7 +28,7 @@ static const char*version_string = "Icarus Verilog VVP Code Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com)\n\n" +"Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" diff --git a/tgt-vvp/vvp_priv.h b/tgt-vvp/vvp_priv.h index 7f0fbac77..c4e725bd9 100644 --- a/tgt-vvp/vvp_priv.h +++ b/tgt-vvp/vvp_priv.h @@ -23,11 +23,6 @@ # include "ivl_target.h" # include -#ifdef __MINGW32__ /* MinGW has inconsistent %p output. */ -#define snprintf _snprintf -#endif - - extern int debug_draw; /* @@ -138,6 +133,7 @@ extern void draw_vpi_task_call(ivl_statement_t net); extern void draw_vpi_func_call(ivl_expr_t expr); extern void draw_vpi_rfunc_call(ivl_expr_t expr); +extern void draw_vpi_sfunc_call(ivl_expr_t expr); extern void draw_class_in_scope(ivl_type_t classtype); @@ -213,6 +209,10 @@ extern void draw_eval_expr_into_integer(ivl_expr_t expr, unsigned ix); */ extern int draw_eval_condition(ivl_expr_t expr); +/* + * Return true if the signal is the return value of a function. + */ +extern int signal_is_return_value(ivl_signal_t sig); extern int number_is_unknown(ivl_expr_t ex); extern int number_is_immediate(ivl_expr_t ex, unsigned lim_wid, int negative_is_ok); @@ -242,13 +242,13 @@ extern void draw_eval_real(ivl_expr_t ex); extern int draw_eval_bool64(ivl_expr_t ex); /* - * The draw_eval_string functio evaluates the expression as a string, + * The draw_eval_string function evaluates the expression as a string, * and pushes the string onto the string stack. */ extern void draw_eval_string(ivl_expr_t ex); /* - * The draw_eval_string functio evaluates the expression as an object, + * The draw_eval_string function evaluates the expression as an object, * and pushes the object onto the object stack. */ extern int draw_eval_object(ivl_expr_t ex); @@ -261,6 +261,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 ffa07493c..a8d2f1dfa 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-2017 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 @@ -23,10 +23,6 @@ # include # include -#ifdef __MINGW32__ /* MinGW has inconsistent %p output. */ -#define snprintf _snprintf -#endif - static int show_statement(ivl_statement_t net, ivl_scope_t sscope); unsigned local_count = 0; @@ -229,6 +225,10 @@ static void assign_to_lvector(ivl_lval_t lval, const unsigned long use_word = 0; + const char*assign_op = "%assign"; + if (ivl_signal_type(sig) == IVL_SIT_UWIRE) + assign_op = "%force"; + // 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. @@ -267,8 +267,8 @@ static void assign_to_lvector(ivl_lval_t lval, to know to skip the assign. */ draw_eval_expr_into_integer(part_off_ex, offset_index); /* If the index expression has XZ bits, skip the assign. */ - fprintf(vvp_out, " %%assign/vec4/off/d v%p_%lu, %d, %d;\n", - sig, use_word, offset_index, delay_index); + fprintf(vvp_out, " %s/vec4/off/d v%p_%lu, %d, %d;\n", + assign_op, sig, use_word, offset_index, delay_index); clr_word(offset_index); clr_word(delay_index); @@ -277,8 +277,8 @@ static void assign_to_lvector(ivl_lval_t lval, int offset_index = allocate_word(); /* Event control delay... */ draw_eval_expr_into_integer(part_off_ex, offset_index); - fprintf(vvp_out, " %%assign/vec4/off/e v%p_%lu, %d;\n", - sig, use_word, offset_index); + fprintf(vvp_out, " %s/vec4/off/e v%p_%lu, %d;\n", + assign_op, sig, use_word, offset_index); fprintf(vvp_out, " %%evctl/c;\n"); clr_word(offset_index); @@ -296,8 +296,8 @@ static void assign_to_lvector(ivl_lval_t lval, to know to skip the assign. */ draw_eval_expr_into_integer(part_off_ex, offset_index); /* If the index expression has XZ bits, skip the assign. */ - fprintf(vvp_out, " %%assign/vec4/off/d v%p_%lu, %d, %d;\n", - sig, use_word, offset_index, delay_index); + fprintf(vvp_out, " %s/vec4/off/d v%p_%lu, %d, %d;\n", + assign_op, sig, use_word, offset_index, delay_index); clr_word(offset_index); clr_word(delay_index); } @@ -310,8 +310,8 @@ static void assign_to_lvector(ivl_lval_t lval, fprintf(vvp_out, " %%ix/load %d, %lu, 0;\n", offset_index, part_off); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); - fprintf(vvp_out, " %%assign/vec4/off/e v%p_%lu, %d;\n", - sig, use_word, offset_index); + fprintf(vvp_out, " %s/vec4/off/e v%p_%lu, %d;\n", + assign_op, sig, use_word, offset_index); fprintf(vvp_out, " %%evctl/c;\n"); clr_word(offset_index); @@ -329,8 +329,8 @@ static void assign_to_lvector(ivl_lval_t lval, delay_index, low_d, hig_d); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); } - fprintf(vvp_out, " %%assign/vec4/off/d v%p_%lu, %d, %d;\n", - sig, use_word, offset_index, delay_index); + fprintf(vvp_out, " %s/vec4/off/d v%p_%lu, %d, %d;\n", + assign_op, sig, use_word, offset_index, delay_index); clr_word(offset_index); clr_word(delay_index); } @@ -339,13 +339,13 @@ static void assign_to_lvector(ivl_lval_t lval, /* Calculated delay... */ int delay_index = allocate_word(); draw_eval_expr_into_integer(dexp, delay_index); - fprintf(vvp_out, " %%assign/vec4/d v%p_%lu, %d;\n", - sig, use_word, delay_index); + fprintf(vvp_out, " %s/vec4/d v%p_%lu, %d;\n", + assign_op, sig, use_word, delay_index); clr_word(delay_index); } else if (nevents != 0) { /* Event control delay... */ - fprintf(vvp_out, " %%assign/vec4/e v%p_%lu;\n", - sig, use_word); + fprintf(vvp_out, " %s/vec4/e v%p_%lu;\n", + assign_op, sig, use_word); fprintf(vvp_out, " %%evctl/c;\n"); } else { @@ -357,12 +357,12 @@ static void assign_to_lvector(ivl_lval_t lval, int delay_index = allocate_word(); fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n", delay_index, low_d, hig_d); - fprintf(vvp_out, " %%assign/vec4/d v%p_%lu, %d;\n", - sig, use_word, delay_index); + fprintf(vvp_out, " %s/vec4/d v%p_%lu, %d;\n", + assign_op, sig, use_word, delay_index); clr_word(delay_index); } else { - fprintf(vvp_out, " %%assign/vec4 v%p_%lu, %lu;\n", - sig, use_word, low_d); + fprintf(vvp_out, " %s/vec4 v%p_%lu, %lu;\n", + assign_op, sig, use_word, low_d); } } } @@ -675,7 +675,7 @@ static int show_stmt_case(ivl_statement_t net, ivl_scope_t sscope) /* Duplicate the case expression so that the cmp instructions below do not completely erase the - value. Do this in fromt of each compare. */ + value. Do this in front of each compare. */ fprintf(vvp_out, " %%dup/vec4;\n"); draw_eval_vec4(cex); @@ -771,7 +771,7 @@ static int show_stmt_case_r(ivl_statement_t net, ivl_scope_t sscope) continue; } - /* The referene value... */ + /* The reference value... */ fprintf(vvp_out, " %%dup/real;\n"); /* The guard value... */ draw_eval_real(cex); @@ -1625,16 +1625,25 @@ static int show_stmt_utask(ivl_statement_t net) show_stmt_file_line(net, "User task call."); - fprintf(vvp_out, " %%fork TD_%s", - vvp_mangle_id(ivl_scope_name(task))); - fprintf(vvp_out, ", S_%p;\n", task); - fprintf(vvp_out, " %%join;\n"); + if (ivl_scope_type(task) == IVL_SCT_FUNCTION) { + // A function called as a task is (presumably) a void function. + // Use the %callf/void instruction to call it. + fprintf(vvp_out, " %%callf/void TD_%s", + vvp_mangle_id(ivl_scope_name(task))); + fprintf(vvp_out, ", S_%p;\n", task); + } else { + fprintf(vvp_out, " %%fork TD_%s", + vvp_mangle_id(ivl_scope_name(task))); + fprintf(vvp_out, ", S_%p;\n", task); + fprintf(vvp_out, " %%join;\n"); + } return 0; } static int show_stmt_wait(ivl_statement_t net, ivl_scope_t sscope) { + static unsigned int cascade_counter = 0; /* Look to see if this is a SystemVerilog wait fork. */ if ((ivl_stmt_nevent(net) == 1) && (ivl_stmt_events(net, 0) == 0)) { assert(ivl_statement_type(ivl_stmt_sub_stmt(net)) == IVL_ST_NOOP); @@ -1647,11 +1656,17 @@ static int show_stmt_wait(ivl_statement_t net, ivl_scope_t sscope) if (ivl_stmt_nevent(net) == 1) { ivl_event_t ev = ivl_stmt_events(net, 0); - fprintf(vvp_out, " %%wait E_%p;\n", ev); + if (ivl_stmt_needs_t0_trigger(net)) { + fprintf(vvp_out, "Ewait_%u .event/or E_%p, E_0x0;\n", + cascade_counter, ev); + fprintf(vvp_out, " %%wait Ewait_%u;\n", cascade_counter); + cascade_counter += 1; + } else { + fprintf(vvp_out, " %%wait E_%p;\n", ev); + } } else { unsigned idx; - static unsigned int cascade_counter = 0; ivl_event_t ev = ivl_stmt_events(net, 0); fprintf(vvp_out, "Ewait_%u .event/or E_%p", cascade_counter, ev); @@ -1659,6 +1674,7 @@ static int show_stmt_wait(ivl_statement_t net, ivl_scope_t sscope) ev = ivl_stmt_events(net, idx); fprintf(vvp_out, ", E_%p", ev); } + assert(ivl_stmt_needs_t0_trigger(net) == 0); fprintf(vvp_out, ";\n %%wait Ewait_%u;\n", cascade_counter); cascade_counter += 1; } @@ -2302,6 +2318,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. */ @@ -2310,6 +2327,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; @@ -2325,7 +2348,7 @@ int draw_process(ivl_process_t net, void*x) fprintf(vvp_out, " .scope S_%p;\n", scope); /* Generate the entry label. Just give the thread a number so - that we ar certain the label is unique. */ + that we are certain the label is unique. */ fprintf(vvp_out, "T_%u ;\n", thread_count); /* Draw the contents of the thread. */ @@ -2343,6 +2366,9 @@ int draw_process(ivl_process_t net, void*x) break; case IVL_PR_ALWAYS: + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: fprintf(vvp_out, " %%jmp T_%u;\n", thread_count); break; } @@ -2353,7 +2379,12 @@ int draw_process(ivl_process_t net, void*x) case IVL_PR_INITIAL: case IVL_PR_ALWAYS: - if (push_flag) { + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: + 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 44d5b1e53..3177e7976 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-2018 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 @@ -290,6 +290,16 @@ static unsigned is_netlist_signal(ivl_net_logic_t net, ivl_nexus_t nex) return rtn; } +int signal_is_return_value(ivl_signal_t sig) +{ + ivl_scope_t sig_scope = ivl_signal_scope(sig); + if (ivl_scope_type(sig_scope) != IVL_SCT_FUNCTION) + return 0; + if (strcmp(ivl_signal_basename(sig), ivl_scope_basename(sig_scope)) == 0) + return 1; + return 0; +} + /* * This tests a bufz device against an output receiver, and determines * if the device can be skipped. If this function returns false, then a @@ -449,6 +459,33 @@ static void draw_reg_in_scope(ivl_signal_t sig) break; } + /* Special Case: If this variable is the return value of a function, + then it need to exist as an actual variable. */ + if ((ivl_signal_data_type(sig)==IVL_VT_REAL) + && signal_is_return_value(sig)) { + fprintf(vvp_out, "; Variable %s is REAL return value of scope S_%p\n", + ivl_signal_basename(sig), ivl_signal_scope(sig)); + return; + } + if ((ivl_signal_data_type(sig)==IVL_VT_STRING) + && signal_is_return_value(sig)) { + fprintf(vvp_out, "; Variable %s is string return value of scope S_%p\n", + ivl_signal_basename(sig), ivl_signal_scope(sig)); + return; + } + if ((ivl_signal_data_type(sig)==IVL_VT_LOGIC) + && signal_is_return_value(sig)) { + fprintf(vvp_out, "; Variable %s is vec4 return value of scope S_%p\n", + ivl_signal_basename(sig), ivl_signal_scope(sig)); + return; + } + if ((ivl_signal_data_type(sig)==IVL_VT_BOOL) + && signal_is_return_value(sig)) { + fprintf(vvp_out, "; Variable %s is bool return value of scope S_%p\n", + ivl_signal_basename(sig), ivl_signal_scope(sig)); + return; + } + const char *datatype_flag = ivl_signal_integer(sig) ? "/i" : ivl_signal_signed(sig)? "/s" : ""; const char *local_flag = local_flag_str(sig); @@ -669,7 +706,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 +754,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 +767,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) @@ -865,6 +860,7 @@ static void draw_udp_in_scope(ivl_net_logic_t lptr) * (.resolv, etc.) can be built before we build the .udp call. * This matches what is done for the other primitives. */ + assert(ivl_logic_pins(lptr) > 0); ninp = ivl_logic_pins(lptr) - 1; input_strings = calloc(ninp, sizeof(char*)); for (pdx = 0 ; pdx < ninp ; pdx += 1) { @@ -908,7 +904,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) @@ -927,7 +923,7 @@ static void draw_logic_in_scope(ivl_net_logic_t lptr) ivl_drive_t str1 = ivl_logic_drive1(lptr); int level; - int ninp; + unsigned ninp; const char **input_strings; switch (ivl_logic_type(lptr)) { @@ -1060,8 +1056,8 @@ static void draw_logic_in_scope(ivl_net_logic_t lptr) /* Get all the input label that I will use for parameters to the functor that I create later. */ + assert(ivl_logic_pins(lptr) > 0); ninp = ivl_logic_pins(lptr) - 1; - assert(ninp >= 0); input_strings = calloc(ninp, sizeof(char*)); for (pdx = 0 ; pdx < (unsigned)ninp ; pdx += 1) input_strings[pdx] = draw_net_input(ivl_logic_pin(lptr, pdx+1)); @@ -1084,7 +1080,7 @@ static void draw_logic_in_scope(ivl_net_logic_t lptr) } for (pdx = inst; pdx < (unsigned)ninp && pdx < inst+4 ; pdx += 1) { if (level) { - fprintf(vvp_out, ", L_%p/%d/%d", + fprintf(vvp_out, ", L_%p/%d/%u", lptr, level - 1, pdx*4); } else { fprintf(vvp_out, ", %s", input_strings[pdx]); @@ -1112,7 +1108,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 +1308,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; @@ -1538,6 +1522,16 @@ static void draw_lpm_cmp(ivl_lpm_t net) type = "nee"; signed_string = ""; break; + case IVL_LPM_CMP_WEQ: + assert(dtc != IVL_VT_REAL); /* Should never get here! */ + type = "weq"; + signed_string = ""; + break; + case IVL_LPM_CMP_WNE: + assert(dtc != IVL_VT_REAL); /* Should never get here! */ + type = "wne"; + signed_string = ""; + break; default: assert(0); } @@ -1716,69 +1710,108 @@ static void draw_lpm_concat(ivl_lpm_t net) /* * Emit a DFF primitive. This uses the following syntax: * - * .dff , , , ; - * - * The async pin currently sets the stored data value and propagates it - * to the output (not very useful). This routine always sets the async - * value to high-Z which is ignored in the VVP code. This is all OK - * since synthesis is not currently functional. + * .dff , , [, [, ]]; */ static void draw_lpm_ff(ivl_lpm_t net) { ivl_nexus_t nex; - /* Sync/Async set/clear control is currently only supported in V0.8 - * which has working synthesis. If/when this is added see that code - * for clues about how this should be implemented. The dff primitive - * used here (from vvp) needs to be improved to support both an - * async set and clear. See the UDP generated by the tgt-vlog95 code - * generator in V0.10 and later for how this might be done. */ + /* Sync set/clear control is not currently supported. This is not + * a problem, as synthesis can incorporate this in the D input + * expression. All modern synthesis tools do this as a matter of + * course, as most cell libraries don't contain flip-flops with + * sync set/clear. + */ assert(ivl_lpm_sync_clr(net) == 0); assert(ivl_lpm_sync_set(net) == 0); + unsigned width = ivl_lpm_width(net); + char*edge = ivl_lpm_negedge(net) ? "n" : "p"; if (ivl_lpm_async_clr(net)) { + /* Synthesis doesn't currently support both set and clear. + If it ever does, it might be better to implement the + flip-flop as a UDP. See tgt-vlog95 for an example of + how to do this. */ if (ivl_lpm_async_set(net)) { - fprintf(stderr, "%s:%u:vvp.tgt: sorry: No support for a D-ff " + fprintf(stderr, "%s:%u:vvp.tgt: sorry: No support for a DFF " "with both an async. set and clear.\n", ivl_lpm_file(net), ivl_lpm_lineno(net)); vvp_errors += 1; } - fprintf(vvp_out, "L_%p .dff/aclr ", net); + fprintf(vvp_out, "L_%p .dff/%s/aclr %u ", net, edge, width); } else if (ivl_lpm_async_set(net)) { - fprintf(vvp_out, "L_%p .dff/aset ", net); + fprintf(vvp_out, "L_%p .dff/%s/aset %u ", net, edge, width); } else { - fprintf(vvp_out, "L_%p .dff ", net); + fprintf(vvp_out, "L_%p .dff/%s %u ", net, edge, width); } nex = ivl_lpm_data(net,0); assert(nex); fprintf(vvp_out, "%s", draw_net_input(nex)); - assert(width_of_nexus(nex) == ivl_lpm_width(net));; + assert(width_of_nexus(nex) == width); nex = ivl_lpm_clk(net); assert(nex); - assert(width_of_nexus(nex) == 1);; + assert(width_of_nexus(nex) == 1); fprintf(vvp_out, ", %s", draw_net_input(nex)); nex = ivl_lpm_enable(net); if (nex) { - assert(width_of_nexus(nex) == 1);; + assert(width_of_nexus(nex) == 1); fprintf(vvp_out, ", %s", draw_net_input(nex)); } else { fprintf(vvp_out, ", C4<1>"); } if ( (nex = ivl_lpm_async_clr(net)) ) { + assert(width_of_nexus(nex) == 1); fprintf(vvp_out, ", %s", draw_net_input(nex)); } if ( (nex = ivl_lpm_async_set(net)) ) { + ivl_expr_t val = ivl_lpm_aset_value(net); + assert(width_of_nexus(nex) == 1); fprintf(vvp_out, ", %s", draw_net_input(nex)); + if (val) { + unsigned nbits = ivl_expr_width(val); + const char*bits = ivl_expr_bits(val); + unsigned bb; + assert(nbits == width); + fprintf(vvp_out, ", C4<"); + for (bb = 0 ; bb < nbits; bb += 1) + fprintf(vvp_out, "%c", bits[nbits-bb-1]); + fprintf(vvp_out, ">"); + } } fprintf(vvp_out, ";\n"); } +/* + * Emit a LATCH primitive. This uses the following syntax: + * + * .latch , ; + */ +static void draw_lpm_latch(ivl_lpm_t net) +{ + ivl_nexus_t nex; + + unsigned width = ivl_lpm_width(net); + fprintf(vvp_out, "L_%p .latch %u ", net, width); + + nex = ivl_lpm_data(net,0); + assert(nex); + fprintf(vvp_out, "%s", draw_net_input(nex)); + assert(width_of_nexus(nex) == width); + + nex = ivl_lpm_enable(net); + assert(nex); + assert(width_of_nexus(nex) == 1); + fprintf(vvp_out, ", %s", draw_net_input(nex)); + + fprintf(vvp_out, ";\n"); +} + static void draw_lpm_shiftl(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); @@ -1918,6 +1951,19 @@ static void draw_lpm_ufunc(ivl_lpm_t net) ivl_variable_type_t dt = data_type_of_nexus(ivl_lpm_q(net)); const char*dly = draw_lpm_output_delay(net, dt); + const char*type_string = ""; + switch (dt) { + case IVL_VT_REAL: + type_string = "/real"; + break; + case IVL_VT_BOOL: + case IVL_VT_LOGIC: + type_string = "/vec4"; + break; + default: + break; + } + /* Get all the input labels that I will use for net signals that connect to the inputs of the function. */ ninp = ivl_lpm_size(net); @@ -1930,7 +1976,7 @@ static void draw_lpm_ufunc(ivl_lpm_t net) vvp_mangle_id(ivl_scope_name(def)), ivl_lpm_width(net), ivl_lpm_trigger(net)); else - fprintf(vvp_out, "L_%p%s .ufunc TD_%s, %u", net, dly, + fprintf(vvp_out, "L_%p%s .ufunc%s TD_%s, %u", net, dly, type_string, vvp_mangle_id(ivl_scope_name(def)), ivl_lpm_width(net)); @@ -1958,7 +2004,7 @@ static void draw_lpm_ufunc(ivl_lpm_t net) } fprintf(vvp_out, ")"); - +#if 0 /* Now print the reference to the signal from which the result is collected. */ { ivl_signal_t psig = ivl_scope_port(def, 0); @@ -1967,7 +2013,7 @@ static void draw_lpm_ufunc(ivl_lpm_t net) fprintf(vvp_out, " v%p_0", psig); } - +#endif /* Finally, print the scope identifier. */ fprintf(vvp_out, " S_%p;\n", def); } @@ -2096,6 +2142,10 @@ static void draw_lpm_in_scope(ivl_lpm_t net) draw_lpm_ff(net); return; + case IVL_LPM_LATCH: + draw_lpm_latch(net); + return; + case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_EQX: @@ -2104,6 +2154,8 @@ static void draw_lpm_in_scope(ivl_lpm_t net) case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: draw_lpm_cmp(net); return; @@ -2181,6 +2233,9 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent) const char *type; const char*prefix = ivl_scope_is_auto(net) ? "auto" : ""; + char suffix[32]; + + suffix[0] = 0; switch (ivl_scope_type(net)) { case IVL_SCT_MODULE: type = "module"; break; @@ -2194,8 +2249,36 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent) default: type = "?"; assert(0); } - fprintf(vvp_out, "S_%p .scope %s%s, \"%s\" \"%s\" %u %u", - net, prefix, type, + if (ivl_scope_type(net)==IVL_SCT_FUNCTION) { + switch (ivl_scope_func_type(net)) { + case IVL_VT_LOGIC: + case IVL_VT_BOOL: + snprintf(suffix, sizeof suffix, ".vec4.%c%u", + ivl_scope_func_signed(net)? 'u' : 's', + ivl_scope_func_width(net)); + break; + case IVL_VT_REAL: + snprintf(suffix, sizeof suffix, ".real"); + break; + case IVL_VT_STRING: + snprintf(suffix, sizeof suffix, ".str"); + break; + case IVL_VT_CLASS: + case IVL_VT_DARRAY: + case IVL_VT_QUEUE: + snprintf(suffix, sizeof suffix, ".obj"); + break; + case IVL_VT_VOID: + snprintf(suffix, sizeof suffix, ".void"); + break; + default: + assert(0); + break; + } + } + + fprintf(vvp_out, "S_%p .scope %s%s%s, \"%s\" \"%s\" %u %u", + net, prefix, type, suffix, vvp_mangle_name(ivl_scope_basename(net)), vvp_mangle_name(ivl_scope_tname(net)), ivl_file_table_index(ivl_scope_file(net)), @@ -2222,7 +2305,7 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent) unsigned width = ivl_scope_mod_module_port_width(net,idx); if( name == 0 ) name = ""; - fprintf( vvp_out, " .port_info %u %s %u \"%s\"\n", + fprintf( vvp_out, " .port_info %u %s %u \"%s\";\n", idx, vvp_port_info_type_str(ptype), width, vvp_mangle_name(name) ); } diff --git a/verilog.spec b/verilog.spec index b3095447e..97a0e3c1a 100644 --- a/verilog.spec +++ b/verilog.spec @@ -1,6 +1,6 @@ #norootforbuild # -%define rev_date 20150105 +%define rev_date 20150815 # Normally, the suff-ix is %nil, meaning the suffix is to not be used. # But if the builder wants to make a suffixed package, he may set this # to a value (i.e. -test) to cause suffixes to be put in all the right @@ -10,7 +10,7 @@ # Summary: Icarus Verilog Name: verilog%{suff} -Version: 0.10.0.%{rev_date} +Version: 11.0.%{rev_date} Release: 0 License: GPL Group: Productivity/Scientific/Electronics @@ -102,6 +102,8 @@ rm -rf $RPM_BUILD_ROOT %attr(-,root,root) %{_libdir}/ivl%{suff}/v2009.vpi %attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl_sys.sft %attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl_sys.vpi +%attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl_textio.sft +%attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl_textio.vpi %attr(-,root,root) %{_libdir}/ivl%{suff}/vpi_debug.vpi %attr(-,root,root) %{_libdir}/ivl%{suff}/cadpli.vpl %attr(-,root,root) %{_libdir}/libvpi%{suff}.a diff --git a/verinum.cc b/verinum.cc index c5f4be65e..7b466053b 100644 --- a/verinum.cc +++ b/verinum.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2018 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 @@ -22,6 +22,7 @@ # include "verinum.h" # include # include +# include # include // Needed to get pow for as_double(). # include // Needed to get snprintf for as_string(). # include @@ -1444,7 +1445,8 @@ verinum operator / (const verinum&left, const verinum&right) if (use_len <= (8*sizeof(long) - 1)) { long l = left.as_long(); long r = right.as_long(); - long v = l / r; + bool overflow = (l == LONG_MIN) && (r == -1); + long v = overflow ? LONG_MIN : l / r; for (unsigned idx = 0 ; idx < use_len ; idx += 1) { result.set(idx, (v & 1)? verinum::V1 : verinum::V0); v >>= 1; @@ -1518,7 +1520,8 @@ verinum operator % (const verinum&left, const verinum&right) /* Use native signed modulus to do the work. */ long l = left.as_long(); long r = right.as_long(); - long v = l % r; + bool overflow = (l == LONG_MIN) && (r == -1); + long v = overflow ? 0 : l % r; for (unsigned idx = 0 ; idx < use_len ; idx += 1) { result.set(idx, (v & 1)? verinum::V1 : verinum::V0); v >>= 1; diff --git a/verinum.h b/verinum.h index dfae90958..9d087f685 100644 --- a/verinum.h +++ b/verinum.h @@ -42,9 +42,9 @@ class verinum { enum V { V0 = 0, V1, Vx, Vz }; verinum(); - verinum(const string&str); + explicit verinum(const string&str); verinum(const V*v, unsigned nbits, bool has_len =true); - verinum(V, unsigned nbits =1, bool has_len =true); + explicit verinum(V, unsigned nbits =1, bool has_len =true); verinum(uint64_t val, unsigned bits); verinum(double val, bool); verinum(const verinum&); diff --git a/veriuser.h b/veriuser.h index 7e061c16f..7614673db 100644 --- a/veriuser.h +++ b/veriuser.h @@ -1,7 +1,7 @@ #ifndef VERIUSER_H #define VERIUSER_H /* - * Copyright (c) 2002-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2018 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 @@ -159,6 +159,7 @@ extern void veriusertfs_register_table(p_tfcell vtable); /* callback reasons */ #define reason_checktf 1 +#define reason_sizetf 2 #define reason_calltf 3 #define reason_paramvc 7 #define reason_synch 8 diff --git a/version.c b/version.c index 9367e2792..64e4178c2 100644 --- a/version.c +++ b/version.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Stephen Williams (steve@icarus.com) + * Copyright (c) 2009-2015 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 @@ -29,13 +29,10 @@ static void run_string(const char*txt) if (cp[0] == '%' && cp[1] != 0) { switch (cp[1]) { case 'M': - fprintf(stdout, "%u", VERSION_MAJOR1); - break; - case 'm': - fprintf(stdout, "%u", VERSION_MAJOR2); + fprintf(stdout, "%d", VERSION_MAJOR); break; case 'n': - fprintf(stdout, "%u", VERSION_MINOR); + fprintf(stdout, "%d", VERSION_MINOR); break; case 'E': fprintf(stdout, "%s", VERSION_EXTRA); diff --git a/version_base.h b/version_base.h index ce10b6b6e..39a3fa0b9 100644 --- a/version_base.h +++ b/version_base.h @@ -3,17 +3,17 @@ * Edit this definition in version_base.in to define the base version * number for the compiled result. */ -# define VERSION_MAJOR1 0 -# define VERSION_MAJOR2 10 +# define VERSION_MAJOR 11 # define VERSION_MINOR 0 -# define VERSION_EXTRA "(devel)" -/* This is a concatenation of MAJOR1.MAJOR2 that is used by - vams_simparam.c to make a double value. */ -# define VERSION_MAJOR 0.10 +/* + * This will be appended to the version. Use this to mark development + * versions and the like. + */ +# define VERSION_EXTRA " (devel)" # define VERSION_STRINGIFY(x) #x -# define VERSION_STR(a,b,extra) VERSION_STRINGIFY(a.b) " " extra +# define VERSION_STR(a,b,extra) VERSION_STRINGIFY(a.b) extra #define VERSION VERSION_STR(VERSION_MAJOR,VERSION_MINOR,VERSION_EXTRA) #endif diff --git a/vhdlpp/Makefile.in b/vhdlpp/Makefile.in index ce796c588..0a0f264d2 100644 --- a/vhdlpp/Makefile.in +++ b/vhdlpp/Makefile.in @@ -57,7 +57,7 @@ LIBS = @LIBS@ @EXTRALIBS@ M = StringHeap.o LineInfo.o -O = main.o architec.o compiler.o entity.o \ +O = main.o architec.o compiler.o entity.o std_funcs.o std_types.o \ expression.o package.o scope.o sequential.o subprogram.o vsignal.o vtype.o \ vtype_match.o \ architec_elaborate.o entity_elaborate.o expression_elaborate.o \ diff --git a/vhdlpp/architec.cc b/vhdlpp/architec.cc index b576af3b7..488ee9cd5 100644 --- a/vhdlpp/architec.cc +++ b/vhdlpp/architec.cc @@ -20,6 +20,7 @@ # include "architec.h" # include "expression.h" # include "parse_types.h" +# include "sequential.h" // Need this for parse_errors? # include "parse_api.h" # include @@ -28,7 +29,7 @@ using namespace std; Architecture::Architecture(perm_string name, const ActiveScope&ref, list&s) -: Scope(ref), name_(name), cur_component_(NULL) +: Scope(ref), name_(name), cur_component_(NULL), cur_process_(NULL) { statements_.splice(statements_.end(), s); } @@ -67,6 +68,14 @@ bool Architecture::find_constant(perm_string by_name, const VType*&typ, Expressi return false; } +Variable* Architecture::find_variable(perm_string by_name) const +{ + if(cur_process_) + return cur_process_->find_variable(by_name); + + return ScopeBase::find_variable(by_name); +} + void Architecture::push_genvar_type(perm_string gname, const VType*gtype) { genvar_type_t tmp; @@ -136,7 +145,7 @@ GenerateStatement::~GenerateStatement() } ForGenerate::ForGenerate(perm_string gname, perm_string genvar, - prange_t*rang, std::list&s) + ExpRange*rang, std::list&s) : GenerateStatement(gname, s), genvar_(genvar), lsb_(rang->lsb()), msb_(rang->msb()) { @@ -177,6 +186,21 @@ SignalAssignment::~SignalAssignment() delete lval_; } +CondSignalAssignment::CondSignalAssignment(ExpName*target, std::list&options) +: lval_(target) +{ + options_.splice(options_.end(), options); +} + +CondSignalAssignment::~CondSignalAssignment() +{ + delete lval_; + for(list::iterator it = options_.begin(); + it != options_.end(); ++it) { + delete *it; + } +} + ComponentInstantiation::ComponentInstantiation(perm_string i, perm_string c, list*parms, list*ports) @@ -229,18 +253,34 @@ Expression*ComponentInstantiation::find_generic_map(perm_string by_name) const return p->second; } +StatementList::StatementList(std::list*statement_list) +{ + if(statement_list) + statements_.splice(statements_.end(), *statement_list); +} + +StatementList::~StatementList() +{ + for(std::list::iterator it = statements_.begin(); + it != statements_.end(); ++it) { + delete *it; + } +} ProcessStatement::ProcessStatement(perm_string iname, + const ActiveScope&ref, std::list*sensitivity_list, std::list*statements_list) -: iname_(iname) +: StatementList(statements_list), Scope(ref), iname_(iname) { if (sensitivity_list) sensitivity_list_.splice(sensitivity_list_.end(), *sensitivity_list); - if (statements_list) - statements_list_.splice(statements_list_.end(), *statements_list); } ProcessStatement::~ProcessStatement() { + for(std::list::iterator it = sensitivity_list_.begin(); + it != sensitivity_list_.end(); ++it) { + delete *it; + } } diff --git a/vhdlpp/architec.h b/vhdlpp/architec.h index 869e89d19..052628f0d 100644 --- a/vhdlpp/architec.h +++ b/vhdlpp/architec.h @@ -30,10 +30,11 @@ class Entity; class Expression; class ExpName; class GenerateStatement; +class ProcessStatement; class SequentialStmt; class Signal; class named_expr_t; -class prange_t; +class ExpRange; /* * The Architecture class carries the contents (name, statements, @@ -65,9 +66,20 @@ class Architecture : public Scope, public LineInfo { perm_string get_name() const { return name_; } - // Sets the currently processed component (to be able to reach its parameters). - void set_cur_component(ComponentInstantiation*component) { cur_component_ = component; } bool find_constant(perm_string by_name, const VType*&typ, Expression*&exp) const; + Variable* find_variable(perm_string by_name) const; + + // Sets the currently processed component (to be able to reach its parameters). + void set_cur_component(ComponentInstantiation*component) { + assert(!cur_component_ || !component); + cur_component_ = component; + } + + // Sets the currently elaborated process (to use its scope for variable resolving). + void set_cur_process(ProcessStatement*process) { + assert(!cur_process_ || !process); + cur_process_ = process; + } // Elaborate this architecture in the context of the given entity. int elaborate(Entity*entity); @@ -113,7 +125,8 @@ class Architecture : public Scope, public LineInfo { // Currently processed component (or NULL if none). ComponentInstantiation*cur_component_; - private: // Not implemented + // Currently elaborated process (or NULL if none). + ProcessStatement*cur_process_; }; /* @@ -142,7 +155,7 @@ class ForGenerate : public GenerateStatement { public: ForGenerate(perm_string gname, perm_string genvar, - prange_t*rang, std::list&s); + ExpRange*rang, std::list&s); ~ForGenerate(); int elaborate(Entity*ent, Architecture*arc); @@ -189,6 +202,25 @@ class SignalAssignment : public Architecture::Statement { std::list rval_; }; +class CondSignalAssignment : public Architecture::Statement { + + public: + CondSignalAssignment(ExpName*target, std::list&options); + ~CondSignalAssignment(); + + int elaborate(Entity*ent, Architecture*arc); + int emit(ostream&out, Entity*entity, Architecture*arc); + void dump(ostream&out, int ident =0) const; + + private: + ExpName*lval_; + std::list options_; + + // List of signals that should be emitted in the related process + // sensitivity list. It is filled during the elaboration step. + std::listsens_list_; +}; + class ComponentInstantiation : public Architecture::Statement { public: @@ -215,28 +247,67 @@ class ComponentInstantiation : public Architecture::Statement { std::map port_map_; }; -class ProcessStatement : public Architecture::Statement { +class StatementList : public Architecture::Statement { + public: + StatementList(std::list*statement_list); + virtual ~StatementList(); + + int elaborate(Entity*ent, Architecture*arc) { + return elaborate(ent, static_cast(arc)); + } + + int emit(ostream&out, Entity*ent, Architecture*arc) { + return emit(out, ent, static_cast(arc)); + } + + virtual int elaborate(Entity*ent, ScopeBase*scope); + virtual int emit(ostream&out, Entity*entity, ScopeBase*scope); + virtual void dump(ostream&out, int indent =0) const; + + std::list& stmt_list() { return statements_; } + + private: + std::list statements_; +}; + +// There is no direct VHDL counterpart to SV 'initial' statement, +// but we can still use it during the translation process. +class InitialStatement : public StatementList { + public: + InitialStatement(std::list*statement_list) + : StatementList(statement_list) {} + + int emit(ostream&out, Entity*entity, ScopeBase*scope); + void dump(ostream&out, int indent =0) const; +}; + +// There is no direct VHDL counterpart to SV 'final' statement, +// but we can still use it during the translation process. +class FinalStatement : public StatementList { + public: + FinalStatement(std::list*statement_list) + : StatementList(statement_list) {} + + int emit(ostream&out, Entity*entity, ScopeBase*scope); + void dump(ostream&out, int indent =0) const; +}; + +class ProcessStatement : public StatementList, public Scope { public: ProcessStatement(perm_string iname, + const ActiveScope&ref, std::list*sensitivity_list, std::list*statement_list); ~ProcessStatement(); - virtual int elaborate(Entity*ent, Architecture*arc); - virtual int emit(ostream&out, Entity*entity, Architecture*arc); - virtual void dump(ostream&out, int indent =0) const; - - private: - int rewrite_as_always_edge_(Entity*ent, Architecture*arc); - int extract_anyedge_(Entity*ent, Architecture*arc); + int elaborate(Entity*ent, Architecture*arc); + int emit(ostream&out, Entity*entity, Architecture*arc); + void dump(ostream&out, int indent =0) const; private: perm_string iname_; - std::list sensitivity_list_; - std::list statements_list_; - }; #endif /* IVL_architec_H */ diff --git a/vhdlpp/architec_debug.cc b/vhdlpp/architec_debug.cc index d60bf48fb..7c5e5a711 100644 --- a/vhdlpp/architec_debug.cc +++ b/vhdlpp/architec_debug.cc @@ -95,6 +95,42 @@ void SignalAssignment::dump(ostream&out, int indent) const } } +void CondSignalAssignment::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "CondSignalAssignment file=" << get_fileline() << endl; + lval_->dump(out, indent+1); + out << setw(indent+2) << "" << "<= ..." << endl; + + for(list::const_iterator it = options_.begin(); + it != options_.end(); ++it) { + (*it)->dump(out, indent+2); + } +} + +void StatementList::dump(ostream&out, int indent) const +{ + out << setw(indent+3) << "" << "sequence of statements:" << endl; + + for (list::const_iterator cur = statements_.begin() + ; cur != statements_.end() ; ++cur) { + (*cur)->dump(out, indent+4); + } +} + +void InitialStatement::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "InitialStatement file=" << get_fileline() << endl; + + StatementList::dump(out, indent); +} + +void FinalStatement::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "FinalStatement file=" << get_fileline() << endl; + + StatementList::dump(out, indent); +} + void ProcessStatement::dump(ostream&out, int indent) const { out << setw(indent) << "" << "ProcessStatement name_=" << iname_ @@ -107,10 +143,5 @@ void ProcessStatement::dump(ostream&out, int indent) const (*cur)->dump(out, indent+4); } - out << setw(indent+3) << "" << "sequence of statements:" << endl; - - for (list::const_iterator cur = statements_list_.begin() - ; cur != statements_list_.end() ; ++cur) { - (*cur)->dump(out, indent+4); - } + StatementList::dump(out, indent); } diff --git a/vhdlpp/architec_elaborate.cc b/vhdlpp/architec_elaborate.cc index 9ab09bfa8..cb472977a 100644 --- a/vhdlpp/architec_elaborate.cc +++ b/vhdlpp/architec_elaborate.cc @@ -21,6 +21,7 @@ # include "entity.h" # include "expression.h" # include "sequential.h" +# include "subprogram.h" # include # include @@ -44,21 +45,39 @@ int Architecture::elaborate(Entity*entity) // Elaborate initializer expressions for signals & variables for (map::iterator cur = old_signals_.begin() ; cur != old_signals_.end() ; ++cur) { - cur->second->elaborate_init_expr(entity, this); + cur->second->elaborate(entity, this); } for (map::iterator cur = new_signals_.begin() ; cur != new_signals_.end() ; ++cur) { - cur->second->elaborate_init_expr(entity, this); + cur->second->elaborate(entity, this); } for (map::iterator cur = old_variables_.begin() ; cur != old_variables_.end() ; ++cur) { - cur->second->elaborate_init_expr(entity, this); + cur->second->elaborate(entity, this); } for (map::iterator cur = new_variables_.begin() ; cur != new_variables_.end() ; ++cur) { - cur->second->elaborate_init_expr(entity, this); + cur->second->elaborate(entity, this); } + // Elaborate subprograms + for (map::const_iterator cur = cur_subprograms_.begin() + ; cur != cur_subprograms_.end() ; ++cur) { + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + errors += (*it)->elaborate(); + } + } + // Create 'initial' and 'final' blocks for implicit + // initialization and clean-up actions + if(!initializers_.empty()) + statements_.push_front(new InitialStatement(&initializers_)); + + if(!finalizers_.empty()) + statements_.push_front(new FinalStatement(&finalizers_)); + for (list::iterator cur = statements_.begin() ; cur != statements_.end() ; ++cur) { @@ -167,187 +186,32 @@ int IfGenerate::elaborate(Entity*ent, Architecture*arc) return errors; } -/* - * This method attempts to rewrite the process content as an - * always-@(n-edge ) version of the same statement. This makes - * for a more natural translation to Verilog, if it comes to that. - */ -int ProcessStatement::rewrite_as_always_edge_(Entity*, Architecture*) +int StatementList::elaborate(Entity*ent, ScopeBase*scope) { - // If there are multiple sensitivity expressions, I give up. - if (sensitivity_list_.size() != 1) - return -1; + int errors = 0; - // If there are multiple statements, I give up. - if (statements_list_.size() != 1) - return -1; - - Expression*se = sensitivity_list_.front(); - SequentialStmt*stmt_raw = statements_list_.front(); - - // If the statement is not an if-statement, I give up. - IfSequential*stmt = dynamic_cast (stmt_raw); - if (stmt == 0) - return -1; - - // If the "if" statement has a false clause, then give up. - if (stmt->false_size() != 0) - return -1; - - const Expression*ce_raw = stmt->peek_condition(); - // Now we have matched this pattern: - // process() begin if ... - // The is the condition. - - if (const ExpFunc*ce_func = dynamic_cast(ce_raw)) { - if (ce_func->func_args() != 1) - return -1; - if (ce_func->func_name()!="rising_edge" && ce_func->func_name()!="falling_edge") - return -1; - - if (! se->symbolic_compare(ce_func->func_arg(0))) - return -1; - - // We've matched this pattern: - // process() if (rising_edge()) then ... - // and we can convert it to: - // always @(posedge ) ... - - ExpEdge::fun_t use_edge; - if (ce_func->func_name()=="rising_edge") - use_edge = ExpEdge::POSEDGE; - else if (ce_func->func_name()=="falling_edge") - use_edge = ExpEdge::NEGEDGE; - else - use_edge = ExpEdge::ANYEDGE; - - // Replace the sensitivity expression with an edge - // expression. The ExpEdge expression signals that this - // is an always-@(edge) statement. - ExpEdge*edge = new ExpEdge(use_edge, se); - assert(sensitivity_list_.size() == 1); - sensitivity_list_.pop_front(); - sensitivity_list_.push_front(edge); - - // Replace the statement with the body of the always - // statement, which is the true clause of the top "if" - // statement. There should be no "else" clause. - assert(statements_list_.size() == 1); - statements_list_.pop_front(); - stmt->extract_true(statements_list_); - - delete stmt; - return 0; + for (std::list::iterator it = statements_.begin(); + it != statements_.end(); ++it) { + errors += (*it)->elaborate(ent, scope); } - // Here we expect the condition to be - // 'event AND ='1'. - // So if ce_raw is not a logical AND, I give up. - const ExpLogical*ce = dynamic_cast (ce_raw); - if (ce == 0) - return -1; - if (ce->logic_fun() != ExpLogical::AND) - return -1; - - const Expression*op1_raw = ce->peek_operand1(); - const Expression*op2_raw = ce->peek_operand2(); - if (dynamic_cast(op2_raw)) { - const Expression*tmp = op1_raw; - op1_raw = op2_raw; - op2_raw = tmp; - } - - // If operand1 is not an 'event attribute, I give up. - const ExpAttribute*op1 = dynamic_cast(op1_raw); - if (op1 == 0) - return -1; - if (op1->peek_attribute() != "event") - return -1; - - const ExpRelation*op2 = dynamic_cast(op2_raw); - if (op2 == 0) - return -1; - if (op2->relation_fun() != ExpRelation::EQ) - return -1; - - const Expression*op2a_raw = op2->peek_operand1(); - const Expression*op2b_raw = op2->peek_operand2(); - - if (dynamic_cast(op2a_raw)) { - const Expression*tmp = op2b_raw; - op2b_raw = op2a_raw; - op2a_raw = tmp; - } - - if (! se->symbolic_compare(op1->peek_base())) - return -1; - - const ExpCharacter*op2b = dynamic_cast(op2b_raw); - if (op2b->value() != '1' && op2b->value() != '0') - return -1; - - // We've matched this pattern: - // process () if ('event and = ) then ... - // And we can convert it to: - // always @(edge ) ... - - // Replace the sensitivity expression with an edge - // expression. The ExpEdge expression signals that this is an - // always-@(edge) statement. - ExpEdge*edge = new ExpEdge(op2b->value()=='1'? ExpEdge::POSEDGE : ExpEdge::NEGEDGE, se); - assert(sensitivity_list_.size() == 1); - sensitivity_list_.pop_front(); - sensitivity_list_.push_front(edge); - - // Replace the statement with the body of the always - // statement, which is the true clause of the top "if" - // statement. There should be no "else" clause. - assert(statements_list_.size() == 1); - statements_list_.pop_front(); - - stmt->extract_true(statements_list_); - - delete stmt; - - return 0; -} - -/* - * Change the "process () " into "always @() ..." - */ -int ProcessStatement::extract_anyedge_(Entity*, Architecture*) -{ - - vector se; - while (! sensitivity_list_.empty()) { - se.push_back(sensitivity_list_.front()); - sensitivity_list_.pop_front(); - } - - for (size_t idx = 0 ; idx < se.size() ; idx += 1) { - ExpEdge*edge = new ExpEdge(ExpEdge::ANYEDGE, se[idx]); - FILE_NAME(edge, se[idx]); - sensitivity_list_.push_back(edge); - } - - return 0; + return errors; } int ProcessStatement::elaborate(Entity*ent, Architecture*arc) { int errors = 0; - if (rewrite_as_always_edge_(ent, arc) >= 0) { + arc->set_cur_process(this); - } else if (extract_anyedge_(ent, arc) >= 0) { - - } else { + for (map::iterator cur = new_variables_.begin() + ; cur != new_variables_.end() ; ++cur) { + cur->second->elaborate(ent, arc); } - for (list::iterator cur = statements_list_.begin() - ; cur != statements_list_.end() ; ++cur) { - errors += (*cur)->elaborate(ent, arc); - } + StatementList::elaborate(ent, arc); + + arc->set_cur_process(NULL); return errors; } @@ -378,3 +242,48 @@ int SignalAssignment::elaborate(Entity*ent, Architecture*arc) return errors; } + +int CondSignalAssignment::elaborate(Entity*ent, Architecture*arc) +{ + int errors = 0; + + // Visitor to extract signal names occurring in the conditional + // statements to create the sensitivity list + struct name_extractor_t : public ExprVisitor { + name_extractor_t(list& name_list) + : name_list_(name_list) {} + void operator() (Expression*s) { + if(const ExpName*name = dynamic_cast(s)) + name_list_.push_back(name); + } + + private: + list& name_list_; + } name_extractor(sens_list_); + + // Elaborate the l-value expression. + errors += lval_->elaborate_lval(ent, arc, true); + + // The elaborate_lval should have resolved the type of the + // l-value expression. We'll use that type to elaborate the + // r-value. + const VType*lval_type = lval_->peek_type(); + if (lval_type == 0) { + if (errors == 0) { + errors += 1; + cerr << get_fileline() + << ": error: Unable to calculate type for l-value expression." + << endl; + } + return errors; + } + + for(list::iterator it = options_.begin(); + it != options_.end(); ++it) { + ExpConditional::case_t*cas = (*it); + cas->elaborate_expr(ent, arc, lval_type); + cas->visit(name_extractor); + } + + return errors; +} diff --git a/vhdlpp/architec_emit.cc b/vhdlpp/architec_emit.cc index cf18cdd84..0c72d3bd3 100644 --- a/vhdlpp/architec_emit.cc +++ b/vhdlpp/architec_emit.cc @@ -23,42 +23,33 @@ # include "sequential.h" # include "subprogram.h" # include "vsignal.h" +# include "std_types.h" # include # include # include -int Scope::emit_signals(ostream&out, Entity*entity, Architecture*arc) +int Scope::emit_signals(ostream&out, Entity*entity, ScopeBase*scope) { - int errors = 0; + int errors = 0; - for (map::iterator cur = old_signals_.begin() - ; cur != old_signals_.end() ; ++cur) { + for (map::iterator cur = new_signals_.begin() + ; cur != new_signals_.end() ; ++cur) { + errors += cur->second->emit(out, entity, scope); + } - errors += cur->second->emit(out, entity, arc); - } - for (map::iterator cur = new_signals_.begin() - ; cur != new_signals_.end() ; ++cur) { - - errors += cur->second->emit(out, entity, arc); - } - return errors; + return errors; } -int Scope::emit_variables(ostream&out, Entity*entity, Architecture*arc) +int Scope::emit_variables(ostream&out, Entity*entity, ScopeBase*scope) { - int errors = 0; + int errors = 0; - for (map::iterator cur = old_variables_.begin() - ; cur != old_variables_.end() ; ++cur) { + for (map::iterator cur = new_variables_.begin() + ; cur != new_variables_.end() ; ++cur) { + errors += cur->second->emit(out, entity, scope); + } - errors += cur->second->emit(out, entity, arc); - } - for (map::iterator cur = new_variables_.begin() - ; cur != new_variables_.end() ; ++cur) { - - errors += cur->second->emit(out, entity, arc); - } - return errors; + return errors; } int Architecture::emit(ostream&out, Entity*entity) @@ -70,15 +61,16 @@ int Architecture::emit(ostream&out, Entity*entity) // of the full definition. typedef_context_t typedef_ctx; - //for (map::iterator cur = use_types_.begin() - //; cur != use_types_.end() ; ++cur) { + for (map::iterator cur = use_types_.begin() + ; cur != use_types_.end() ; ++cur) { + if(is_global_type(cur->first)) + continue; - //if(const VTypeDef*def = dynamic_cast(cur->second)) - //errors += def->emit_typedef(out, typedef_ctx); - //} + if(const VTypeDef*def = dynamic_cast(cur->second)) + errors += def->emit_typedef(out, typedef_ctx); + } for (map::iterator cur = cur_types_.begin() ; cur != cur_types_.end() ; ++cur) { - if(const VTypeDef*def = dynamic_cast(cur->second)) errors += def->emit_typedef(out, typedef_ctx); } @@ -101,11 +93,18 @@ int Architecture::emit(ostream&out, Entity*entity) errors += emit_signals(out, entity, this); errors += emit_variables(out, entity, this); - for (map::const_iterator cur = cur_subprograms_.begin() + for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++ cur) { - // Do not emit unbounded functions, we will just need fixed instances later - if(!cur->second->unbounded()) - errors += cur->second->emit_package(out); + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + SubprogramHeader*subp = *it; + + // Do not emit unbounded functions, we will just need fixed instances later + if(!subp->unbounded()) + errors += subp->emit_package(out); + } } for (list::iterator cur = statements_.begin() @@ -130,19 +129,76 @@ int SignalAssignment::emit(ostream&out, Entity*ent, Architecture*arc) int errors = 0; ivl_assert(*this, rval_.size() == 1); - Expression*rval = rval_.front(); + const Expression*rval = rval_.front(); out << "// " << get_fileline() << endl; out << "assign "; + if(const ExpDelay*delayed = dynamic_cast(rval)) { + out << "#("; + delayed->peek_delay()->emit(out, ent, arc); + out << ") "; + rval = delayed->peek_expr(); + } errors += lval_->emit(out, ent, arc); out << " = "; - errors += rval->emit(out, ent, arc); - out << ";" << endl; + return errors; } +int CondSignalAssignment::emit(ostream&out, Entity*ent, Architecture*arc) +{ + int errors = 0; + + out << "// " << get_fileline() << endl; + out << "always begin" << endl; + bool first = true; + + for(list::iterator it = options_.begin(); + it != options_.end(); ++it) { + ExpConditional::case_t*cas = *it; + ivl_assert(*this, cas->true_clause().size() == 1); + const Expression*rval = cas->true_clause().front(); + + if(first) + first = false; + else + out << "else "; + + if(Expression*cond = cas->condition()) { + out << "if("; + cond->emit(out, ent, arc); + out << ") "; + } + + out << endl; + lval_->emit(out, ent, arc); + out << " = "; + rval->emit(out, ent, arc); + out << ";" << endl; + } + + // Sensitivity list + first = true; + out << "@("; + + for(list::const_iterator it = sens_list_.begin(); + it != sens_list_.end(); ++it) { + if(first) + first = false; + else + out << ","; + + errors += (*it)->emit(out, ent, arc); + } + + out << ");" << endl; + out << "end" << endl; + + return errors; +} + int ComponentInstantiation::emit(ostream&out, Entity*ent, Architecture*arc) { const char*comma = ""; @@ -150,6 +206,22 @@ int ComponentInstantiation::emit(ostream&out, Entity*ent, Architecture*arc) arc->set_cur_component(this); + if(ComponentBase*comp = arc->find_component(cname_)) { + const std::vector& generics = comp->get_generics(); + + if(generics.size() != generic_map_.size()) + // Display an error for generics that do not have neither + // default nor component specific value defined + for(vector::const_iterator it = generics.begin(); + it != generics.end(); ++it) { + if(!(*it)->expr && generic_map_.count((*it)->name) == 0) { + cerr << get_fileline() << ": generic " << (*it)->name << + "value is not defined" << endl; + ++errors; + } + } + } + out << cname_; if (! generic_map_.empty()) { out << " #("; @@ -230,6 +302,36 @@ int IfGenerate::emit(ostream&out, Entity*ent, Architecture*arc) return errors; } +int StatementList::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + int errors = 0; + + for (std::list::iterator it = statements_.begin(); + it != statements_.end(); ++it) { + errors += (*it)->emit(out, ent, scope); + } + + return errors; +} + +int InitialStatement::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + out << "initial begin" << endl; + int errors = StatementList::emit(out, ent, scope); + out << "end" << endl; + + return errors; +} + +int FinalStatement::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + out << "final begin" << endl; + int errors = StatementList::emit(out, ent, scope); + out << "end" << endl; + + return errors; +} + /* * Emit a process statement using "always" syntax. * @@ -238,16 +340,25 @@ int IfGenerate::emit(ostream&out, Entity*ent, Architecture*arc) * beginning. In VHDL, all the statements are initially executed once * before blocking in the first wait on the sensitivity list. */ -int ProcessStatement::emit(ostream&out, Entity*ent, Architecture*arc) +int ProcessStatement::emit(ostream&out, Entity*ent, Architecture*) { int errors = 0; - out << "always begin" << endl; + /* Check if the process has no sensitivity list and ends up with + * a final wait. If so, convert the process to an initial block. */ + const WaitStmt*wait_stmt = NULL; + if (!stmt_list().empty()) + wait_stmt = dynamic_cast(stmt_list().back()); - for (list::iterator cur = statements_list_.begin() - ; cur != statements_list_.end() ; ++cur) { - errors += (*cur)->emit(out, ent, arc); - } + if (wait_stmt && wait_stmt->type() == WaitStmt::FINAL) + out << "initial begin : "; + else + out << "always begin : "; + + out << peek_name() << endl; + + errors += emit_variables(out, ent, this); + errors += StatementList::emit(out, ent, this); if (! sensitivity_list_.empty()) { out << "@("; @@ -256,13 +367,12 @@ int ProcessStatement::emit(ostream&out, Entity*ent, Architecture*arc) ; cur != sensitivity_list_.end() ; ++cur) { if (comma) out << comma; - errors += (*cur)->emit(out, ent, arc); + errors += (*cur)->emit(out, ent, this); comma = ", "; } - out << ") /* sensitivity list for process */;" << endl; + out << "); /* sensitivity list for process */" << endl; } - out << "end" << endl; + out << "end /* " << peek_name() << " */" << endl; return errors; - } diff --git a/vhdlpp/compiler.cc b/vhdlpp/compiler.cc index 66193a370..9a1a6279e 100644 --- a/vhdlpp/compiler.cc +++ b/vhdlpp/compiler.cc @@ -21,3 +21,5 @@ StringHeapLex lex_strings; StringHeapLex filename_strings; + +StringHeapLex gen_strings; diff --git a/vhdlpp/compiler.h b/vhdlpp/compiler.h index 3cd1b820a..165663a40 100644 --- a/vhdlpp/compiler.h +++ b/vhdlpp/compiler.h @@ -30,8 +30,13 @@ extern bool verbose_flag; extern bool debug_elaboration; extern std::ofstream debug_log_file; +// Stores strings created by the lexer extern StringHeapLex lex_strings; +// Stores file names extern StringHeapLex filename_strings; +// Stores generated strings (e.g. scope names) +extern StringHeapLex gen_strings; + #endif /* IVL_compiler_H */ diff --git a/vhdlpp/debug.cc b/vhdlpp/debug.cc index 7c2295718..717687d68 100644 --- a/vhdlpp/debug.cc +++ b/vhdlpp/debug.cc @@ -101,7 +101,7 @@ void ComponentBase::dump_ports(ostream&out, int indent) const } } -void Scope::dump_scope(ostream&out) const +void ScopeBase::dump_scope(ostream&out) const { // Dump types out << " -- imported types" << endl; @@ -150,18 +150,35 @@ void Scope::dump_scope(ostream&out) const } // Dump subprograms out << " -- Imported Subprograms" << endl; - for (map::const_iterator cur = use_subprograms_.begin() - ; cur != use_subprograms_.end() ; ++cur) { - out << " subprogram " << cur->first << " is" << endl; - cur->second->dump(out); - out << " end subprogram " << cur->first << endl; + for (map::const_iterator cur = use_subprograms_.begin() + ; cur != cur_subprograms_.end() ; ++ cur) { + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + const SubprogramHeader*subp = *it; + out << " subprogram " << cur->first << " is" << endl; + subp->dump(out); + if(subp->body()) + subp->body()->dump(out); + out << " end subprogram " << cur->first << endl; + } } + out << " -- Subprograms from this scope" << endl; - for (map::const_iterator cur = cur_subprograms_.begin() + for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++cur) { - out << " subprogram " << cur->first << " is" << endl; - cur->second->dump(out); - out << " end subprogram " << cur->first << endl; + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + const SubprogramHeader*subp = *it; + out << " subprogram " << cur->first << " is" << endl; + subp->dump(out); + if(subp->body()) + subp->body()->dump(out); + out << " end subprogram " << cur->first << endl; + } } // Dump component declarations out << " -- Components" << endl; @@ -247,9 +264,16 @@ void ExpAggregate::choice_t::dump(ostream&out, int indent) const out << setw(indent) << "" << "?choice_t?" << endl; } -void ExpAttribute::dump(ostream&out, int indent) const +void ExpTypeAttribute::dump(ostream&out, int indent) const { - out << setw(indent) << "" << "Attribute " << name_ + out << setw(indent) << "" << "Attribute (type-related) " << name_ + << " at " << get_fileline() << endl; + base_->show(out); +} + +void ExpObjAttribute::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "Attribute (object-related) " << name_ << " at " << get_fileline() << endl; base_->dump(out, indent+4); } @@ -279,22 +303,14 @@ void ExpCharacter::dump(ostream&out, int indent) const void ExpConditional::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Conditional expression at "<< get_fileline() << endl; - out << setw(indent) << "" << " when:" << endl; - cond_->dump(out, indent+4); - out << setw(indent) << "" << " do:" << endl; - for (list::const_iterator cur = true_clause_.begin() - ; cur != true_clause_.end() ; ++cur) { - (*cur)->dump(out, indent+4); - } - - for (list::const_iterator cur = else_clause_.begin() - ; cur != else_clause_.end() ; ++cur) { + for (list::const_iterator cur = options_.begin() + ; cur != options_.end() ; ++cur) { (*cur)->dump(out, indent); } } -void ExpConditional::else_t::dump(ostream&out, int indent) const +void ExpConditional::case_t::dump(ostream&out, int indent) const { out << setw(indent) << "" << "when:" << endl; if (cond_) cond_->dump(out, indent+4); @@ -379,10 +395,13 @@ void ExpName::dump(ostream&out, int indent) const << " at " << get_fileline() << endl; if (prefix_.get()) prefix_->dump(out, indent+8); - if (index_) - index_->dump(out, indent+6); - if (lsb_) - lsb_->dump(out, indent+6); + + if (indices_) { + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + (*it)->dump(out, indent+6); + } + } } void ExpNameALL::dump(ostream&out, int indent) const @@ -417,46 +436,12 @@ void ExpRelation::dump(ostream&out, int indent) const dump_operands(out, indent+4); } -void ExpString::dump(ostream&out, int indent) const -{ - out << setw(indent) << "" << "String \""; - for(vector::const_iterator it = value_.begin(); - it != value_.end(); ++it) - out << *it; - out << "\"" - << " at " << get_fileline() << endl; -} - -void ExpUAbs::dump(ostream&out, int indent) const -{ - out << setw(indent) << "" << "abs() at " << get_fileline() << endl; - dump_operand1(out, indent+4); -} - -void ExpUnary::dump_operand1(ostream&out, int indent) const -{ - operand1_->dump(out, indent); -} - -void ExpUNot::dump(ostream&out, int indent) const -{ - out << setw(indent) << "" << "not() at " << get_fileline() << endl; - dump_operand1(out, indent+4); -} - void named_expr_t::dump(ostream&out, int indent) const { out << setw(indent) << "" << name_ << "=>"; expr_->dump(out, indent); } -void prange_t::dump(ostream&out, int indent) const -{ - left_->dump(out, indent); - out << setw(indent) << "" << (direction_ ? "downto" : "to"); - right_->dump(out, indent); -} - ostream& Expression::dump_inline(ostream&out) const { out << typeid(*this).name(); @@ -475,7 +460,20 @@ ostream& ExpReal::dump_inline(ostream&out) const return out; } -void Subprogram::dump(ostream&fd) const +void SubprogramBody::dump(ostream&fd) const +{ + if (statements_== 0 || statements_->empty()) { + fd << " " << endl; + } else { + for (list::const_iterator cur = statements_->begin() + ; cur != statements_->end() ; ++cur) { + SequentialStmt*curp = *cur; + curp->dump(fd, 8); + } + } +} + +void SubprogramHeader::dump(ostream&fd) const { fd << " " << name_; @@ -501,14 +499,4 @@ void Subprogram::dump(ostream&fd) const fd << " return "; return_type_->show(fd); fd << endl; - - if (statements_== 0 || statements_->empty()) { - fd << " " << endl; - } else { - for (list::const_iterator cur = statements_->begin() - ; cur != statements_->end() ; ++cur) { - SequentialStmt*curp = *cur; - curp->dump(fd, 8); - } - } } diff --git a/vhdlpp/entity.h b/vhdlpp/entity.h index b93d6a369..01a5e15fb 100644 --- a/vhdlpp/entity.h +++ b/vhdlpp/entity.h @@ -27,14 +27,27 @@ # include "StringHeap.h" # include "LineInfo.h" -typedef enum { PORT_NONE=0, PORT_IN, PORT_OUT } port_mode_t; +typedef enum { PORT_NONE=0, PORT_IN, PORT_OUT, PORT_INOUT } port_mode_t; class Architecture; class Expression; class InterfacePort : public LineInfo { public: - InterfacePort() { mode = PORT_NONE; type=0; expr=0; } + InterfacePort(port_mode_t mod = PORT_NONE, + perm_string nam = empty_perm_string, + const VType*typ = NULL, + Expression*exp = NULL) + : mode(mod), name(nam), type(typ), expr(exp) + {} + + explicit InterfacePort(const VType*typ) + : mode(PORT_NONE), type(typ), expr(NULL) + {} + + InterfacePort(const VType*typ, port_mode_t mod) + : mode(mod), type(typ), expr(NULL) + {} // Port direction from the source code. port_mode_t mode; @@ -55,7 +68,7 @@ class InterfacePort : public LineInfo { class ComponentBase : public LineInfo { public: - ComponentBase(perm_string name); + explicit ComponentBase(perm_string name); ~ComponentBase(); // Entities have names. @@ -63,6 +76,7 @@ class ComponentBase : public LineInfo { const InterfacePort* find_port(perm_string by_name) const; const InterfacePort* find_generic(perm_string by_name) const; + const std::vector& get_generics() const { return parms_; } // Declare the ports for the entity. The parser calls this // method with a list of interface elements that were parsed @@ -91,7 +105,7 @@ class ComponentBase : public LineInfo { class Entity : public ComponentBase { public: - Entity(perm_string name); + explicit Entity(perm_string name); ~Entity(); // bind an architecture to the entity, and return the diff --git a/vhdlpp/entity_emit.cc b/vhdlpp/entity_emit.cc index 268b36f7d..62acac193 100644 --- a/vhdlpp/entity_emit.cc +++ b/vhdlpp/entity_emit.cc @@ -50,11 +50,14 @@ int Entity::emit(ostream&out) const InterfacePort*curp = *cur; if (cur != parms_.begin()) out << ", "; - out << "parameter \\" << curp->name << " "; + out << "parameter \\" << curp->name << " = "; if(curp->expr) { - out << "= "; errors += curp->expr->emit(out, this, 0); - } + } else { + // Unlike VHDL, Verilog module parameter port list + // elements are always assignments. Fill in the blank. + out << "1'bx"; + } } out << ") "; } @@ -74,17 +77,21 @@ int Entity::emit(ostream&out) switch (port->mode) { case PORT_NONE: // Should not happen + cerr << get_fileline() << ": error: Undefined port direction." << endl; out << "NO_PORT " << port->name; break; case PORT_IN: out << "input "; - errors += decl.emit(out, port->name); break; case PORT_OUT: out << "output "; - errors += decl.emit(out, port->name); + break; + case PORT_INOUT: + out << "inout "; break; } + + errors += decl.emit(out, port->name); } cout << ")"; } diff --git a/vhdlpp/entity_stream.cc b/vhdlpp/entity_stream.cc index cd1e67334..dfde97c74 100644 --- a/vhdlpp/entity_stream.cc +++ b/vhdlpp/entity_stream.cc @@ -69,6 +69,9 @@ void ComponentBase::write_to_stream(ostream&fd) const case PORT_OUT: fd << "out "; break; + case PORT_INOUT: + fd << "inout "; + break; } item->type->write_to_stream(fd); diff --git a/vhdlpp/expression.cc b/vhdlpp/expression.cc index 77d80a4d6..b9dff1b91 100644 --- a/vhdlpp/expression.cc +++ b/vhdlpp/expression.cc @@ -1,6 +1,7 @@ /* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2015 / Stephen Williams (steve@icarus.com), + * Copyright CERN 2016 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it @@ -23,6 +24,7 @@ # include "subprogram.h" # include "parse_types.h" # include "scope.h" +# include "library.h" # include # include # include @@ -54,21 +56,97 @@ bool Expression::symbolic_compare(const Expression*) const return false; } -ExpAttribute::ExpAttribute(ExpName*bas, perm_string nam) -: base_(bas), name_(nam) +ExpAttribute::ExpAttribute(perm_string nam, list*args) +: name_(nam), args_(args) { } ExpAttribute::~ExpAttribute() { - delete base_; + if(args_) { + for(list::iterator it = args_->begin(); + it != args_->end(); ++it) { + delete *it; + } + } + + delete args_; } -Expression*ExpAttribute::clone() const -{ - return new ExpAttribute(static_cast(base_->clone()), name_); +list*ExpAttribute::clone_args() const { + list*new_args = NULL; + + if(args_) { + for(list::iterator it = args_->begin(); + it != args_->end(); ++it) { + new_args->push_back((*it)->clone()); + } + } + + return new_args; } +void ExpAttribute::visit_args(ExprVisitor& func) +{ + func.down(); + func(this); + + if(args_) { + for(list::iterator it = args_->begin(); + it != args_->end(); ++it) { + (*it)->visit(func); + } + } + + func.up(); +} + +ExpObjAttribute::ExpObjAttribute(ExpName*base, perm_string name, list*args) +: ExpAttribute(name, args), base_(base) +{ +} + +ExpObjAttribute::~ExpObjAttribute() +{ + delete base_; +} + +Expression*ExpObjAttribute::clone() const +{ + return new ExpObjAttribute(static_cast(base_->clone()), + name_, clone_args()); +} + +void ExpObjAttribute::visit(ExprVisitor&func) +{ + func.down(); + func(this); + visit_args(func); + base_->visit(func); + func.up(); +} + +ExpTypeAttribute::ExpTypeAttribute(const VType*base, perm_string name, list*args) +: ExpAttribute(name, args), base_(base) +{ +} + +Expression*ExpTypeAttribute::clone() const +{ + return new ExpTypeAttribute(base_, name_, clone_args()); +} + +void ExpTypeAttribute::visit(ExprVisitor&func) +{ + func.down(); + func(this); + visit_args(func); + func.up(); +} + +const perm_string ExpAttribute::LEFT = perm_string::literal("left"); +const perm_string ExpAttribute::RIGHT = perm_string::literal("right"); + ExpBinary::ExpBinary(Expression*op1, Expression*op2) : operand1_(op1), operand2_(op2) { @@ -80,14 +158,23 @@ ExpBinary::~ExpBinary() delete operand2_; } -bool ExpBinary::eval_operand1(ScopeBase*scope, int64_t&val) const +bool ExpBinary::eval_operand1(Entity*ent, ScopeBase*scope, int64_t&val) const { - return operand1_->evaluate(scope, val); + return operand1_->evaluate(ent, scope, val); } -bool ExpBinary::eval_operand2(ScopeBase*scope, int64_t&val) const +bool ExpBinary::eval_operand2(Entity*ent, ScopeBase*scope, int64_t&val) const { - return operand2_->evaluate(scope, val); + return operand2_->evaluate(ent, scope, val); +} + +void ExpBinary::visit(ExprVisitor&func) +{ + func.down(); + func(this); + operand1_->visit(func); + operand2_->visit(func); + func.up(); } ExpUnary::ExpUnary(Expression*op1) @@ -100,6 +187,14 @@ ExpUnary::~ExpUnary() delete operand1_; } +void ExpUnary::visit(ExprVisitor&func) +{ + func.down(); + func(this); + operand1_->visit(func); + func.up(); +} + ExpAggregate::ExpAggregate(std::list*el) : elements_(el? el->size() : 0) { @@ -110,12 +205,21 @@ ExpAggregate::ExpAggregate(std::list*el) elements_[idx++] = el->front(); el->pop_front(); } + delete el; } ExpAggregate::~ExpAggregate() { - for (size_t idx = 0 ; idx < elements_.size() ; idx += 1) - delete elements_[idx]; + for(std::vector::iterator it = elements_.begin(); + it != elements_.end(); ++it) { + delete *it; + } + + for(std::vector::iterator it = aggregate_.begin(); + it != aggregate_.end(); ++it) { + delete it->choice; + if(!it->alias_flag) delete it->expr; + } } Expression* ExpAggregate::clone() const @@ -135,6 +239,27 @@ Expression* ExpAggregate::clone() const return new ExpAggregate(new_elements); } +void ExpAggregate::visit(ExprVisitor&func) +{ + func.down(); + func(this); + + for(std::vector::iterator it = elements_.begin(); + it != elements_.end(); ++it) { + (*it)->extract_expression()->visit(func); + } + + for(std::vector::iterator it = aggregate_.begin(); + it != aggregate_.end(); ++it) { + if(Expression*choice_expr = it->choice->simple_expression(false)) + choice_expr->visit(func); + + it->expr->visit(func); + } + + func.up(); +} + ExpAggregate::choice_t::choice_t(Expression*exp) : expr_(exp) { @@ -144,7 +269,7 @@ ExpAggregate::choice_t::choice_t() { } -ExpAggregate::choice_t::choice_t(prange_t*rang) +ExpAggregate::choice_t::choice_t(ExpRange*rang) : range_(rang) { } @@ -155,7 +280,7 @@ ExpAggregate::choice_t::choice_t(const choice_t&other) expr_.reset(e->clone()); if(other.range_.get()) - range_.reset(new prange_t(*other.range_.get())); + range_.reset(static_cast(other.range_.get()->clone())); } ExpAggregate::choice_t::~choice_t() @@ -173,7 +298,7 @@ Expression*ExpAggregate::choice_t::simple_expression(bool detach_flag) return res; } -prange_t*ExpAggregate::choice_t::range_expressions(void) +ExpRange*ExpAggregate::choice_t::range_expressions(void) { return range_.get(); } @@ -256,61 +381,66 @@ ExpConcat::~ExpConcat() delete operand2_; } -ExpConditional::ExpConditional(Expression*co, list*tru, - list*fal) -: cond_(co) +void ExpConcat::visit(ExprVisitor&func) { - if (tru) true_clause_.splice(true_clause_.end(), *tru); - if (fal) else_clause_.splice(else_clause_.end(), *fal); + func.down(); + func(this); + operand1_->visit(func); + operand2_->visit(func); + func.up(); +} + +ExpConditional::ExpConditional(Expression*co, list*tru, + list*options) +{ + if(co && tru) options_.push_back(new case_t(co, tru)); + if(options) options_.splice(options_.end(), *options); } ExpConditional::~ExpConditional() { - delete cond_; - while (! true_clause_.empty()) { - Expression*tmp = true_clause_.front(); - true_clause_.pop_front(); - delete tmp; - } - while (! else_clause_.empty()) { - else_t*tmp = else_clause_.front(); - else_clause_.pop_front(); + while (!options_.empty()) { + case_t*tmp = options_.front(); + options_.pop_front(); delete tmp; } } Expression*ExpConditional::clone() const { - std::list*new_true_clause = NULL; - if(!true_clause_.empty()) { - new_true_clause = new std::list(); + std::list*new_options = NULL; + if(!options_.empty()) { + new_options = new std::list(); - for(std::list::const_iterator it = true_clause_.begin(); - it != true_clause_.end(); ++it) { - new_true_clause->push_back((*it)->clone()); + for(std::list::const_iterator it = options_.begin(); + it != options_.end(); ++it) { + new_options->push_back(new case_t(**it)); } } - std::list*new_else_clause = NULL; - if(!else_clause_.empty()) { - new_else_clause = new std::list(); - - for(std::list::const_iterator it = else_clause_.begin(); - it != else_clause_.end(); ++it) { - new_else_clause->push_back(new else_t(**it)); - } - } - - return new ExpConditional(cond_->clone(), new_true_clause, new_else_clause); + return new ExpConditional(NULL, NULL, new_options); } -ExpConditional::else_t::else_t(Expression*cond, std::list*tru) +void ExpConditional::visit(ExprVisitor&func) +{ + func.down(); + func(this); + + for(std::list::iterator it = options_.begin(); + it != options_.end(); ++it) + (*it)->visit(func); + + func.up(); +} + +ExpConditional::case_t::case_t(Expression*cond, std::list*tru) : cond_(cond) { if (tru) true_clause_.splice(true_clause_.end(), *tru); } -ExpConditional::else_t::else_t(const else_t&other) +ExpConditional::case_t::case_t(const case_t&other) +: LineInfo(other) { cond_ = other.cond_->clone(); for(std::list::const_iterator it = other.true_clause_.begin(); @@ -319,7 +449,7 @@ ExpConditional::else_t::else_t(const else_t&other) } } -ExpConditional::else_t::~else_t() +ExpConditional::case_t::~case_t() { delete cond_; while (! true_clause_.empty()) { @@ -329,6 +459,50 @@ ExpConditional::else_t::~else_t() } } +ExpSelected::ExpSelected(Expression*selector, std::list*options) +: ExpConditional(NULL, NULL, options), selector_(selector) +{ + // Currently condition field contains only value, + // so substitute it with a comparison to create a valid condition + for(std::list::iterator it = options_.begin(); + it != options_.end(); ++it) { + Expression*cond = (*it)->condition(); + + if(cond) + (*it)->set_condition(new ExpRelation(ExpRelation::EQ, selector_->clone(), cond)); + } +} + +ExpSelected::~ExpSelected() +{ +} + +Expression*ExpSelected::clone() const +{ + std::list*new_options = NULL; + if(!options_.empty()) { + new_options = new std::list(); + + for(std::list::const_iterator it = options_.begin(); + it != options_.end(); ++it) { + new_options->push_back(new case_t(**it)); + } + } + + return new ExpSelected(selector_->clone(), new_options); +} + +void ExpConditional::case_t::visit(ExprVisitor&func) +{ + func.down(); + if(cond_) + cond_->visit(func); + + for(std::list::iterator it = true_clause_.begin(); + it != true_clause_.end(); ++it) + (*it)->visit(func); + func.up(); +} ExpEdge::ExpEdge(ExpEdge::fun_t typ, Expression*op) : ExpUnary(op), fun_(typ) @@ -377,11 +551,51 @@ Expression*ExpFunc::clone() const { return f; } +void ExpFunc::visit(ExprVisitor&func) +{ + func.down(); + func(this); + + if(!argv_.empty()) { + for(std::vector::iterator it = argv_.begin(); + it != argv_.end(); ++it) + (*it)->visit(func); + } + + func.up(); +} + const VType* ExpFunc::func_ret_type() const { return def_ ? def_->peek_return_type() : NULL; } +SubprogramHeader*ExpFunc::match_signature(Entity*ent, ScopeBase*scope) const +{ + SubprogramHeader*prog = NULL; + list arg_types; + + // Create a list of argument types to find a matching subprogram + for(vector::const_iterator it = argv_.begin(); + it != argv_.end(); ++it) { + arg_types.push_back((*it)->probe_type(ent, scope)); + } + + prog = scope->match_subprogram(name_, &arg_types); + + if(!prog) + prog = library_match_subprogram(name_, &arg_types); + + if(!prog) { + cerr << get_fileline() << ": sorry: could not find function "; + emit_subprogram_sig(cerr, name_, arg_types); + cerr << endl; + ivl_assert(*this, false); + } + + return prog; +} + ExpInteger::ExpInteger(int64_t val) : value_(val) { @@ -391,7 +605,7 @@ ExpInteger::~ExpInteger() { } -bool ExpInteger::evaluate(ScopeBase*, int64_t&val) const +bool ExpInteger::evaluate(Entity*, ScopeBase*, int64_t&val) const { val = value_; return true; @@ -416,38 +630,54 @@ ExpLogical::~ExpLogical() } ExpName::ExpName(perm_string nn) -: name_(nn), index_(0), lsb_(0) +: name_(nn), indices_(NULL) { } ExpName::ExpName(perm_string nn, list*indices) -: name_(nn), index_(0), lsb_(0) -{ - /* For now, assume a single index. */ - ivl_assert(*this, indices->size() == 1); - - index_ = indices->front(); - indices->pop_front(); -} - -ExpName::ExpName(perm_string nn, Expression*msb, Expression*lsb) -: name_(nn), index_(msb), lsb_(lsb) +: name_(nn), indices_(indices) { } -ExpName::ExpName(ExpName*prefix, perm_string nn) -: prefix_(prefix), name_(nn), index_(0), lsb_(0) -{ -} - -ExpName::ExpName(ExpName*prefix, perm_string nn, Expression*msb, Expression*lsb) -: prefix_(prefix), name_(nn), index_(msb), lsb_(lsb) +ExpName::ExpName(ExpName*prefix, perm_string nn, std::list*indices) +: prefix_(prefix), name_(nn), indices_(indices) { } ExpName::~ExpName() { - delete index_; + if(indices_) { + for(list::iterator it = indices_->begin(); + it != indices_->end(); ++it) { + delete *it; + } + + delete indices_; + } +} + +Expression*ExpName::clone() const { + list*new_indices = NULL; + + if(indices_) { + new_indices = new list(); + + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + new_indices->push_back((*it)->clone()); + } + } + + return new ExpName(static_cast(safe_clone(prefix_.get())), + name_, new_indices); +} + +void ExpName::add_index(std::list*idx) +{ + if(!indices_) + indices_ = new list(); + + indices_->splice(indices_->end(), *idx); } bool ExpName::symbolic_compare(const Expression*that) const @@ -459,28 +689,69 @@ bool ExpName::symbolic_compare(const Expression*that) const if (name_ != that_name->name_) return false; - if (that_name->index_ && !index_) + if (that_name->indices_ && !indices_) return false; - if (index_ && !that_name->index_) + if (indices_ && !that_name->indices_) return false; - if (index_) { - assert(that_name->index_); - return index_->symbolic_compare(that_name->index_); + if (indices_) { + assert(that_name->indices_); + + if(indices_->size() != that_name->indices_->size()) + return false; + + list::const_iterator it, jt; + it = indices_->begin(); + jt = that_name->indices_->begin(); + + for(unsigned int i = 0; i < indices_->size(); ++i) { + if(!(*it)->symbolic_compare(*jt)) + return false; + + ++it; + ++jt; + } } return true; } -void ExpName::set_range(Expression*msb, Expression*lsb) +Expression*ExpName::index(unsigned int number) const { - assert(index_==0); - index_ = msb; - assert(lsb_==0); - lsb_ = lsb; + if(!indices_) + return NULL; + + if(number >= indices_->size()) + return NULL; + + if(number == 0) + return indices_->front(); + + list::const_iterator it = indices_->begin(); + advance(it, number); + + return *it; } -int ExpName::index_t::emit(ostream&out, Entity*ent, ScopeBase*scope) +void ExpName::visit(ExprVisitor&func) +{ + func.down(); + func(this); + + if(prefix_.get()) + prefix_.get()->visit(func); + + if(indices_) { + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + (*it)->visit(func); + } + } + + func.up(); +} + +int ExpName::index_t::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; @@ -511,16 +782,45 @@ ExpRelation::~ExpRelation() { } +ExpScopedName::ExpScopedName(perm_string scope, ExpName*exp) +: scope_name_(scope), scope_(NULL), name_(exp) +{ +} + +ExpScopedName::~ExpScopedName() +{ + delete name_; +} + +void ExpScopedName::visit(ExprVisitor&func) +{ + func.down(); + func(this); + name_->visit(func); + func.up(); +} + +ScopeBase*ExpScopedName::get_scope(const ScopeBase*scope) +{ + if(!scope_) + scope_ = scope->find_scope(scope_name_); + + return scope_; +} + +ScopeBase*ExpScopedName::get_scope(const ScopeBase*scope) const +{ + return scope_ ? scope_ : scope->find_scope(scope_name_); +} + ExpShift::ExpShift(ExpShift::shift_t op, Expression*op1, Expression*op2) : ExpBinary(op1, op2), shift_(op) { } ExpString::ExpString(const char* value) -: value_(strlen(value)) +: value_(value) { - for(size_t idx = 0; idx < value_.size(); idx += 1) - value_[idx] = value[idx]; } ExpString::~ExpString() @@ -545,6 +845,15 @@ ExpUNot::~ExpUNot() { } +ExpUMinus::ExpUMinus(Expression*op1) +: ExpUnary(op1) +{ +} + +ExpUMinus::~ExpUMinus() +{ +} + ExpCast::ExpCast(Expression*base, const VType*type) : base_(base), type_(type) { @@ -554,6 +863,14 @@ ExpCast::~ExpCast() { } +void ExpCast::visit(ExprVisitor&func) +{ + func.down(); + func(this); + base_->visit(func); + func.up(); +} + ExpNew::ExpNew(Expression*size) : size_(size) { @@ -563,3 +880,125 @@ ExpNew::~ExpNew() { delete size_; } + +void ExpNew::visit(ExprVisitor&func) +{ + func.down(); + func(this); + size_->visit(func); + func.up(); +} + +ExpTime::ExpTime(uint64_t amount, timeunit_t unit) +: amount_(amount), unit_(unit) +{ +} + +double ExpTime::to_fs() const +{ + double val = amount_; + + switch(unit_) { + case FS: break; + case PS: val *= 1e3; break; + case NS: val *= 1e6; break; + case US: val *= 1e9; break; + case MS: val *= 1e12; break; + case S: val *= 1e15; break; + default: ivl_assert(*this, false); break; + } + + return val; +} + +ExpRange::ExpRange(Expression*left_idx, Expression*right_idx, range_dir_t dir) +: left_(left_idx), right_(right_idx), direction_(dir), range_expr_(false), + range_base_(NULL) +{ +} + +ExpRange::ExpRange(ExpName*base, bool reverse_range) +: left_(NULL), right_(NULL), direction_(AUTO), range_expr_(true), + range_base_(base), range_reverse_(reverse_range) +{ +} + +ExpRange::~ExpRange() +{ + delete left_; + delete right_; + delete range_base_; +} + +Expression*ExpRange::clone() const +{ + if(range_expr_) + return new ExpRange(static_cast(range_base_->clone()), range_reverse_); + else + return new ExpRange(left_->clone(), right_->clone(), direction_); +} + +Expression* ExpRange::msb() +{ + ivl_assert(*this, direction() != AUTO); + + switch(direction()) { + case DOWNTO: return left_; + case TO: return right_; + default: return NULL; + } + + return NULL; +} + +Expression* ExpRange::lsb() +{ + ivl_assert(*this, direction() != AUTO); + + switch(direction()) { + case DOWNTO: return right_; + case TO: return left_; + default: return NULL; + } + + return NULL; +} + +Expression*ExpRange::left() +{ + if(range_expr_ && !left_) + // TODO check if it is an object or type + left_ = new ExpObjAttribute(static_cast(range_base_->clone()), + ExpAttribute::LEFT, NULL); + + return left_; +} + +Expression*ExpRange::right() +{ + if(range_expr_ && !right_) + // TODO check if it is an object or type + right_ = new ExpObjAttribute(static_cast(range_base_->clone()), + ExpAttribute::RIGHT, NULL); + return right_; +} + +ExpDelay::ExpDelay(Expression*expr, Expression*delay) +: expr_(expr), delay_(delay) +{ +} + +ExpDelay::~ExpDelay() +{ + delete expr_; + delete delay_; +} + +void ExpDelay::visit(ExprVisitor&func) +{ + func.down(); + func(this); + expr_->visit(func); + delay_->visit(func); + func.up(); +} diff --git a/vhdlpp/expression.h b/vhdlpp/expression.h index 7d5f47ee6..3ae6c2bb9 100644 --- a/vhdlpp/expression.h +++ b/vhdlpp/expression.h @@ -1,8 +1,9 @@ #ifndef IVL_expression_H #define IVL_expression_H /* - * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2018 Stephen Williams (steve@icarus.com) * Copyright CERN 2015 / Stephen Williams (steve@icarus.com), + * Copyright CERN 2016 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it @@ -28,16 +29,41 @@ # include # include # include +# include -class prange_t; -class Entity; +class ExpRange; class ScopeBase; -class Subprogram; +class SubprogramHeader; class VType; class VTypeArray; class VTypePrimitive; class ExpName; +#if __cplusplus < 201103L +#define unique_ptr auto_ptr +#endif + +/* + * Helper class to recursively traverse an expression tree + * (i.e. complex expressions). + */ +struct ExprVisitor { + ExprVisitor() : level_(0) {} + virtual ~ExprVisitor() {} + virtual void operator() (Expression*s) = 0; + + // Methods to manage recursion depth. Every Expression::visit() method + // should call down() in the beginning and up() in the end. + inline void down() { ++level_; } + inline void up() { --level_; assert(level_ >= 0); } + +protected: + int level() const { return level_; } + +private: + int level_; +}; + /* * The Expression class represents parsed expressions from the parsed * VHDL input. The Expression class is a virtual class that holds more @@ -92,18 +118,18 @@ class Expression : public LineInfo { // The emit virtual method is called by architecture emit to // output the generated code for the expression. The derived // class fills in the details of what exactly happened. - virtual int emit(ostream&out, Entity*ent, ScopeBase*scope) =0; + virtual int emit(ostream&out, Entity*ent, ScopeBase*scope) const =0; // The emit_package virtual message is similar, but is called // in a package context and to emit SV packages. - virtual int emit_package(std::ostream&out); + virtual int emit_package(std::ostream&out) const; // The evaluate virtual method tries to evaluate expressions // to constant literal values. Return true and set the val // argument if the evaluation works, or return false if it // cannot be done. - virtual bool evaluate(ScopeBase*scope, int64_t&val) const; - virtual bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; + virtual bool evaluate(Entity*, ScopeBase*, int64_t&) const { return false; } + bool evaluate(ScopeBase*scope, int64_t&val) const { return evaluate(NULL, scope, val); } // The symbolic compare returns true if the two expressions // are equal without actually calculating the value. @@ -120,6 +146,9 @@ class Expression : public LineInfo { virtual void dump(ostream&out, int indent = 0) const =0; virtual ostream& dump_inline(ostream&out) const; + // Recursively visits a tree of expressions (useful for complex expressions). + virtual void visit(ExprVisitor& func) { func.down(); func(this); func.up(); } + protected: // This function is called by the derived class during // elaboration to set the type of the current expression that @@ -154,18 +183,21 @@ static inline ostream& operator <<(ostream&out, const Expression&exp) class ExpUnary : public Expression { public: - ExpUnary(Expression*op1); + explicit ExpUnary(Expression*op1); virtual ~ExpUnary() =0; inline const Expression*peek_operand() const { return operand1_; } const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; + const VType*probe_type(Entity*ent, ScopeBase*scope) const; + int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); + void visit(ExprVisitor& func); protected: inline void write_to_stream_operand1(std::ostream&fd) const { operand1_->write_to_stream(fd); } - int emit_operand1(ostream&out, Entity*ent, ScopeBase*scope); + int emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) const; void dump_operand1(ostream&out, int indent = 0) const; private: @@ -186,15 +218,16 @@ class ExpBinary : public Expression { inline const Expression* peek_operand2(void) const { return operand2_; } const VType*probe_type(Entity*ent, ScopeBase*scope) const; + void visit(ExprVisitor& func); protected: int elaborate_exprs(Entity*, ScopeBase*, const VType*); - int emit_operand1(ostream&out, Entity*ent, ScopeBase*scope); - int emit_operand2(ostream&out, Entity*ent, ScopeBase*scope); + int emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) const; + int emit_operand2(ostream&out, Entity*ent, ScopeBase*scope) const; - bool eval_operand1(ScopeBase*scope, int64_t&val) const; - bool eval_operand2(ScopeBase*scope, int64_t&val) const; + bool eval_operand1(Entity*ent, ScopeBase*scope, int64_t&val) const; + bool eval_operand2(Entity*ent, ScopeBase*scope, int64_t&val) const; inline void write_to_stream_operand1(std::ostream&out) const { operand1_->write_to_stream(out); } @@ -227,7 +260,7 @@ class ExpAggregate : public Expression { // Create a named choice explicit choice_t(perm_string name); // discreate_range choice - explicit choice_t(prange_t*ran); + explicit choice_t(ExpRange*ran); choice_t(const choice_t&other); @@ -237,15 +270,15 @@ class ExpAggregate : public Expression { bool others() const; // Return expression if this represents a simple_expression. Expression*simple_expression(bool detach_flag =true); - // Return prange_t if this represents a range_expression - prange_t*range_expressions(void); + // Return ExpRange if this represents a range_expression + ExpRange*range_expressions(void); void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; private: - std::auto_ptrexpr_; - std::auto_ptr range_; + std::unique_ptrexpr_; + std::unique_ptr range_; private: // not implemented choice_t& operator= (const choice_t&); }; @@ -288,23 +321,23 @@ class ExpAggregate : public Expression { }; public: - ExpAggregate(std::list*el); + explicit ExpAggregate(std::list*el); ~ExpAggregate(); Expression*clone() const; - const VType*probe_type(Entity*ent, ScopeBase*scope) const; const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; + void visit(ExprVisitor& func); private: int elaborate_expr_array_(Entity*ent, ScopeBase*scope, const VTypeArray*ltype); int elaborate_expr_record_(Entity*ent, ScopeBase*scope, const VTypeRecord*ltype); - int emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*ltype); - int emit_record_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeRecord*ltype); + int emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*ltype) const; + int emit_record_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeRecord*ltype) const; private: // This is the elements as directly parsed. @@ -330,8 +363,8 @@ class ExpArithmetic : public ExpBinary { int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); - virtual bool evaluate(ScopeBase*scope, int64_t&val) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + virtual bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; private: @@ -344,26 +377,70 @@ class ExpArithmetic : public ExpBinary { class ExpAttribute : public Expression { public: - ExpAttribute(ExpName*base, perm_string name); - ~ExpAttribute(); + ExpAttribute(perm_string name,std::list*args); + virtual ~ExpAttribute(); + + inline perm_string peek_attribute() const { return name_; } + + // Constants for the standard attributes + static const perm_string LEFT; + static const perm_string RIGHT; + + protected: + std::list*clone_args() const; + int elaborate_args(Entity*ent, ScopeBase*scope, const VType*ltype); + void visit_args(ExprVisitor& func); + + bool evaluate_type_attr(const VType*type, Entity*ent, ScopeBase*scope, int64_t&val) const; + bool test_array_type(const VType*type) const; + + perm_string name_; + std::list*args_; +}; + +class ExpObjAttribute : public ExpAttribute { + public: + ExpObjAttribute(ExpName*base, perm_string name, std::list*args); + ~ExpObjAttribute(); Expression*clone() const; - inline perm_string peek_attribute() const { return name_; } inline const ExpName* peek_base() const { return base_; } + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + const VType*probe_type(Entity*ent, ScopeBase*scope) const; + int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); + void write_to_stream(std::ostream&fd) const; + // Some attributes can be evaluated at compile time + bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; + void dump(ostream&out, int indent = 0) const; + void visit(ExprVisitor& func); + + private: + ExpName*base_; +}; + +class ExpTypeAttribute : public ExpAttribute { + public: + ExpTypeAttribute(const VType*base, perm_string name, std::list*args); + // no destructor - VType objects (base_) are shared between many expressions + + Expression*clone() const; + + inline const VType* peek_base() const { return base_; } + + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); // Some attributes can be evaluated at compile time bool evaluate(ScopeBase*scope, int64_t&val) const; bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; + void visit(ExprVisitor& func); private: - ExpName*base_; - perm_string name_; + const VType*base_; }; class ExpBitstring : public Expression { @@ -378,7 +455,7 @@ class ExpBitstring : public Expression { const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; private: @@ -389,7 +466,7 @@ class ExpBitstring : public Expression { class ExpCharacter : public Expression { public: - ExpCharacter(char val); + explicit ExpCharacter(char val); ExpCharacter(const ExpCharacter&other) : Expression() { value_ = other.value_; } ~ExpCharacter(); @@ -398,7 +475,7 @@ class ExpCharacter : public Expression { const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; bool is_primary(void) const; void dump(ostream&out, int indent = 0) const; @@ -406,7 +483,7 @@ class ExpCharacter : public Expression { private: int emit_primitive_bit_(ostream&out, Entity*ent, ScopeBase*scope, - const VTypePrimitive*etype); + const VTypePrimitive*etype) const; private: char value_; @@ -426,10 +503,10 @@ class ExpConcat : public Expression { const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); - virtual bool evaluate(ScopeBase*scope, int64_t&val) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; bool is_primary(void) const; void dump(ostream&out, int indent = 0) const; + void visit(ExprVisitor& func); private: int elaborate_expr_array_(Entity*ent, ScopeBase*scope, const VTypeArray*ltype); @@ -447,16 +524,22 @@ class ExpConcat : public Expression { class ExpConditional : public Expression { public: - class else_t : public LineInfo { + class case_t : public LineInfo { public: - else_t(Expression*cond, std::list*tru); - else_t(const else_t&other); - ~else_t(); + case_t(Expression*cond, std::list*tru); + case_t(const case_t&other); + ~case_t(); + + inline Expression*condition() const { return cond_; } + inline void set_condition(Expression*cond) { cond_ = cond; } + inline const std::list& true_clause() const { return true_clause_; } int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*lt); - int emit_when_else(ostream&out, Entity*ent, ScopeBase*scope); - int emit_else(ostream&out, Entity*ent, ScopeBase*scope); + int emit_option(ostream&out, Entity*ent, ScopeBase*scope) const; + int emit_default(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; + std::list& extract_true_clause() { return true_clause_; } + void visit(ExprVisitor& func); private: Expression*cond_; @@ -465,21 +548,34 @@ class ExpConditional : public Expression { public: ExpConditional(Expression*cond, std::list*tru, - std::list*fal); - ~ExpConditional(); + std::list*options); + virtual ~ExpConditional(); - Expression*clone() const; + virtual Expression*clone() const; const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; + void visit(ExprVisitor& func); + + protected: + std::list options_; +}; + +/* + * Expression to handle selected assignments (with .. select target <= value when ..) + */ +class ExpSelected : public ExpConditional { + public: + ExpSelected(Expression*selector, std::list*options); + ~ExpSelected(); + + Expression*clone() const; private: - Expression*cond_; - std::list true_clause_; - std::list else_clause_; + Expression*selector_; }; /* @@ -500,7 +596,7 @@ class ExpEdge : public ExpUnary { inline fun_t edge_fun() const { return fun_; } void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; private: @@ -526,19 +622,23 @@ class ExpFunc : public Expression { const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; + void visit(ExprVisitor& func); // NOTE: does not handle expressions in subprogram body + + // Returns a subprogram header that matches the function call + SubprogramHeader*match_signature(Entity*ent, ScopeBase*scope) const; private: perm_string name_; std::vector argv_; - Subprogram*def_; + mutable SubprogramHeader*def_; }; class ExpInteger : public Expression { public: - ExpInteger(int64_t val); + explicit ExpInteger(int64_t val); ExpInteger(const ExpInteger&other) : Expression(), value_(other.value_) {} ~ExpInteger(); @@ -547,10 +647,10 @@ class ExpInteger : public Expression { const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); - int emit_package(std::ostream&out); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + int emit_package(std::ostream&out) const; bool is_primary(void) const { return true; } - bool evaluate(ScopeBase*scope, int64_t&val) const; + bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; virtual ostream& dump_inline(ostream&out) const; @@ -561,7 +661,7 @@ class ExpInteger : public Expression { class ExpReal : public Expression { public: - ExpReal(double val); + explicit ExpReal(double val); ExpReal(const ExpReal&other) : Expression(), value_(other.value_) {} ~ExpReal(); @@ -570,8 +670,8 @@ class ExpReal : public Expression { const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); - int emit_package(std::ostream&out); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + int emit_package(std::ostream&out) const; bool is_primary(void) const; void dump(ostream&out, int indent = 0) const; virtual ostream& dump_inline(ostream&out) const; @@ -597,7 +697,7 @@ class ExpLogical : public ExpBinary { int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; private: @@ -614,40 +714,40 @@ class ExpName : public Expression { public: explicit ExpName(perm_string nn); ExpName(perm_string nn, std::list*indices); - ExpName(perm_string nn, Expression*msb, Expression*lsb); - ExpName(ExpName*prefix, perm_string nn); - ExpName(ExpName*prefix, perm_string nn, Expression*msb, Expression*lsb); - ~ExpName(); + ExpName(ExpName*prefix, perm_string nn, std::list*indices = NULL); + virtual ~ExpName(); public: // Base methods - Expression*clone() const { - return new ExpName(static_cast(safe_clone(prefix_.get())), - name_, safe_clone(index_), safe_clone(lsb_)); - } + Expression*clone() const; int elaborate_lval(Entity*ent, ScopeBase*scope, bool); int elaborate_rval(Entity*ent, ScopeBase*scope, const InterfacePort*); const VType* probe_type(Entity*ent, ScopeBase*scope) const; const VType* fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*host) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit_indices(ostream&out, Entity*ent, ScopeBase*scope) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; bool is_primary(void) const; - bool evaluate(ScopeBase*scope, int64_t&val) const; bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; bool symbolic_compare(const Expression*that) const; void dump(ostream&out, int indent = 0) const; inline const char* name() const { return name_; } inline const perm_string& peek_name() const { return name_; } - - void set_range(Expression*msb, Expression*lsb); + void add_index(std::list*idx); + void visit(ExprVisitor& func); private: class index_t { public: index_t(Expression*idx, Expression*size, Expression*offset = NULL) : idx_(idx), size_(size), offset_(offset) {} + ~index_t() { + delete idx_; + delete size_; + delete offset_; + } - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; private: Expression*idx_; @@ -661,14 +761,14 @@ class ExpName : public Expression { const VType* probe_prefix_type_(Entity*ent, ScopeBase*scope) const; const VType* probe_prefixed_type_(Entity*ent, ScopeBase*scope) const; - int emit_as_prefix_(ostream&out, Entity*ent, ScopeBase*scope); + int emit_as_prefix_(ostream&out, Entity*ent, ScopeBase*scope) const; // There are some workarounds required for constant arrays/records, as // they are currently emitted as flat localparams (without any type // information). The following workarounds adjust the access indices // to select appropriate parts of the localparam. bool try_workarounds_(ostream&out, Entity*ent, ScopeBase*scope, - list&indices, int&data_size); + list&indices, int&data_size) const; bool check_const_array_workaround_(const VTypeArray*arr, ScopeBase*scope, list&indices, int&data_size) const; @@ -677,22 +777,22 @@ class ExpName : public Expression { list&indices, int&data_size) const; int emit_workaround_(ostream&out, Entity*ent, ScopeBase*scope, - const list&indices, int field_size); + const list&indices, int field_size) const; private: - std::auto_ptr prefix_; + Expression*index(unsigned int number) const; + + std::unique_ptr prefix_; perm_string name_; - Expression*index_; - Expression*lsb_; + std::list*indices_; }; class ExpNameALL : public ExpName { public: - ExpNameALL() : ExpName(perm_string()) { } + ExpNameALL() : ExpName(empty_perm_string) { } public: - int elaborate_lval(Entity*ent, ScopeBase*scope, bool); const VType* probe_type(Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent =0) const; }; @@ -715,13 +815,71 @@ class ExpRelation : public ExpBinary { const VType* probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; private: fun_t fun_; }; +/* + * Helper class to handle name expressions coming from another scope. As such, + * we get more information regarding their type, etc. from the associated scope. + */ +class ExpScopedName : public Expression { + public: + ExpScopedName(perm_string scope, ExpName*exp); + ~ExpScopedName(); + + Expression*clone() const + { return new ExpScopedName(scope_name_, static_cast(name_->clone())); } + + int elaborate_lval(Entity*ent, ScopeBase*scope, bool is_sequ) + { return name_->elaborate_lval(ent, get_scope(scope), is_sequ); } + + int elaborate_rval(Entity*ent, ScopeBase*scope, const InterfacePort*lval) + { return name_->elaborate_rval(ent, get_scope(scope), lval); } + + const VType* probe_type(Entity*ent, ScopeBase*scope) const + { return name_->probe_type(ent, get_scope(scope)); } + + const VType* fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*host) const + { return name_->fit_type(ent, get_scope(scope), host); } + + int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) + { return name_->elaborate_expr(ent, get_scope(scope), ltype); } + + void write_to_stream(std::ostream&fd) const + { name_->write_to_stream(fd); } + + int emit(ostream&out, Entity*ent, ScopeBase*scope) const { + out << scope_name_ << "."; + return name_->emit(out, ent, scope); + } + + bool is_primary(void) const + { return name_->is_primary(); } + + bool evaluate(Entity*ent, ScopeBase*, int64_t&val) const + { return name_->evaluate(ent, scope_, val); } + + bool symbolic_compare(const Expression*that) const + { return name_->symbolic_compare(that); } + + void dump(ostream&out, int indent = 0) const; + + void visit(ExprVisitor&func); + + private: + // Functions that resolve the origin scope for the name expression + ScopeBase*get_scope(const ScopeBase*scope); + ScopeBase*get_scope(const ScopeBase*scope) const; + + perm_string scope_name_; + ScopeBase*scope_; + ExpName*name_; +}; + class ExpShift : public ExpBinary { public: enum shift_t { SRL, SLL, SRA, SLA, ROL, ROR }; @@ -735,8 +893,8 @@ class ExpShift : public ExpBinary { int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); - virtual bool evaluate(ScopeBase*scope, int64_t&val) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; private: @@ -755,42 +913,58 @@ class ExpString : public Expression { const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; bool is_primary(void) const; void dump(ostream&out, int indent = 0) const; - const std::vector& get_value() const { return value_; } + const std::string& get_value() const { return value_; } + + // Converts quotation marks (") to its escaped + // counterpart in SystemVerilog (\") + static std::string escape_quot(const std::string& str); private: - int emit_as_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*arr); + int emit_as_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*arr) const; private: - std::vector value_; + std::string value_; }; class ExpUAbs : public ExpUnary { public: - ExpUAbs(Expression*op1); + explicit ExpUAbs(Expression*op1); ~ExpUAbs(); Expression*clone() const { return new ExpUAbs(peek_operand()->clone()); } void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; }; class ExpUNot : public ExpUnary { public: - ExpUNot(Expression*op1); + explicit ExpUNot(Expression*op1); ~ExpUNot(); Expression*clone() const { return new ExpUNot(peek_operand()->clone()); } - int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + void dump(ostream&out, int indent = 0) const; +}; + +class ExpUMinus : public ExpUnary { + + public: + explicit ExpUMinus(Expression*op1); + ~ExpUMinus(); + + Expression*clone() const { return new ExpUMinus(peek_operand()->clone()); } + + void write_to_stream(std::ostream&fd) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; }; @@ -809,8 +983,9 @@ class ExpCast : public Expression { return base_->elaborate_expr(ent, scope, type_); } void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; + void visit(ExprVisitor& func); private: Expression*base_; @@ -824,18 +999,106 @@ class ExpCast : public Expression { class ExpNew : public Expression { public: - ExpNew(Expression*size); + explicit ExpNew(Expression*size); ~ExpNew(); Expression*clone() const { return new ExpNew(size_->clone()); } // There is no 'new' in VHDL - do not emit anything void write_to_stream(std::ostream&) const {}; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; + void visit(ExprVisitor& func); private: Expression*size_; }; +class ExpTime : public Expression { + public: + typedef enum { FS, PS, NS, US, MS, S } timeunit_t; + + ExpTime(uint64_t amount, timeunit_t unit); + + Expression*clone() const { return new ExpTime(amount_, unit_); } + + int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); + void write_to_stream(std::ostream&) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + //bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; + void dump(ostream&out, int indent = 0) const; + + private: + // Returns the time value expressed in femtoseconds + double to_fs() const; + uint64_t amount_; + timeunit_t unit_; +}; + +class ExpRange : public Expression { + public: + typedef enum { DOWNTO, TO, AUTO } range_dir_t; + + // Regular range + ExpRange(Expression*left_idx, Expression*right_idx, range_dir_t dir); + // 'range/'reverse range attribute + ExpRange(ExpName*base, bool reverse_range); + ~ExpRange(); + + Expression*clone() const; + + // Returns the upper boundary + Expression*msb(); + // Returns the lower boundary + Expression*lsb(); + + Expression*left(); + Expression*right(); + + range_dir_t direction() const { return direction_; } + + int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); + void write_to_stream(std::ostream&) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + void dump(ostream&out, int indent = 0) const; + private: + // Regular range related fields + Expression*left_, *right_; + range_dir_t direction_; + + // 'range/'reverse_range attribute related fields + // Flag to indicate it is a 'range/'reverse_range expression + bool range_expr_; + // Object name to which the attribute is applied + ExpName*range_base_; + // Flag to distinguish between 'range & 'reverse_range + bool range_reverse_; +}; + +// Helper class that wraps other expression to specify delay. +class ExpDelay : public Expression { +public: + ExpDelay(Expression*expr, Expression*delay); + ~ExpDelay(); + + Expression*clone() const { return new ExpDelay(expr_->clone(), delay_->clone()); } + + int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); + void write_to_stream(std::ostream&) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + void dump(ostream&out, int indent = 0) const; + void visit(ExprVisitor& func); + + const Expression*peek_expr() const { return expr_; } + const Expression*peek_delay() const { return delay_; } + +private: + Expression*expr_; + Expression*delay_; +}; + +#if __cplusplus < 201103L +#undef unique_ptr +#endif + #endif /* IVL_expression_H */ diff --git a/vhdlpp/expression_debug.cc b/vhdlpp/expression_debug.cc index 1a7b44e8c..cf6a46902 100644 --- a/vhdlpp/expression_debug.cc +++ b/vhdlpp/expression_debug.cc @@ -71,7 +71,7 @@ void ExpConcat::dump(ostream&out, int indent) const void ExpCast::dump(ostream&out, int indent) const { - out << "Casting "; + out << setw(indent) << "" << "Casting "; base_->dump(out, indent+4); out << " to "; type_->emit_def(out, empty_perm_string); @@ -79,10 +79,17 @@ void ExpCast::dump(ostream&out, int indent) const void ExpNew::dump(ostream&out, int indent) const { - out << "New dynamic array size: "; + out << setw(indent) << "" << "New dynamic array size: " << endl; size_->dump(out, indent); } +void ExpScopedName::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "Scoped name expression: " << endl; + out << " scope " << scope_name_ << " " << scope_ << endl; + name_->dump(out, indent+4); +} + void ExpShift::dump(ostream&out, int indent) const { const char*fun_name = "?"; @@ -112,3 +119,51 @@ void ExpShift::dump(ostream&out, int indent) const dump_operands(out, indent+4); } +void ExpString::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "String \"" << value_; + out << "\"" << " at " << get_fileline() << endl; +} + +void ExpUAbs::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "abs() at " << get_fileline() << endl; + dump_operand1(out, indent+4); +} + +void ExpUnary::dump_operand1(ostream&out, int indent) const +{ + operand1_->dump(out, indent); +} + +void ExpUNot::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "not() at " << get_fileline() << endl; + dump_operand1(out, indent+4); +} + +void ExpUMinus::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "unary_minus() at " << get_fileline() << endl; + dump_operand1(out, indent+4); +} + +void ExpTime::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "Time "; + write_to_stream(out); +} + +void ExpRange::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "Range "; + write_to_stream(out); +} + +void ExpDelay::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "Expression "; + expr_->write_to_stream(out); + out << " delayed by "; + delay_->write_to_stream(out); +} diff --git a/vhdlpp/expression_elaborate.cc b/vhdlpp/expression_elaborate.cc index 30ddd4e2f..97405ca0b 100644 --- a/vhdlpp/expression_elaborate.cc +++ b/vhdlpp/expression_elaborate.cc @@ -1,6 +1,8 @@ /* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com) + * Copyright CERN 2016 + * @author Maciej Suminski (maciej.suminski@cern.ch) * * 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,7 +26,7 @@ # include "entity.h" # include "vsignal.h" # include "subprogram.h" -# include "library.h" +# include "std_types.h" # include # include # include "parse_types.h" @@ -56,7 +58,8 @@ const VType* Expression::fit_type(Entity*ent, ScopeBase*scope, const VTypeArray* return res; } -const VType*ExpName::elaborate_adjust_type_with_range_(Entity*, ScopeBase*scope, const VType*type) +const VType*ExpName::elaborate_adjust_type_with_range_(Entity*ent, ScopeBase*scope, + const VType*type) { // Unfold typedefs while (const VTypeDef*tdef = dynamic_cast(type)) { @@ -64,23 +67,24 @@ const VType*ExpName::elaborate_adjust_type_with_range_(Entity*, ScopeBase*scope, } if (const VTypeArray*array = dynamic_cast(type)) { - if (index_ && !lsb_) { - // If the name is an array or a vector, then an - // indexed name has the type of the element. - type = array->element_type(); + Expression*idx = index(0); - } else if (index_ && lsb_) { + if (ExpRange*range = dynamic_cast(idx)) { // If the name is an array, then a part select is // also an array, but with different bounds. int64_t use_msb, use_lsb; - bool flag; + bool flag = true; - flag = index_->evaluate(scope, use_msb); - ivl_assert(*this, flag); - flag = lsb_->evaluate(scope, use_lsb); - ivl_assert(*this, flag); + flag &= range->msb()->evaluate(ent, scope, use_msb); + flag &= range->lsb()->evaluate(ent, scope, use_lsb); - type = new VTypeArray(array->element_type(), use_msb, use_lsb); + if(flag) + type = new VTypeArray(array->element_type(), use_msb, use_lsb); + } + else if(idx) { + // If the name is an array or a vector, then an + // indexed name has the type of the element. + type = array->element_type(); } } @@ -95,10 +99,14 @@ int ExpName::elaborate_lval_(Entity*ent, ScopeBase*scope, bool is_sequ, ExpName* debug_log_file << get_fileline() << ": ExpName::elaborate_lval_: " << "name_=" << name_ << ", suffix->name()=" << suffix->name(); - if (index_) - debug_log_file << ", index_=" << *index_; - if (lsb_) - debug_log_file << ", lsb_=" << *lsb_; + if (indices_) { + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + debug_log_file << "["; + debug_log_file << **it; + debug_log_file << "]"; + } + } debug_log_file << endl; } @@ -112,7 +120,7 @@ int ExpName::elaborate_lval_(Entity*ent, ScopeBase*scope, bool is_sequ, ExpName* const VType*found_type = 0; if (const InterfacePort*cur = ent->find_port(name_)) { - if (cur->mode != PORT_OUT) { + if (cur->mode != PORT_OUT && cur->mode != PORT_INOUT) { cerr << get_fileline() << ": error: Assignment to " "input port " << name_ << "." << endl; return errors + 1; @@ -215,36 +223,44 @@ int ExpName::elaborate_lval(Entity*ent, ScopeBase*scope, bool is_sequ) const VType*found_type = 0; - if (const InterfacePort*cur = ent->find_port(name_)) { - if (cur->mode != PORT_OUT) { - cerr << get_fileline() << ": error: Assignment to " - "input port " << name_ << "." << endl; - return errors += 1; - } + if (ent) { + if (const InterfacePort*cur = ent->find_port(name_)) { + if (cur->mode != PORT_OUT && cur->mode != PORT_INOUT) { + cerr << get_fileline() << ": error: Assignment to " + "input port " << name_ << "." << endl; + return errors += 1; + } - if (is_sequ) - ent->set_declaration_l_value(name_, is_sequ); + if (is_sequ) + ent->set_declaration_l_value(name_, is_sequ); - found_type = cur->type; + found_type = cur->type; - } else if (ent->find_generic(name_)) { + } else if (ent->find_generic(name_)) { - cerr << get_fileline() << ": error: Assignment to generic " - << name_ << " from entity " - << ent->get_name() << "." << endl; - return 1; + cerr << get_fileline() << ": error: Assignment to generic " + << name_ << " from entity " + << ent->get_name() << "." << endl; + return 1; + } + } - } else if (Signal*sig = scope->find_signal(name_)) { - // Tell the target signal that this may be a sequential l-value. - if (is_sequ) sig->count_ref_sequ(); + if (!found_type && scope) { + if (Signal*sig = scope->find_signal(name_)) { + // Tell the target signal that this may be a sequential l-value. + if (is_sequ) sig->count_ref_sequ(); - found_type = sig->peek_type(); + found_type = sig->peek_type(); - } else if (Variable*var = scope->find_variable(name_)) { - // Tell the target signal that this may be a sequential l-value. - if (is_sequ) var->count_ref_sequ(); + } else if (Variable*var = scope->find_variable(name_)) { + // Tell the target signal that this may be a sequential l-value. + if (is_sequ) var->count_ref_sequ(); - found_type = var->peek_type(); + found_type = var->peek_type(); + + } else if (const InterfacePort*port = scope->find_param(name_)) { + found_type = port->type; + } } if (found_type == 0) { @@ -303,6 +319,9 @@ int ExpName::elaborate_rval(Entity*ent, ScopeBase*scope, const InterfacePort*lva } else if (scope->find_constant(name_, dummy_type, dummy_expr)) { /* OK */ + } else if (scope->is_enum_name(name_)) { + /* OK */ + } else { cerr << get_fileline() << ": error: No port, signal or constant " << name_ << " to be used as r-value." << endl; @@ -312,14 +331,10 @@ int ExpName::elaborate_rval(Entity*ent, ScopeBase*scope, const InterfacePort*lva return errors; } -int ExpNameALL::elaborate_lval(Entity*ent, ScopeBase*scope, bool is_sequ) -{ - return Expression::elaborate_lval(ent, scope, is_sequ); -} - int Expression::elaborate_expr(Entity*, ScopeBase*, const VType*) { - cerr << get_fileline() << ": internal error: I don't know how to elaborate expression type=" << typeid(*this).name() << endl; + cerr << get_fileline() << ": internal error: I don't know how to " + << "elaborate expression type=" << typeid(*this).name() << endl; return 1; } @@ -333,8 +348,10 @@ const VType* ExpBinary::probe_type(Entity*ent, ScopeBase*scope) const if (t2 == 0) return t1; - if (t1 == t2) + if (t1->type_match(t2)) return t1; + if (t2->type_match(t1)) + return t2; if (const VType*tb = resolve_operand_types_(t1, t2)) return tb; @@ -372,9 +389,16 @@ const VType*ExpUnary::fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*aty return operand1_->fit_type(ent, scope, atype); } -const VType*ExpAggregate::probe_type(Entity*ent, ScopeBase*scope) const +const VType*ExpUnary::probe_type(Entity*ent, ScopeBase*scope) const { - return Expression::probe_type(ent, scope); + return operand1_->probe_type(ent, scope); +} + +int ExpUnary::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) +{ + ivl_assert(*this, ltype != 0); + set_type(ltype); + return operand1_->elaborate_expr(ent, scope, ltype); } const VType*ExpAggregate::fit_type(Entity*, ScopeBase*, const VTypeArray*host) const @@ -387,13 +411,13 @@ const VType*ExpAggregate::fit_type(Entity*, ScopeBase*, const VTypeArray*host) c elements_[0]->map_choices(&ce[0]); ivl_assert(*this, ce.size() == 1); - prange_t*prange = ce[0].choice->range_expressions(); + ExpRange*prange = ce[0].choice->range_expressions(); ivl_assert(*this, prange); Expression*use_msb = prange->msb(); Expression*use_lsb = prange->lsb(); - ivl_assert(*this, host->dimensions() == 1); + ivl_assert(*this, host->dimensions().size() == 1); vector range (1); range[0] = VTypeArray::range_t(use_msb, use_lsb); @@ -554,6 +578,7 @@ int ExpArithmetic::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype const VType* ExpArithmetic::resolve_operand_types_(const VType*t1, const VType*t2) const { + // Ranges while (const VTypeRange*tmp = dynamic_cast (t1)) t1 = tmp->base_type(); while (const VTypeRange*tmp = dynamic_cast (t2)) @@ -561,31 +586,107 @@ const VType* ExpArithmetic::resolve_operand_types_(const VType*t1, const VType*t if (t1->type_match(t2)) return t1; - if (t2->type_match(t2)) - return t2; - return 0; + // Signed & unsigned (resized to the widest argument) + const VTypeArray*t1_arr = dynamic_cast(t1); + const VTypeArray*t2_arr = dynamic_cast(t2); + + if(t1_arr && t2_arr) { + const VTypeArray*t1_parent = t1_arr->get_parent_type(); + const VTypeArray*t2_parent = t2_arr->get_parent_type(); + + if(t1_parent == t2_parent + && (t1_parent == &primitive_SIGNED || t1_parent == &primitive_UNSIGNED)) { + int t1_size = t1_arr->get_width(NULL); + int t2_size = t2_arr->get_width(NULL); + + // Easy, the same sizes, so we do not need to resize + if(t1_size == t2_size && t1_size > 0) + return t1; // == t2 + + VTypeArray*resolved = new VTypeArray(t1_parent->element_type(), + std::max(t1_size, t2_size) - 1, 0, t1_parent->signed_vector()); + resolved->set_parent_type(t1_parent); + + return resolved; + } + + } else if(t1_arr) { + if(const VTypePrimitive*prim = dynamic_cast(t2)) { + const VTypeArray*t1_parent = t1_arr->get_parent_type(); + VTypePrimitive::type_t t2_type = prim->type(); + + if((t2_type == VTypePrimitive::NATURAL || t2_type == VTypePrimitive::INTEGER) + && t1_parent == &primitive_SIGNED) + return t1; + + if((t2_type == VTypePrimitive::NATURAL) && t1_parent == &primitive_UNSIGNED) + return t1; + } + + } else if(t2_arr) { + if(const VTypePrimitive*prim = dynamic_cast(t1)) { + const VTypeArray*t2_parent = t2_arr->get_parent_type(); + VTypePrimitive::type_t t1_type = prim->type(); + + if((t1_type == VTypePrimitive::NATURAL || t1_type == VTypePrimitive::INTEGER) + && t2_parent == &primitive_SIGNED) + return t2; + + if((t1_type == VTypePrimitive::NATURAL) && t2_parent == &primitive_UNSIGNED) + return t2; + } + } + + return 0; } -const VType* ExpAttribute::probe_type(Entity*ent, ScopeBase*scope) const +int ExpAttribute::elaborate_args(Entity*ent, ScopeBase*scope, const VType*ltype) { - base_->probe_type(ent, scope); + int errors = 0; - if (name_ == "length" || name_ == "left" || name_ == "right") { - return &primitive_NATURAL; + if(args_) { + for(list::iterator it = args_->begin(); + it != args_->end(); ++it) { + errors += (*it)->elaborate_expr(ent, scope, ltype); + } } - return 0; + return errors; } -int ExpAttribute::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*) +int ExpObjAttribute::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*) { int errors = 0; const VType*sub_type = base_->probe_type(ent, scope); + + errors += elaborate_args(ent, scope, sub_type); errors += base_->elaborate_expr(ent, scope, sub_type); + return errors; } +const VType* ExpObjAttribute::probe_type(Entity*, ScopeBase*) const +{ + if (name_ == "length" || name_ == "left" || name_ == "right") + return &primitive_NATURAL; + + return NULL; +} + +int ExpTypeAttribute::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) +{ + return elaborate_args(ent, scope, ltype); +} + +const VType* ExpTypeAttribute::probe_type(Entity*, ScopeBase*) const +{ + if(name_ == "image") + return &primitive_STRING; + + return NULL; +} + const VType*ExpBitstring::fit_type(Entity*, ScopeBase*, const VTypeArray*atype) const { // Really should check that this string can work with the @@ -627,7 +728,7 @@ const VType*ExpConcat::fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*at if(const VTypeArray*arr = dynamic_cast(types[i])) { types[i] = arr->element_type(); - ivl_assert(*this, arr->dimensions() == 1); + ivl_assert(*this, arr->dimensions().size() == 1); const VTypeArray::range_t&dim = arr->dimension(0); sizes[i] = new ExpArithmetic(ExpArithmetic::MINUS, dim.msb(), dim.lsb()); } else { @@ -642,8 +743,8 @@ const VType*ExpConcat::fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*at new ExpArithmetic(ExpArithmetic::PLUS, sizes[0], sizes[1]), new ExpInteger(1)); - std::list ranges; - ranges.push_front(new prange_t(size, new ExpInteger(0), true)); + std::list ranges; + ranges.push_front(new ExpRange(size, new ExpInteger(0), ExpRange::DOWNTO)); const VType*array = new VTypeArray(types[1], &ranges); return array; @@ -682,7 +783,7 @@ int ExpConcat::elaborate_expr_array_(Entity*ent, ScopeBase*scope, const VTypeArr int errors = 0; // For now, only support single-dimension arrays here. - ivl_assert(*this, atype->dimensions() == 1); + ivl_assert(*this, atype->dimensions().size() == 1); const VType*type1 = operand1_->fit_type(ent, scope, atype); ivl_assert(*this, type1); @@ -714,22 +815,16 @@ int ExpConditional::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltyp /* Note that the type for the condition expression need not have anything to do with the type of this expression. */ - errors += cond_->elaborate_expr(ent, scope, 0); - for (list::const_iterator cur = true_clause_.begin() - ; cur != true_clause_.end() ; ++cur) { - errors += (*cur)->elaborate_expr(ent, scope, ltype); - } - - for (list::const_iterator cur = else_clause_.begin() - ; cur != else_clause_.end() ; ++cur) { + for (list::const_iterator cur = options_.begin() + ; cur != options_.end() ; ++cur) { errors += (*cur)->elaborate_expr(ent, scope, ltype); } return errors; } -int ExpConditional::else_t::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) +int ExpConditional::case_t::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { int errors = 0; @@ -746,81 +841,35 @@ int ExpConditional::else_t::elaborate_expr(Entity*ent, ScopeBase*scope, const VT const VType*ExpFunc::probe_type(Entity*ent, ScopeBase*scope) const { - if(name_ == "integer") - return &primitive_INTEGER; + if(!def_) + def_ = match_signature(ent, scope); - if(name_ == "unsigned" || name_ == "resize") { - if(argv_.empty()) - return NULL; - - const VType*type = argv_[0]->probe_type(ent, scope); - if(!type) - return NULL; - - int msb = type->get_width(scope) - 1; - ivl_assert(*this, msb >= 0); - - // Determine the sign - bool sign = false; - if(name_ == "resize") { - if(const VTypeArray*arr = dynamic_cast(type)) - sign = arr->signed_vector(); - } - - return new VTypeArray(&primitive_BIT, msb, 0, sign); - } - - if(name_ == "std_logic_vector" || name_ == "conv_std_logic_vector") { - if(argv_.empty()) - return NULL; - - const VType*type = argv_[0]->probe_type(ent, scope); - if(!type) - return NULL; - - int msb = type->get_width(scope) - 1; - - return new VTypeArray(&primitive_STDLOGIC, msb, 0); - } - - Subprogram*prog = scope->find_subprogram(name_); - - if(!prog) - prog = library_find_subprogram(name_); - - if(!prog) - return NULL; - - return prog->peek_return_type(); + return def_ ? def_->exact_return_type(argv_, ent, scope) : NULL; } int ExpFunc::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*) { int errors = 0; - ivl_assert(*this, scope); - Subprogram*prog = scope->find_subprogram(name_); + if(def_) + return 0; - if(!prog) - prog = library_find_subprogram(name_); + def_ = match_signature(ent, scope); - ivl_assert(*this, def_==0); - def_ = prog; + if(!def_) + return 1; // Elaborate arguments - for (size_t idx = 0 ; idx < argv_.size() ; idx += 1) { - const VType*tmp = argv_[idx]->probe_type(ent, scope); - const VType*param_type = prog ? prog->peek_param_type(idx) : NULL; - - if(!tmp && param_type) - tmp = param_type; - - errors += argv_[idx]->elaborate_expr(ent, scope, tmp); + for (size_t idx = 0; idx < argv_.size(); ++idx) { + errors += def_->elaborate_argument(argv_[idx], idx, ent, scope); } - if(def_ && def_->unbounded()) { - def_ = prog->make_instance(argv_, scope); - name_ = def_->name(); + // SystemVerilog functions work only with defined size data types, therefore + // if header does not specify argument or return type size, create a function + // instance that work with this particular size. + if(def_ && !def_->is_std() && def_->unbounded()) { + def_ = def_->make_instance(argv_, scope); + name_ = def_->name(); // TODO necessary? } return errors; @@ -828,74 +877,15 @@ int ExpFunc::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*) const VType* ExpFunc::fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*) const { - // Built-in functions - if(name_ == "to_integer" || name_ == "unsigned" || name_ == "integer") { - ivl_assert(*this, argv_.size() == 1); - - const VType*type = argv_[0]->probe_type(ent, scope); - ivl_assert(*this, type); - - // Determine the sign - bool sign = false; - - if(name_ == "integer") { - sign = true; - } else if(name_ == "to_integer") { - if(const VTypeArray*arr = dynamic_cast(type)) - sign = arr->signed_vector(); - } - - return new VTypeArray(&primitive_BIT, type->get_width(scope), 0, sign); - } - - if(name_ == "to_unsigned" || name_ == "std_logic_vector" || - name_ == "conv_std_logic_vector" || name_ == "resize") - { - ivl_assert(*this, argv_.size() == 2); - - // Determine the sign - bool sign = false; - const VType*element = &primitive_STDLOGIC; - - if(name_ == "resize") { - const VType*type = argv_[0]->probe_type(ent, scope); - ivl_assert(*this, type); - - if(const VTypeArray*arr = dynamic_cast(type)) - { - sign = arr->signed_vector(); - element = arr->element_type(); - } - } else if(name_ == "to_unsigned") { - element = &primitive_BIT; - } - - int64_t width = 0; - bool evaluated = argv_[1]->evaluate(scope, width); - ivl_assert(*this, evaluated); - - return new VTypeArray(element, width, 0, sign); - } - - // Other cases - Subprogram*prog = def_; - - if(!prog) { - ivl_assert(*this, scope); - prog = scope->find_subprogram(name_); - } - - if(!prog) - prog = library_find_subprogram(name_); - - ivl_assert(*this, prog); - - return def_->peek_return_type(); + return probe_type(ent, scope); } const VType* ExpInteger::probe_type(Entity*, ScopeBase*) const { - return &primitive_INTEGER; + if(value_ >= 0) + return &primitive_NATURAL; + else + return &primitive_INTEGER; } int ExpInteger::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) @@ -954,46 +944,55 @@ const VType* ExpName::probe_prefix_type_(Entity*ent, ScopeBase*scope) const } /* - * This method is the probe_type() imlementation for ExpName objects + * This method is the probe_type() implementation for ExpName objects * that have prefix parts. In this case we try to get the type of the * prefix and interpret the name in that context. */ const VType* ExpName::probe_prefixed_type_(Entity*ent, ScopeBase*scope) const { - // First, get the type of the prefix. + // First, get the type of the prefix. const VType*prefix_type = prefix_->probe_prefix_type_(ent, scope); if (prefix_type == 0) { - return 0; + return 0; } while (const VTypeDef*def = dynamic_cast (prefix_type)) { - prefix_type = def->peek_definition(); + prefix_type = def->peek_definition(); } - // If the prefix type is a record, then the current name is - // the name of a member. - if (const VTypeRecord*pref_record = dynamic_cast (prefix_type)) { - const VTypeRecord::element_t*element = pref_record->element_by_name(name_); - ivl_assert(*this, element); + const VType*element_type = prefix_type; + bool type_changed = true; - const VType*element_type = element->peek_type(); - ivl_assert(*this, element_type); + // Keep unwinding the type until we find the basic element type + while (type_changed) { + type_changed = false; - return element_type; + // If the prefix type is a record, then the current name is + // the name of a member. + if (const VTypeRecord*pref_record = dynamic_cast(element_type)) { + const VTypeRecord::element_t*element = pref_record->element_by_name(name_); + ivl_assert(*this, element); + + element_type = element->peek_type(); + ivl_assert(*this, element_type); + type_changed = true; + } + + if (const VTypeArray*pref_array = dynamic_cast(element_type)) { + element_type = pref_array->basic_type(false); + ivl_assert(*this, element_type); + type_changed = true; + } } - if (const VTypeArray*pref_array = dynamic_cast (prefix_type)) { - const VType*element_type = pref_array->element_type(); - ivl_assert(*this, element_type); - - return element_type; + if(!element_type) { + cerr << get_fileline() << ": sorry: I don't know how to probe " + << "prefix type " << typeid(*prefix_type).name() + << " of " << name_ << "." << endl; + return NULL; } - cerr << get_fileline() << ": sorry: I don't know how to probe " - << "prefix type " << typeid(*prefix_type).name() - << " of " << name_ << "." << endl; - - return 0; + return element_type; } const VType* ExpName::probe_type(Entity*ent, ScopeBase*scope) const @@ -1003,41 +1002,50 @@ const VType* ExpName::probe_type(Entity*ent, ScopeBase*scope) const if(ent) { if (const InterfacePort*cur = ent->find_port(name_)) { - ivl_assert(*this, cur->type); - return cur->type; + ivl_assert(*this, cur->type); + return cur->type; } if (const InterfacePort*cur = ent->find_generic(name_)) { - ivl_assert(*this, cur->type); - return cur->type; + ivl_assert(*this, cur->type); + return cur->type; } } if(scope) { if (Signal*sig = scope->find_signal(name_)) - return sig->peek_type(); + return sig->peek_type(); if (Variable*var = scope->find_variable(name_)) - return var->peek_type(); + return var->peek_type(); - const VType*ctype = 0; + const VType*type = 0; Expression*cval = 0; - if (scope->find_constant(name_, ctype, cval)) - return ctype; + if (scope->find_constant(name_, type, cval)) + return type; - const VType*gtype = 0; Architecture*arc = dynamic_cast(scope); - if (arc && (gtype = arc->probe_genvar_type(name_))) { - return gtype; + if (arc && (type = arc->probe_genvar_type(name_))) { + return type; } - if (scope->is_enum_name(name_)) { - return &primitive_INTEGER; + if (const InterfacePort*port = scope->find_param(name_)) { + return port->type; + } + + if ((type = scope->is_enum_name(name_))) { + return type; } } - cerr << get_fileline() << ": error: Signal/variable " << name_ - << " not found in this context." << endl; + if(ent || scope) { + // Do not display error messages if there was no entity or scope + // specified. There are functions that are called without any specific + // context and they still may want to probe the expression type. + cerr << get_fileline() << ": error: Signal/variable " << name_ + << " not found in this context." << endl; + } + return 0; } @@ -1046,13 +1054,23 @@ const VType* ExpName::fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*)co return probe_type(ent, scope); } -int ExpName::elaborate_expr(Entity*, ScopeBase*, const VType*ltype) +int ExpName::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { if (ltype) { ivl_assert(*this, ltype != 0); set_type(ltype); } + if(prefix_.get()) + prefix_.get()->elaborate_expr(ent, scope, NULL); + + if (indices_) { + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + (*it)->elaborate_expr(ent, scope, &primitive_INTEGER); + } + } + return 0; } @@ -1063,7 +1081,7 @@ const VType* ExpNameALL::probe_type(Entity*, ScopeBase*) const const VType* ExpRelation::probe_type(Entity*, ScopeBase*) const { - return &primitive_BOOLEAN; + return &type_BOOLEAN; } int ExpRelation::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) @@ -1121,9 +1139,31 @@ int ExpString::elaborate_expr(Entity*, ScopeBase*, const VType*ltype) return 0; } -int ExpUNot::elaborate_expr(Entity*, ScopeBase*, const VType*ltype) +int ExpTime::elaborate_expr(Entity*, ScopeBase*, const VType*) { - ivl_assert(*this, ltype != 0); - set_type(ltype); + set_type(&primitive_INTEGER); return 0; } + +int ExpRange::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*) +{ + int errors = 0; + + if(left_) + errors += left_->elaborate_expr(ent, scope, &primitive_INTEGER); + + if(right_) + errors += right_->elaborate_expr(ent, scope, &primitive_INTEGER); + + return errors; +} + +int ExpDelay::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) +{ + int errors = 0; + + errors += expr_->elaborate_expr(ent, scope, ltype); + errors += delay_->elaborate_expr(ent, scope, ltype); + + return errors; +} diff --git a/vhdlpp/expression_emit.cc b/vhdlpp/expression_emit.cc index 39e853a71..a3d6e77e0 100644 --- a/vhdlpp/expression_emit.cc +++ b/vhdlpp/expression_emit.cc @@ -23,7 +23,8 @@ # include "vtype.h" # include "architec.h" # include "package.h" -# include "subprogram.h" +# include "std_funcs.h" +# include "std_types.h" # include "parse_types.h" # include # include @@ -34,7 +35,34 @@ using namespace std; -int Expression::emit(ostream&out, Entity*, ScopeBase*) +inline static int emit_logic(char val, ostream& out, const VTypePrimitive::type_t type) +{ +// TODO case 'W': case 'L': case 'H': + + switch (val) { + case '-': case 'U': + val = 'x'; + /* fall through */ + + case 'X': case 'Z': + assert(type == VTypePrimitive::STDLOGIC); + /* fall through */ + + case '0': + case '1': + out << (char) tolower(val); + break; + + default: + assert(false); + out << "x"; + return 1; + } + + return 0; +} + +int Expression::emit(ostream&out, Entity*, ScopeBase*) const { out << " /* " << get_fileline() << ": internal error: " << "I don't know how to emit this expression! " @@ -42,7 +70,7 @@ int Expression::emit(ostream&out, Entity*, ScopeBase*) return 1; } -int Expression::emit_package(ostream&out) +int Expression::emit_package(ostream&out) const { out << " /* " << get_fileline() << ": internal error: " << "I don't know how to emit_package this expression! " @@ -55,7 +83,7 @@ bool Expression::is_primary(void) const return false; } -int ExpBinary::emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) +int ExpBinary::emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; bool oper_primary = operand1_->is_primary(); @@ -65,7 +93,7 @@ int ExpBinary::emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) return errors; } -int ExpBinary::emit_operand2(ostream&out, Entity*ent, ScopeBase*scope) +int ExpBinary::emit_operand2(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; bool oper_primary = operand2_->is_primary(); @@ -75,14 +103,14 @@ int ExpBinary::emit_operand2(ostream&out, Entity*ent, ScopeBase*scope) return errors; } -int ExpUnary::emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) +int ExpUnary::emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; errors += operand1_->emit(out, ent, scope); return errors; } -int ExpAggregate::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpAggregate::emit(ostream&out, Entity*ent, ScopeBase*scope) const { if (peek_type() == 0) { out << "/* " << get_fileline() << ": internal error: " @@ -106,13 +134,13 @@ int ExpAggregate::emit(ostream&out, Entity*ent, ScopeBase*scope) return 1; } -int ExpAggregate::emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*atype) +int ExpAggregate::emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*atype) const { int errors = 0; // Special case: The aggregate is a single "others" item. if (aggregate_.size() == 1 && aggregate_[0].choice->others()) { - assert(atype->dimensions() == 1); + assert(atype->dimensions().size() == 1); const VTypeArray::range_t&rang = atype->dimension(0); assert(! rang.is_box()); @@ -166,8 +194,8 @@ int ExpAggregate::emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VT if(use_msb < use_lsb) swap(use_msb, use_lsb); - map element_map; - choice_element*element_other = 0; + map element_map; + const choice_element*element_other = 0; bool positional_section = true; int64_t positional_idx = use_msb; @@ -196,7 +224,7 @@ int ExpAggregate::emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VT // If this is a range choice, then calculate the bounds // of the range and scan through the values, mapping the // value to the aggregate_[idx] element. - if (prange_t*range = aggregate_[idx].choice->range_expressions()) { + if (ExpRange*range = aggregate_[idx].choice->range_expressions()) { int64_t begin_val, end_val; if (! range->msb()->evaluate(ent, scope, begin_val)) { @@ -255,7 +283,7 @@ int ExpAggregate::emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VT out << "{"; for (int64_t idx = use_msb ; idx >= use_lsb ; idx -= 1) { - choice_element*cur = element_map[idx]; + const choice_element*cur = element_map[idx]; if (cur == 0) cur = element_other; @@ -275,7 +303,7 @@ int ExpAggregate::emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VT return errors; } -int ExpAggregate::emit_record_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeRecord*) +int ExpAggregate::emit_record_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeRecord*) const { int errors = 0; @@ -303,13 +331,13 @@ int ExpAggregate::emit_record_(ostream&out, Entity*ent, ScopeBase*scope, const V return errors; } -int ExpAttribute::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpObjAttribute::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; // Try to evaluate first int64_t val; - if(evaluate(scope, val)) { + if(evaluate(ent, scope, val)) { out << val; return 0; } @@ -337,16 +365,70 @@ int ExpAttribute::emit(ostream&out, Entity*ent, ScopeBase*scope) return errors; } + // Fallback out << "$ivl_attribute("; errors += base_->emit(out, ent, scope); out << ", \"" << name_ << "\")"; return errors; } -int ExpArithmetic::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpTypeAttribute::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; + // Special case: The image attribute + if (name_=="image") { + if(!args_ || args_->size() != 1) { + out << "/* Invalid 'image attribute */" << endl; + cerr << get_fileline() << ": error: 'image attribute takes " + << "exactly one argument." << endl; + ++errors; + } else { + out << "$sformatf(\""; + + if(base_->type_match(&primitive_INTEGER)) + out << "%0d"; + else if(base_->type_match(&primitive_REAL)) + out << "%f"; + else if(base_->type_match(&primitive_CHARACTER)) + out << "'%c'"; + else if(base_->type_match(&primitive_TIME)) + out << "%+0t"; + + out << "\","; + args_->front()->emit(out, ent, scope); + out << ")"; + } + return errors; + } + + // Fallback + out << "$ivl_attribute("; + errors += base_->emit_def(out, empty_perm_string); + out << ", \"" << name_ << "\")"; + return errors; +} + +int ExpArithmetic::emit(ostream&out, Entity*ent, ScopeBase*scope) const +{ + int errors = 0; + + if(fun_ == REM) { + // Special case: division remainder, defined in the VHDL standard 1076-2008/9.2.7 + // there is no direct counterpart, therefore output the formula to + // compute a remainder: A rem B = A - (A/B) * B; + out << "(("; + errors += emit_operand1(out, ent, scope); + out << ")-(("; + errors += emit_operand1(out, ent, scope); + out << ")/("; + errors += emit_operand2(out, ent, scope); + out << "))*("; + errors += emit_operand2(out, ent, scope); + out << "))"; + return errors; + } + errors += emit_operand1(out, ent, scope); switch (fun_) { @@ -368,9 +450,8 @@ int ExpArithmetic::emit(ostream&out, Entity*ent, ScopeBase*scope) case POW: out << " ** "; break; - case REM: - out << " /* ?remainder? */ "; - break; + case REM: // should not happen as it is handled above, suppress warnings + ivl_assert(*this, 0); case xCONCAT: ivl_assert(*this, 0); out << " /* ?concat? */ "; @@ -382,7 +463,7 @@ int ExpArithmetic::emit(ostream&out, Entity*ent, ScopeBase*scope) return errors; } -int ExpBitstring::emit(ostream&out, Entity*, ScopeBase*) +int ExpBitstring::emit(ostream&out, Entity*, ScopeBase*) const { int errors = 0; @@ -394,41 +475,32 @@ int ExpBitstring::emit(ostream&out, Entity*, ScopeBase*) } int ExpCharacter::emit_primitive_bit_(ostream&out, Entity*, ScopeBase*, - const VTypePrimitive*etype) + const VTypePrimitive*etype) const { - switch (etype->type()) { - case VTypePrimitive::BOOLEAN: - case VTypePrimitive::BIT: - case VTypePrimitive::STDLOGIC: - switch (value_) { - case '0': - case '1': - out << "1'b" << value_; - return 0; - default: - break; - } + out << "1'b"; + int res = emit_logic(value_, out, etype->type()); - default: - return 1; - } - return 1; + if(res) + cerr << get_fileline() << ": internal error: " + << "Don't know how to handle bit " << value_ + << " with etype==" << etype->type() << endl; + + return res; } -int ExpCharacter::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpCharacter::emit(ostream&out, Entity*ent, ScopeBase*scope) const { const VType*etype = peek_type(); + const VTypeArray*array; + + if (etype != &primitive_CHARACTER && (array = dynamic_cast(etype))) { + etype = array->element_type(); + } if (const VTypePrimitive*use_type = dynamic_cast(etype)) { return emit_primitive_bit_(out, ent, scope, use_type); } - if (const VTypeArray*array = dynamic_cast(etype)) { - if (const VTypePrimitive*use_type = dynamic_cast(array->element_type())) { - return emit_primitive_bit_(out, ent, scope, use_type); - } - } - out << "\"" << value_ << "\""; return 0; } @@ -447,7 +519,7 @@ bool ExpConcat::is_primary(void) const return true; } -int ExpConcat::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpConcat::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; out << "{"; @@ -458,49 +530,37 @@ int ExpConcat::emit(ostream&out, Entity*ent, ScopeBase*scope) return errors; } -int ExpConditional::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpConditional::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; out << "("; - errors += cond_->emit(out, ent, scope); - out << ")? ("; - - if (true_clause_.size() > 1) { - cerr << get_fileline() << ": sorry: Multiple expression waveforms not supported here." << endl; - errors += 1; - } - - Expression*tmp = true_clause_.front(); - errors += tmp->emit(out, ent, scope); - - out << ") : ("; // Draw out any when-else expressions. These are all the else_ // clauses besides the last. - if (else_clause_.size() > 1) { - list::iterator last = else_clause_.end(); - -- last; + if (options_.size() > 1) { + list::const_iterator last = options_.end(); + --last; - for (list::iterator cur = else_clause_.begin() + for (list::const_iterator cur = options_.begin() ; cur != last ; ++cur) { - errors += (*cur) ->emit_when_else(out, ent, scope); + errors += (*cur)->emit_option(out, ent, scope); } - } + } - errors += else_clause_.back()->emit_else(out, ent, scope); + errors += options_.back()->emit_default(out, ent, scope); out << ")"; - // The emit_when_else() functions do not close the last + // The emit_option() functions do not close the last // parentheses so that the following expression can be // nested. But that means come the end, we have some // expressions to close. - for (size_t idx = 1 ; idx < else_clause_.size() ; idx += 1) + for (size_t idx = 1 ; idx < options_.size() ; idx += 1) out << ")"; return errors; } -int ExpConditional::else_t::emit_when_else(ostream&out, Entity*ent, ScopeBase*scope) +int ExpConditional::case_t::emit_option(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; assert(cond_ != 0); @@ -522,7 +582,7 @@ int ExpConditional::else_t::emit_when_else(ostream&out, Entity*ent, ScopeBase*sc return errors; } -int ExpConditional::else_t::emit_else(ostream&out, Entity*ent, ScopeBase*scope) +int ExpConditional::case_t::emit_default(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; // Trailing else must have no condition. @@ -539,7 +599,7 @@ int ExpConditional::else_t::emit_else(ostream&out, Entity*ent, ScopeBase*scope) return errors; } -int ExpEdge::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpEdge::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; switch (fun_) { @@ -556,120 +616,42 @@ int ExpEdge::emit(ostream&out, Entity*ent, ScopeBase*scope) return errors; } -int ExpFunc::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpFunc::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; - if (name_ == "unsigned" && argv_.size() == 1) { - // Handle the special case that this is a cast to - // unsigned. This function is brought in as part of the - // std numeric library, but we interpret it as the same - // as the $unsigned function. - out << "$unsigned("; - errors += argv_[0]->emit(out, ent, scope); - out << ")"; - - } else if (name_ == "integer" && argv_.size() == 1) { - out << "$signed("; - errors += argv_[0]->emit(out, ent, scope); - out << ")"; - - } else if (name_ == "to_integer" && argv_.size() == 1) { - bool signed_flag = false; - - // to_integer converts unsigned to natural - // signed to integer - // try to determine the converted type - const VType*type = argv_[0]->probe_type(ent, scope); - const VTypeArray*array = dynamic_cast(type); - - if(array) - signed_flag = array->signed_vector(); - else - cerr << get_fileline() << ": sorry: Could not determine the " - << "expression sign. Output may be erroneous." << endl; - - out << (signed_flag ? "$signed(" : "$unsigned("); - errors += argv_[0]->emit(out, ent, scope); - out << ")"; - - } else if (name_ == "std_logic_vector" && argv_.size() == 1) { - // Special case: The std_logic_vector function casts its - // argument to std_logic_vector. Internally, we don't - // have to do anything for that to work. - out << "("; - errors += argv_[0]->emit(out, ent, scope); - out << ")"; - - } else if (name_ == "to_unsigned" && argv_.size() == 2) { - - out << "$ivlh_to_unsigned("; - errors += argv_[0]->emit(out, ent, scope); - out << ", "; - errors += argv_[1]->emit(out, ent, scope); - out << ")"; - - } else if ((name_ == "conv_std_logic_vector" || name_ == "resize") && - argv_.size() == 2) { - int64_t use_size; - bool rc = argv_[1]->evaluate(ent, scope, use_size); - ivl_assert(*this, rc); - out << use_size << "'("; - errors += argv_[0]->emit(out, ent, scope); - out << ")"; - - } else if (name_ == "rising_edge" && argv_.size() == 1) { - out << "$ivlh_rising_edge("; - errors += argv_[0]->emit(out, ent, scope); - out << ")"; - - } else if (name_ == "falling_edge" && argv_.size() == 1) { - out << "$ivlh_falling_edge("; - errors += argv_[0]->emit(out, ent, scope); - out << ")"; - - } else { - // If this function has an elaborated definition, and if - // that definition is in a package, then include the - // package name as a scope qualifier. This assures that - // the SV elaborator finds the correct VHDL elaborated - // definition. - if (def_) { - const Package*pkg = dynamic_cast (def_->get_parent()); - if (pkg != 0) - out << "\\" << pkg->name() << " ::"; - } - - out << "\\" << name_ << " ("; - for (size_t idx = 0; idx < argv_.size() ; idx += 1) { - if (idx > 0) out << ", "; - errors += argv_[idx]->emit(out, ent, scope); - } - out << ")"; + if(!def_) { + cerr << get_fileline() << ": error: unknown function: " << name_ << endl; + return 1; } + def_->emit_full_name(argv_, out, ent, scope); + out << " ("; + def_->emit_args(argv_, out, ent, scope); + out << ")"; + return errors; } -int ExpInteger::emit(ostream&out, Entity*, ScopeBase*) +int ExpInteger::emit(ostream&out, Entity*, ScopeBase*) const { out << "32'd" << value_; return 0; } -int ExpInteger::emit_package(ostream&out) +int ExpInteger::emit_package(ostream&out) const { out << value_; return 0; } -int ExpReal::emit(ostream&out, Entity*, ScopeBase*) +int ExpReal::emit(ostream&out, Entity*, ScopeBase*) const { out << value_; return 0; } -int ExpReal::emit_package(ostream&out) +int ExpReal::emit_package(ostream&out) const { out << value_; return 0; @@ -680,7 +662,7 @@ bool ExpReal::is_primary(void) const return true; } -int ExpLogical::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpLogical::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; @@ -712,7 +694,23 @@ int ExpLogical::emit(ostream&out, Entity*ent, ScopeBase*scope) return errors; } -int ExpName::emit_as_prefix_(ostream&out, Entity*ent, ScopeBase*scope) +int ExpName::emit_indices(ostream&out, Entity*ent, ScopeBase*scope) const +{ + int errors = 0; + + if (indices_) { + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + out << "["; + errors += (*it)->emit(out, ent, scope); + out << "]"; + } + } + + return errors; +} + +int ExpName::emit_as_prefix_(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; if (prefix_.get()) { @@ -720,17 +718,12 @@ int ExpName::emit_as_prefix_(ostream&out, Entity*ent, ScopeBase*scope) } out << "\\" << name_ << " "; - if (index_) { - out << "["; - errors += index_->emit(out, ent, scope); - out << "]"; - ivl_assert(*this, lsb_ == 0); - } + errors += emit_indices(out, ent, scope); out << "."; return errors; } -int ExpName::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpName::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; int field_size = 0; @@ -738,6 +731,11 @@ int ExpName::emit(ostream&out, Entity*ent, ScopeBase*scope) if(try_workarounds_(out, ent, scope, indices, field_size)) { emit_workaround_(out, ent, scope, indices, field_size); + for(list::iterator it = indices.begin(); + it != indices.end(); ++it) + { + delete *it; + } return 0; } @@ -752,26 +750,19 @@ int ExpName::emit(ostream&out, Entity*ent, ScopeBase*scope) else out << "\\" << name_ << " "; - if (index_) { - out << "["; - errors += index_->emit(out, ent, scope); - - if (lsb_) { - out << ":"; - errors += lsb_->emit(out, ent, scope); - } - out << "]"; - } + errors += emit_indices(out, ent, scope); return errors; } bool ExpName::try_workarounds_(ostream&out, Entity*ent, ScopeBase*scope, - list& indices, int& data_size) + list& indices, int& data_size) const { Expression*exp = NULL; bool wrkand_required = false; const VType*type = NULL; + Expression*idx = index(0); + ExpRange*range = dynamic_cast(idx); if(!scope) return false; @@ -779,7 +770,7 @@ bool ExpName::try_workarounds_(ostream&out, Entity*ent, ScopeBase*scope, if(prefix_.get()) prefix_->try_workarounds_(out, ent, scope, indices, data_size); - if(index_ && !lsb_ && scope->find_constant(name_, type, exp)) { + if(idx && !range && scope->find_constant(name_, type, exp)) { while(const VTypeDef*type_def = dynamic_cast(type)) { type = type_def->peek_definition(); } @@ -791,7 +782,7 @@ bool ExpName::try_workarounds_(ostream&out, Entity*ent, ScopeBase*scope, if(prefix_.get() && scope->find_constant(prefix_->name_, type, exp)) { // Handle the case of array of records - if(prefix_->index_) { + if(prefix_->index(0)) { const VTypeArray*arr = dynamic_cast(type); assert(arr); type = arr->element_type(); @@ -808,17 +799,23 @@ bool ExpName::try_workarounds_(ostream&out, Entity*ent, ScopeBase*scope, wrkand_required |= check_const_record_workaround_(rec, scope, indices, data_size); } + // Workarounds are currently implemented only for one-dimensional arrays + assert(!indices_ || indices_->size() == 1 || !wrkand_required); + return wrkand_required; } bool ExpName::check_const_array_workaround_(const VTypeArray*arr, ScopeBase*scope, list&indices, int&data_size) const { + assert(indices_ && indices_->size() == 1); + const VType*element = arr->element_type(); data_size = element->get_width(scope); if(data_size < 0) return false; - indices.push_back(new index_t(index_, new ExpInteger(data_size))); + + indices.push_back(new index_t(index(0)->clone(), new ExpInteger(data_size))); return true; } @@ -843,7 +840,7 @@ bool ExpName::check_const_record_workaround_(const VTypeRecord*rec, data_size = tmp_field; indices.push_back(new index_t(NULL, NULL, new ExpInteger(tmp_offset))); - if(index_) { + if(index(0)) { const VTypeArray*arr = dynamic_cast(type); assert(arr); return check_const_array_workaround_(arr, scope, indices, data_size); @@ -864,7 +861,7 @@ bool ExpName::check_const_record_workaround_(const VTypeRecord*rec, } int ExpName::emit_workaround_(ostream&out, Entity*ent, ScopeBase*scope, - const list& indices, int field_size) + const list& indices, int field_size) const { int errors = 0; @@ -886,14 +883,26 @@ bool ExpName::is_primary(void) const return true; } -int ExpRelation::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpRelation::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; errors += emit_operand1(out, ent, scope); + const VType*type1 = peek_operand1()->probe_type(ent, scope); + const VType*type2 = peek_operand2()->probe_type(ent, scope); + bool logical_compare = false; + + // Apply case equality operator if any of the operands is of logic type + if(((type1 && (type1->type_match(&primitive_STDLOGIC) || + type1->type_match(&primitive_STDLOGIC_VECTOR))) + || (type2 && (type2->type_match(&primitive_STDLOGIC) || + type2->type_match(&primitive_STDLOGIC_VECTOR))))) { + logical_compare = true; + } + switch (fun_) { case EQ: - out << " == "; + out << (logical_compare ? " === " : " == "); break; case LT: out << " < "; @@ -902,7 +911,7 @@ int ExpRelation::emit(ostream&out, Entity*ent, ScopeBase*scope) out << " > "; break; case NEQ: - out << " != "; + out << (logical_compare ? " !== " : " != "); break; case LE: out << " <= "; @@ -916,7 +925,7 @@ int ExpRelation::emit(ostream&out, Entity*ent, ScopeBase*scope) return errors; } -int ExpShift::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpShift::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; @@ -951,34 +960,32 @@ bool ExpString::is_primary(void) const return true; } -int ExpString::emit(ostream& out, Entity*ent, ScopeBase*scope) +int ExpString::emit(ostream& out, Entity*ent, ScopeBase*scope) const { + const VTypeArray*arr; const VType*type = peek_type(); assert(type != 0); - if (const VTypeArray*arr = dynamic_cast(type)) { + if (type != &primitive_STRING && (arr = dynamic_cast(type))) { return emit_as_array_(out, ent, scope, arr); } - out << "\""; - for(vector::const_iterator it = value_.begin() - ; it != value_.end(); ++it) - out << *it; - out << "\""; + out << "\"" << escape_quot(value_) << "\""; + return 0; } -int ExpString::emit_as_array_(ostream& out, Entity*, ScopeBase*, const VTypeArray*arr) +int ExpString::emit_as_array_(ostream& out, Entity*, ScopeBase*, const VTypeArray*arr) const { int errors = 0; - assert(arr->dimensions() == 1); + assert(arr->dimensions().size() == 1); const VTypePrimitive*etype = dynamic_cast (arr->basic_type()); assert(etype); // Detect the special case that this is an array of // CHARACTER. In this case, emit at a Verilog string. - if (etype->type()==VTypePrimitive::CHARACTER) { + if (arr->element_type() == &primitive_CHARACTER) { vector tmp (value_.size() + 3); tmp[0] = '"'; memcpy(&tmp[1], &value_[0], value_.size()); @@ -991,35 +998,32 @@ int ExpString::emit_as_array_(ostream& out, Entity*, ScopeBase*, const VTypeArra assert(etype->type() != VTypePrimitive::INTEGER); out << value_.size() << "'b"; for (size_t idx = 0 ; idx < value_.size() ; idx += 1) { - switch (value_[idx]) { - case '0': - out << "0"; - break; - case '1': - out << "1"; - break; - case 'z': case 'Z': - assert(etype->type() == VTypePrimitive::STDLOGIC); - out << "z"; - break; - case '-': - assert(etype->type() == VTypePrimitive::STDLOGIC); - out << "x"; - break; - default: - cerr << get_fileline() << ": internal error: " - << "Don't know how to handle bit " << value_[idx] - << " with etype==" << etype->type() << endl; - assert(etype->type() == VTypePrimitive::STDLOGIC); - out << "x"; - break; - } + int res = emit_logic(value_[idx], out, etype->type()); + errors += res; + + if(res) + cerr << get_fileline() << ": internal error: " + << "Don't know how to handle bit " << value_[idx] + << " with etype==" << etype->type() << endl; } return errors; } -int ExpUAbs::emit(ostream&out, Entity*ent, ScopeBase*scope) +std::string ExpString::escape_quot(const std::string& str) +{ + size_t idx = 0; + string result(str); + + while((idx = result.find('"', idx)) != string::npos) { + result.replace(idx, 1, "\\\""); + idx += 2; + } + + return result; +} + +int ExpUAbs::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; out << "abs("; @@ -1028,16 +1032,32 @@ int ExpUAbs::emit(ostream&out, Entity*ent, ScopeBase*scope) return errors; } -int ExpUNot::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpUNot::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; - out << "~("; + + const VType*op_type = peek_operand()->probe_type(ent, scope); + + if(op_type && op_type->type_match(&type_BOOLEAN)) + out << "!("; + else + out << "~("; + errors += emit_operand1(out, ent, scope); out << ")"; return errors; } -int ExpCast::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpUMinus::emit(ostream&out, Entity*ent, ScopeBase*scope) const +{ + int errors = 0; + out << "-("; + errors += emit_operand1(out, ent, scope); + out << ")"; + return errors; +} + +int ExpCast::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; errors += type_->emit_def(out, empty_perm_string); @@ -1047,7 +1067,7 @@ int ExpCast::emit(ostream&out, Entity*ent, ScopeBase*scope) return errors; } -int ExpNew::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpNew::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; out << "new["; @@ -1055,3 +1075,53 @@ int ExpNew::emit(ostream&out, Entity*ent, ScopeBase*scope) out << "]"; return errors; } + +int ExpTime::emit(ostream&out, Entity*, ScopeBase*) const +{ + out << amount_; + + switch(unit_) { + case FS: out << "fs"; break; + case PS: out << "ps"; break; + case NS: out << "ns"; break; + case US: out << "us"; break; + case MS: out << "ms"; break; + case S: out << "s"; break; + } + + return 0; +} + +int ExpRange::emit(ostream&out, Entity*ent, ScopeBase*scope) const +{ + int errors = 0; + + if(range_expr_) { + out << "$left("; + errors += range_base_->emit(out, ent, scope); + out << "):$right("; + errors += range_base_->emit(out, ent, scope); + out << ")"; + } else if(direction_ == AUTO) { + ivl_assert(*this, false); + out << "/* auto dir */"; + } else { + errors += left_->emit(out, ent, scope); + out << ":"; + errors += right_->emit(out, ent, scope); + } + + return errors; +} + +int ExpDelay::emit(ostream&out, Entity*ent, ScopeBase*scope) const +{ + int errors = 0; + + out << "#("; + errors += delay_->emit(out, ent, scope); + out << ") "; + errors += expr_->emit(out, ent, scope); + + return errors; +} diff --git a/vhdlpp/expression_evaluate.cc b/vhdlpp/expression_evaluate.cc index 983c0ed6b..56b2b7eec 100644 --- a/vhdlpp/expression_evaluate.cc +++ b/vhdlpp/expression_evaluate.cc @@ -22,27 +22,19 @@ # include "expression.h" # include "architec.h" # include +# include +# include -bool Expression::evaluate(ScopeBase*, int64_t&) const -{ - return false; -} - -bool Expression::evaluate(Entity*, ScopeBase*scope, int64_t&val) const -{ - return evaluate(scope, val); -} - -bool ExpArithmetic::evaluate(ScopeBase*scope, int64_t&val) const +bool ExpArithmetic::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const { int64_t val1, val2; bool rc; - rc = eval_operand1(scope, val1); + rc = eval_operand1(ent, scope, val1); if (rc == false) return false; - rc = eval_operand2(scope, val2); + rc = eval_operand2(ent, scope, val2); if (rc == false) return false; @@ -67,9 +59,13 @@ bool ExpArithmetic::evaluate(ScopeBase*scope, int64_t&val) const val = val1 % val2; break; case REM: + if (val2 == 0) + return false; + val = val1 - (val1 / val2) * val2; return false; case POW: - return false; + val = (int64_t) pow(val1, val2); + break; case xCONCAT: // not possible return false; } @@ -77,149 +73,98 @@ bool ExpArithmetic::evaluate(ScopeBase*scope, int64_t&val) const return true; } -bool ExpAttribute::evaluate(ScopeBase*scope, int64_t&val) const +bool ExpAttribute::test_array_type(const VType*type) const { - /* Special Case: The array attributes can sometimes be calculated all - the down to a literal integer at compile time, and all it - needs is the type of the base expression. (The base - expression doesn't even need to be evaluated.) */ - if (name_ == "length" || name_ == "right" || name_ == "left") { - const VType*base_type = base_->peek_type(); + const VTypeArray*arr = dynamic_cast(type); - if(!base_type) { - const ExpName*name = NULL; + if (arr == 0) { + cerr << endl << get_fileline() << ": error: " + << "Cannot apply the '" << name_ << " attribute to non-array objects" + << endl; + ivl_assert(*this, false); + return false; + } - if(scope && (name = dynamic_cast(base_))) { - const perm_string& n = name->peek_name(); - if(const Variable*var = scope->find_variable(n)) - base_type = var->peek_type(); - else if(const Signal*sig = scope->find_signal(n)) - base_type = sig->peek_type(); - else if(const InterfacePort*port = scope->find_param(n)) - base_type = port->type; - } - } + if (arr->dimensions().size() > 1) { + cerr << endl << get_fileline() << ": error: " + << "Cannot apply the '" << name_ + << " attribute to multidimensional arrays" << endl; + return false; + } - if(!base_type) - return false; // I tried really hard, sorry + if (arr->dimension(0).is_box()) + return false; - const VTypeArray*arr = dynamic_cast(base_type); - if (arr == 0) { - cerr << get_fileline() << ": error: " - << "Cannot apply the 'length attribute to non-array objects" - << endl; - return false; - } + return true; +} - if(name_ == "length") { - int64_t size = arr->get_width(scope); +bool ExpAttribute::evaluate_type_attr(const VType*type, Entity*ent, ScopeBase*scope, int64_t&val) const +{ + if (name_ == "length" && test_array_type(type)) { + int64_t size = type->get_width(scope); - if(size > 0) - val = size; - else - return false; - } else if(name_ == "left") { - arr->dimension(0).msb()->evaluate(scope, val); - } else if(name_ == "right") { - arr->dimension(0).lsb()->evaluate(scope, val); - } else ivl_assert(*this, false); - - return true; + if(size > 0) { + val = size; + return true; + } + } else if (name_ == "left" && test_array_type(type)) { + const VTypeArray*arr = dynamic_cast(type); + return arr->dimension(0).msb()->evaluate(ent, scope, val); + } else if (name_ == "right" && test_array_type(type)) { + const VTypeArray*arr = dynamic_cast(type); + return arr->dimension(0).lsb()->evaluate(ent, scope, val); } return false; } -bool ExpAttribute::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const +bool ExpObjAttribute::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const { - if (!ent || !scope) { // it's impossible to evaluate, probably it is inside a subprogram - return false; - } + const VType*base_type = base_->peek_type(); - if (name_ == "left" || name_ == "right") { - const VType*base_type = base_->peek_type(); - if (base_type == 0) - base_type = base_->probe_type(ent, scope); + if (base_type == NULL) + base_type = base_->probe_type(ent, scope); - ivl_assert(*this, base_type); + if (base_type) + return evaluate_type_attr(base_type, ent, scope, val); - const VTypeArray*arr = dynamic_cast(base_type); - if (arr == 0) { - cerr << get_fileline() << ": error: " - << "Cannot apply the '" << name_ - << " attribute to non-array objects" << endl; - return false; - } - - ivl_assert(*this, arr->dimensions() == 1); - if(name_ == "left") - arr->dimension(0).msb()->evaluate(ent, scope, val); - else // "right" - arr->dimension(0).lsb()->evaluate(ent, scope, val); - - return true; - } - - return evaluate(scope, val); -} - -/* - * I don't yet know how to evaluate concatenations. It is not likely - * to come up anyhow. - */ -bool ExpConcat::evaluate(ScopeBase*, int64_t&) const -{ return false; } -bool ExpName::evaluate(ScopeBase*scope, int64_t&val) const +bool ExpTypeAttribute::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const { - const VType*type; - Expression*exp; - - if (prefix_.get()) { - cerr << get_fileline() << ": sorry: I don't know how to evaluate ExpName prefix parts." << endl; - return false; - } - - if (!scope) - return false; - - if (!scope->find_constant(name_, type, exp)) - return false; - - return exp->evaluate(scope, val); + return evaluate_type_attr(base_, ent, scope, val); } bool ExpName::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const { if (prefix_.get()) { - cerr << get_fileline() << ": sorry: I don't know how to evaluate ExpName prefix parts." << endl; - return false; + cerr << get_fileline() << ": sorry: I don't know how to evaluate " + << "ExpName prefix parts." << endl; + return false; } - const InterfacePort*gen = ent->find_generic(name_); - if (gen) { - cerr << get_fileline() << ": sorry: I don't necessarily handle generic overrides." << endl; + if (scope) { + const VType*type; + Expression*exp; - // Evaluate the default expression and use that. - if (gen->expr) - return gen->expr->evaluate(ent, scope, val); + if (scope->find_constant(name_, type, exp)) + return exp->evaluate(ent, scope, val); } - return evaluate(scope, val); + return false; } -bool ExpShift::evaluate(ScopeBase*scope, int64_t&val) const +bool ExpShift::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const { int64_t val1, val2; bool rc; - rc = eval_operand1(scope, val1); + rc = eval_operand1(ent, scope, val1); if (rc == false) return false; - rc = eval_operand2(scope, val2); + rc = eval_operand2(ent, scope, val2); if (rc == false) return false; @@ -243,3 +188,18 @@ bool ExpShift::evaluate(ScopeBase*scope, int64_t&val) const return true; } + +/*bool ExpTime::evaluate(Entity*, ScopeBase*, int64_t&val) const +{ + double v = to_fs(); + + if(v > std::numeric_limits::max()) { + val = std::numeric_limits::max(); + cerr << get_fileline() << ": sorry: Time value is higher than the " + << "handled limit, reduced to " << val << " fs." << endl; + } + + val = v; + return true; +} +}*/ diff --git a/vhdlpp/expression_stream.cc b/vhdlpp/expression_stream.cc index c379795bf..f9f6f40e5 100644 --- a/vhdlpp/expression_stream.cc +++ b/vhdlpp/expression_stream.cc @@ -61,13 +61,9 @@ void ExpAggregate::choice_t::write_to_stream(ostream&fd) return; } - if (prange_t*rp = range_expressions()) { - rp->msb()->write_to_stream(fd); - if (rp->is_downto()) - fd << " downto "; - else - fd << " to "; - rp->msb()->write_to_stream(fd); + if (ExpRange*rp = range_expressions()) { + rp->write_to_stream(fd); + return; } fd << "/* ERROR */"; @@ -111,7 +107,13 @@ void ExpArithmetic::write_to_stream(ostream&out) const out << ")"; } -void ExpAttribute::write_to_stream(ostream&fd) const +void ExpObjAttribute::write_to_stream(ostream&fd) const +{ + base_->write_to_stream(fd); + fd << "'" << name_; +} + +void ExpTypeAttribute::write_to_stream(ostream&fd) const { base_->write_to_stream(fd); fd << "'" << name_; @@ -179,9 +181,32 @@ void ExpReal::write_to_stream(ostream&fd) const fd << value_; } -void ExpLogical::write_to_stream(ostream&) const +void ExpLogical::write_to_stream(ostream&out) const { - ivl_assert(*this, !"Not supported"); + peek_operand1()->write_to_stream(out); + + switch (fun_) { + case AND: + out << " and "; + break; + case OR: + out << " or "; + break; + case XOR: + out << " xor "; + break; + case NAND: + out << " nand "; + break; + case NOR: + out << " nor "; + break; + case XNOR: + out << " xnor "; + break; + } + + peek_operand2()->write_to_stream(out); } void ExpName::write_to_stream(ostream&fd) const @@ -192,14 +217,20 @@ void ExpName::write_to_stream(ostream&fd) const } fd << name_; - if (index_) { - fd << "("; - index_->write_to_stream(fd); - if (lsb_) { - fd << " downto "; - lsb_->write_to_stream(fd); - } - fd << ")"; + + if (indices_) { + fd << "("; + bool first = true; + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + if(first) + first = false; + else + fd << ","; + + (*it)->write_to_stream(fd); + } + fd << ")"; } } @@ -272,10 +303,15 @@ void ExpShift::write_to_stream(ostream&out) const void ExpString::write_to_stream(ostream&fd) const { fd << "\""; - for(vector::const_iterator it = value_.begin(); - it != value_.end(); ++it) { - fd << *it; + + // Restore double quotation marks + for(string::const_iterator it = value_.begin(); it != value_.end(); ++it) { + if(*it == '"') + fd << "\"\""; + else + fd << *it; } + fd << "\""; } @@ -291,9 +327,53 @@ void ExpUNot::write_to_stream(ostream&fd) const write_to_stream_operand1(fd); } +void ExpUMinus::write_to_stream(ostream&fd) const +{ + fd << "-("; + write_to_stream_operand1(fd); + fd << ")"; +} + void ExpCast::write_to_stream(ostream&fd) const { // Type casting is introduced only for a few specific cases in // SystemVerilog, so no need to use it here base_->write_to_stream(fd); } + +void ExpTime::write_to_stream(ostream&fd) const +{ + fd << amount_; + + switch(unit_) { + case FS: fd << " fs"; break; + case PS: fd << " ps"; break; + case NS: fd << " ns"; break; + case US: fd << " us"; break; + case MS: fd << " ms"; break; + case S: fd << " s"; break; + } +} + +void ExpRange::write_to_stream(ostream&fd) const +{ + if(range_expr_) { + range_base_->write_to_stream(fd); + fd << (range_reverse_ ? "'reverse_range" : "'range"); + } else { + left_->write_to_stream(fd); + switch(direction_) { + case DOWNTO: fd << " downto "; break; + case TO: fd << " to "; break; + default: ivl_assert(*this, false); break; + } + right_->write_to_stream(fd); + } +} + +void ExpDelay::write_to_stream(ostream&out) const +{ + expr_->write_to_stream(out); + out << " after "; + delay_->write_to_stream(out); +} diff --git a/vhdlpp/lexor.lex b/vhdlpp/lexor.lex index c9dae5c51..fdafcb058 100644 --- a/vhdlpp/lexor.lex +++ b/vhdlpp/lexor.lex @@ -6,7 +6,7 @@ %{ /* - * Copyright (c) 2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2017 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 @@ -84,6 +84,7 @@ exponent [eE][-+]?{integer} based_literal {integer}#{based_integer}(\.{based_integer})?#{exponent}? based_integer [0-9a-fA-F](_?[0-9a-fA-F])* +time {integer}{W}*([fFpPnNuUmM]?[sS]) %% [ \t\b\f\r] { ; } @@ -110,9 +111,7 @@ based_integer [0-9a-fA-F](_?[0-9a-fA-F])* return CHARACTER_LITERAL; } -(\"([^\"]|(\"\"))*?\")|(\"[^\"]*\") { -/* first pattern: string literals with doubled quotation mark */ -/* second pattern: string literals without doubled quotation */ +(\"([^\"]|(\"\"))*?\") { yylval.text = escape_quot_and_dup(yytext); assert(yylval.text); return STRING_LITERAL; @@ -164,6 +163,9 @@ based_integer [0-9a-fA-F](_?[0-9a-fA-F])* } {based_literal} { + for(char*cp = yytext ; *cp ; ++cp) + *cp = tolower(*cp); + if(!are_underscores_correct(yytext) || !is_based_correct(yytext)) std::cerr << "An invalid form of based literal:" << yytext << std::endl; @@ -236,6 +238,14 @@ static bool are_underscores_correct(char* text) return 1; } +static bool is_char_ok(char c, int base) +{ + if(base <= 10) + return '0' <= c && c - '0' < base; + else + return isdigit(c) || (c >= 'a' && c < 'a' + base - 10); +} + /** * This function checks if the format of a based number * is correct according to the VHDL standard @@ -247,8 +257,8 @@ static bool is_based_correct(char* text) { char* ptr; //BASE examination - char clean_base[4]; - clean_base[3] = '\0'; + char clean_base[4] = {0,}; + char* clean_base_end = clean_base + sizeof(clean_base); char* clean_base_ptr = clean_base; for(ptr = text; ptr != strchr(text, '#'); ++ptr) { @@ -256,7 +266,7 @@ static bool is_based_correct(char* text) ++ptr; if(!(*ptr >= '0' && *ptr <= '9')) //the base uses chars other than digits return 0; - if(*clean_base_ptr == '\0') + if(clean_base_ptr == clean_base_end) break; *clean_base_ptr = *ptr; ++clean_base_ptr; @@ -279,20 +289,7 @@ static bool is_based_correct(char* text) return 0; } bool point = false; - set allowed_chars; - unsigned c; - if(base <= 10) { - for(c = 0; c < base; ++c) - allowed_chars.insert(c + '0'); - } - else - { - for(c = 0; c < 10; ++c) - allowed_chars.insert(c + '0'); - for(c = 0; c < base - 10; ++c) - allowed_chars.insert(c + 'a'); - } //MANTISSA examination for(ptr = strchr(text, '#') + 1, length = 0; ptr != strrchr(text, '#'); ++ptr) { @@ -308,9 +305,10 @@ static bool is_based_correct(char* text) continue; } } - //the number consists of other chars than allowed - if(allowed_chars.find(*ptr) == allowed_chars.end()) + //check if the number consists of other chars than allowed + if(!is_char_ok(*ptr, base)) return 0; + ++length; } if(length == 0) @@ -750,7 +748,16 @@ yyscan_t prepare_lexor(FILE*fd) return scanner; } +/* + * Modern version of flex (>=2.5.9) can clean up the scanner data. + */ void destroy_lexor(yyscan_t scanner) { +# ifdef FLEX_SCANNER +# if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 +# if YY_FLEX_MINOR_VERSION > 5 || defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(scanner); +# endif +# endif +# endif } diff --git a/vhdlpp/library.cc b/vhdlpp/library.cc index 61ae426df..44555f220 100644 --- a/vhdlpp/library.cc +++ b/vhdlpp/library.cc @@ -1,6 +1,8 @@ /* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) + * Copyright CERN 2016 + * @author Maciej Suminski * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -22,6 +24,8 @@ # include "parse_misc.h" # include "compiler.h" # include "package.h" +# include "std_types.h" +# include "std_funcs.h" # include # include # include @@ -72,9 +76,9 @@ void library_add_directory(const char*directory) library_search_path.push_front(directory); } -Subprogram*library_find_subprogram(perm_string name) +SubprogramHeader*library_match_subprogram(perm_string name, const list*params) { - Subprogram*subp = NULL; + SubprogramHeader*subp; map::const_iterator lib_it; for(lib_it = libraries.begin(); lib_it != libraries.end(); ++lib_it) { @@ -82,7 +86,7 @@ Subprogram*library_find_subprogram(perm_string name) map::const_iterator pack_it; for(pack_it = lib.packages.begin(); pack_it != lib.packages.end(); ++pack_it) { - if((subp = pack_it->second->find_subprogram(name))) + if((subp = pack_it->second->match_subprogram(name, params))) return subp; } } @@ -107,7 +111,7 @@ static string make_library_package_path(perm_string lib_name, perm_string name) } static void import_ieee(void); -static void import_ieee_use(ActiveScope*res, perm_string package, perm_string name); +static void import_ieee_use(const YYLTYPE&loc, ActiveScope*res, perm_string package, perm_string name); static void import_std_use(const YYLTYPE&loc, ActiveScope*res, perm_string package, perm_string name); static void dump_library_package(ostream&file, perm_string lname, perm_string pname, Package*pack) @@ -239,7 +243,7 @@ void library_use(const YYLTYPE&loc, ActiveScope*res, // Special case handling for the IEEE library. if (use_library == "ieee") { - import_ieee_use(res, use_package, use_name); + import_ieee_use(loc, res, use_package, use_name); return; } // Special case handling for the STD library. @@ -305,14 +309,34 @@ static void import_ieee_use_std_logic_1164(ActiveScope*res, perm_string name) bool all_flag = name=="all"; if (all_flag || name == "std_logic_vector") { - vector dims (1); - res->use_name(perm_string::literal("std_logic_vector"), - new VTypeArray(&primitive_STDLOGIC, dims, false)); + res->use_name(perm_string::literal("std_logic_vector"), &primitive_STDLOGIC_VECTOR); } } -static void import_ieee_use_std_logic_arith(ActiveScope*, perm_string) +static void import_ieee_use_std_logic_misc(ActiveScope*, perm_string name) { + bool all_flag = name=="all"; + list*args; + + if (all_flag || name == "or_reduce") { + /* function or_reduce(arg : std_logic_vector) return std_logic; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("or_reduce"), + perm_string::literal("|"), + args, &primitive_STDLOGIC)); + } + + if (all_flag || name == "and_reduce") { + /* function and_reduce(arg : std_logic_vector) return std_logic; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("and_reduce"), + perm_string::literal("&"), + args, &primitive_STDLOGIC)); + } } static void import_ieee_use_numeric_bit(ActiveScope*res, perm_string name) @@ -320,14 +344,10 @@ static void import_ieee_use_numeric_bit(ActiveScope*res, perm_string name) bool all_flag = name=="all"; if (all_flag || name == "signed") { - vector dims (1); - res->use_name(perm_string::literal("signed"), - new VTypeArray(&primitive_STDLOGIC, dims, true)); + res->use_name(perm_string::literal("signed"), &primitive_SIGNED); } if (all_flag || name == "unsigned") { - vector dims (1); - res->use_name(perm_string::literal("unsigned"), - new VTypeArray(&primitive_BIT, dims, false)); + res->use_name(perm_string::literal("unsigned"), &primitive_UNSIGNED); } } @@ -336,18 +356,15 @@ static void import_ieee_use_numeric_std(ActiveScope*res, perm_string name) bool all_flag = name=="all"; if (all_flag || name == "signed") { - vector dims (1); - res->use_name(perm_string::literal("signed"), - new VTypeArray(&primitive_STDLOGIC, dims, true)); + res->use_name(perm_string::literal("signed"), &primitive_SIGNED); } if (all_flag || name == "unsigned") { - vector dims (1); - res->use_name(perm_string::literal("unsigned"), - new VTypeArray(&primitive_STDLOGIC, dims, false)); + res->use_name(perm_string::literal("unsigned"), &primitive_UNSIGNED); } } -static void import_ieee_use(ActiveScope*res, perm_string package, perm_string name) +static void import_ieee_use(const YYLTYPE&/*loc*/, ActiveScope*res, + perm_string package, perm_string name) { if (package == "std_logic_1164") { import_ieee_use_std_logic_1164(res, name); @@ -355,10 +372,20 @@ static void import_ieee_use(ActiveScope*res, perm_string package, perm_string na } if (package == "std_logic_arith") { - import_ieee_use_std_logic_arith(res, name); + // arithmetic operators for std_logic_vector return; } + if (package == "std_logic_misc") { + import_ieee_use_std_logic_misc(res, name); + return; + } + + if (package == "std_logic_unsigned") { + // arithmetic operators for std_logic_vector + return; + } + if (package == "numeric_bit") { import_ieee_use_numeric_bit(res, name); return; @@ -368,64 +395,28 @@ static void import_ieee_use(ActiveScope*res, perm_string package, perm_string na import_ieee_use_numeric_std(res, name); return; } + + cerr << "Warning: Package ieee." << package.str() <<" is not yet supported" << endl; } -static void import_std_use(const YYLTYPE&loc, ActiveScope*/*res*/, perm_string package, perm_string name) +static void import_std_use(const YYLTYPE&/*loc*/, ActiveScope*res, + perm_string package, perm_string /*name*/) { if (package == "standard") { // do nothing return; } else if (package == "textio") { - cerr << "warning: textio package not really supported" << endl; + res->use_name(perm_string::literal("text"), &primitive_INTEGER); + res->use_name(perm_string::literal("line"), &primitive_STRING); + res->use_name(type_FILE_OPEN_KIND.peek_name(), &type_FILE_OPEN_KIND); + res->use_name(type_FILE_OPEN_STATUS.peek_name(), &type_FILE_OPEN_STATUS); return; } else { - sorrymsg(loc, "package %s of library %s not yet supported", package.str(), name.str()); + cerr << "Warning: Package std." << package.str() <<" is not yet supported" << endl; return; } } -const VTypePrimitive primitive_BOOLEAN(VTypePrimitive::BOOLEAN, true); -const VTypePrimitive primitive_BIT(VTypePrimitive::BIT, true); -const VTypePrimitive primitive_INTEGER(VTypePrimitive::INTEGER); -const VTypePrimitive primitive_NATURAL(VTypePrimitive::NATURAL); -const VTypePrimitive primitive_REAL(VTypePrimitive::REAL); -const VTypePrimitive primitive_STDLOGIC(VTypePrimitive::STDLOGIC, true); -const VTypePrimitive primitive_CHARACTER(VTypePrimitive::CHARACTER); - -static const VTypeArray primitive_BIT_VECTOR(&primitive_BIT, vector (1)); -static const VTypeArray primitive_BOOL_VECTOR(&primitive_BOOLEAN, vector (1)); -static const VTypeArray primitive_STRING(&primitive_CHARACTER, vector (1)); - -void generate_global_types(ActiveScope*res) -{ - res->use_name(perm_string::literal("boolean"), &primitive_BOOLEAN); - res->use_name(perm_string::literal("bit"), &primitive_BIT); - res->use_name(perm_string::literal("integer"), &primitive_INTEGER); - res->use_name(perm_string::literal("real"), &primitive_REAL); - res->use_name(perm_string::literal("std_logic"), &primitive_STDLOGIC); - res->use_name(perm_string::literal("character"), &primitive_CHARACTER); - res->use_name(perm_string::literal("bit_vector"),&primitive_BOOL_VECTOR); - res->use_name(perm_string::literal("string"), &primitive_STRING); - res->use_name(perm_string::literal("natural"), &primitive_NATURAL); -} - -bool is_global_type(perm_string name) -{ - if (name == "boolean") return true; - if (name == "bit") return true; - if (name == "integer") return true; - if (name == "real") return true; - if (name == "std_logic") return true; - if (name == "std_logic_vector") return true; - if (name == "character") return true; - if (name == "bit_vector") return true; - if (name == "string") return true; - if (name == "natural") return true; - if (name == "signed") return true; - if (name == "unsigned") return true; - return false; -} - void library_set_work_path(const char*path) { assert(library_work_path == 0); @@ -466,3 +457,27 @@ int emit_packages(void) return 0; } + +static int elaborate_library_packages(mappackages) +{ + int errors = 0; + + for (map::iterator cur = packages.begin() + ; cur != packages.end() ; ++cur) { + errors += cur->second->elaborate(); + } + + return errors; +} + +int elaborate_libraries() +{ + int errors = 0; + + for (map::iterator cur = libraries.begin() + ; cur != libraries.end() ; ++cur) { + errors += elaborate_library_packages(cur->second.packages); + } + + return errors; +} diff --git a/vhdlpp/library.h b/vhdlpp/library.h index ab6464f45..5596db870 100644 --- a/vhdlpp/library.h +++ b/vhdlpp/library.h @@ -19,14 +19,18 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -class Subprogram; +#include -extern void library_set_work_path(const char*work_path); -extern void library_add_directory(const char*directory); +class SubprogramHeader; +class VType; -extern Subprogram*library_find_subprogram(perm_string name); +void library_set_work_path(const char*work_path); +void library_add_directory(const char*directory); -extern int emit_packages(void); +int elaborate_libraries(void); +int emit_packages(void); + +SubprogramHeader*library_match_subprogram(perm_string name, const list*params); #endif /* IVL_library_H */ diff --git a/vhdlpp/main.cc b/vhdlpp/main.cc index b142d3845..d6f3a51d4 100644 --- a/vhdlpp/main.cc +++ b/vhdlpp/main.cc @@ -1,6 +1,6 @@ const char COPYRIGHT[] = - "Copyright (c) 2011-2012 Stephen Williams (steve@icarus.com)\n" + "Copyright (c) 2011-2015 Stephen Williams (steve@icarus.com)\n" "Copyright CERN 2012 / Stephen Williams (steve@icarus.com)"; /* * This source code is free software; you can redistribute it @@ -38,7 +38,7 @@ const char COPYRIGHT[] = * information to the file named . * * elaboration= - * Enable debugging of elaboratin by dumping elaboration + * Enable debugging of elaboration by dumping elaboration * process information to the file named . * * entities= @@ -76,6 +76,8 @@ const char NOTICE[] = # include "compiler.h" # include "library.h" +# include "std_funcs.h" +# include "std_types.h" # include "parse_api.h" # include "vtype.h" # include @@ -184,6 +186,8 @@ int main(int argc, char*argv[]) library_set_work_path(work_path); preload_global_types(); + preload_std_funcs(); + int errors = 0; for (int idx = optind ; idx < argc ; idx += 1) { @@ -232,18 +236,27 @@ int main(int argc, char*argv[]) return 3; } + errors = elaborate_libraries(); + if (errors > 0) { + fprintf(stderr, "%d errors elaborating libraries.\n", errors); + parser_cleanup(); + return 4; + } + + emit_std_types(cout); + errors = emit_packages(); if (errors > 0) { fprintf(stderr, "%d errors emitting packages.\n", errors); parser_cleanup(); - return 4; + return 5; } errors = emit_entities(); if (errors > 0) { fprintf(stderr, "%d errors emitting design.\n", errors); - parser_cleanup(); - return 4; + parser_cleanup(); + return 6; } parser_cleanup(); diff --git a/vhdlpp/package.cc b/vhdlpp/package.cc index df8f60c41..c8ff5b466 100644 --- a/vhdlpp/package.cc +++ b/vhdlpp/package.cc @@ -22,7 +22,10 @@ # include "entity.h" # include "subprogram.h" # include "parse_misc.h" +# include "std_types.h" # include "ivl_assert.h" +# include +# include Package::Package(perm_string n, const ActiveScope&ref) : Scope(ref), name_(n) @@ -40,6 +43,24 @@ void Package::set_library(perm_string lname) from_library_ = lname; } +int Package::elaborate() +{ + int errors = 0; + + for (map::iterator cur = cur_subprograms_.begin() + ; cur != cur_subprograms_.end() ; ++ cur) { + SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + (*it)->set_package(this); + errors += (*it)->elaborate(); + } + } + + return errors; +} + /* * The Package::write_to_stream is used to write the package to the * work space (or library) so writes proper VHDL that the library @@ -54,40 +75,30 @@ void Package::write_to_stream(ostream&fd) const // and identifiers. for (map::const_iterator cur = use_types_.begin() ; cur != use_types_.end() ; ++cur) { - const VTypeDef*def = dynamic_cast (cur->second); - if (def == 0) + + // Do not include global types in types dump + if (is_global_type(cur->first)) continue; + fd << "type " << cur->first << ";" << endl; } for (map::const_iterator cur = cur_types_.begin() ; cur != cur_types_.end() ; ++cur) { - const VTypeDef*def = dynamic_cast (cur->second); - if (def == 0) + + // Do not include global types in types dump + if (is_global_type(cur->first)) continue; + fd << "type " << cur->first << ";" << endl; } for (map::const_iterator cur = use_types_.begin() ; cur != use_types_.end() ; ++cur) { - - // Do not include global types in types dump - if (is_global_type(cur->first)) - continue; - - fd << "type " << cur->first << " is "; - cur->second->write_type_to_stream(fd); - fd << "; -- imported" << endl; + cur->second->write_typedef_to_stream(fd, cur->first); } for (map::const_iterator cur = cur_types_.begin() ; cur != cur_types_.end() ; ++cur) { - - // Do not include global types in types dump - if (is_global_type(cur->first)) - continue; - - fd << "type " << cur->first << " is "; - cur->second->write_type_to_stream(fd); - fd << ";" << endl; + cur->second->write_typedef_to_stream(fd, cur->first); } for (map::const_iterator cur = cur_constants_.begin() @@ -105,9 +116,15 @@ void Package::write_to_stream(ostream&fd) const fd << ";" << endl; } - for (map::const_iterator cur = cur_subprograms_.begin() + for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++cur) { - cur->second->write_to_stream(fd); + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + (*it)->write_to_stream(fd); + fd << ";" << endl; + } } for (map::const_iterator cur = old_components_.begin() @@ -124,9 +141,20 @@ void Package::write_to_stream(ostream&fd) const fd << "end package " << name_ << ";" << endl; fd << "package body " << name_ << " is" << endl; - for (map::const_iterator cur = cur_subprograms_.begin() + for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++cur) { - cur->second->write_to_stream_body(fd); - } + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + const SubprogramHeader*subp = *it; + + if(subp->body()) { + subp->write_to_stream(fd); + fd << " is" << endl; + subp->body()->write_to_stream(fd); + } + } + } fd << "end " << name_ << ";" << endl; } diff --git a/vhdlpp/package.h b/vhdlpp/package.h index a8e303cd5..7ae45120a 100644 --- a/vhdlpp/package.h +++ b/vhdlpp/package.h @@ -36,12 +36,11 @@ class Package : public Scope, public LineInfo { perm_string name() const { return name_; } - Subprogram* recall_subprogram(perm_string name) const; - // This method writes a package header to a library file. void write_to_stream(std::ostream&fd) const; int emit_package(std::ostream&fd) const; + int elaborate(); private: perm_string from_library_; diff --git a/vhdlpp/package_emit.cc b/vhdlpp/package_emit.cc index d8db4494d..962f68a65 100644 --- a/vhdlpp/package_emit.cc +++ b/vhdlpp/package_emit.cc @@ -20,8 +20,9 @@ # include "package.h" # include "subprogram.h" -# include # include "ivl_assert.h" +# include +# include using namespace std; @@ -64,14 +65,22 @@ int Package::emit_package(ostream&fd) const //} fd << "package \\" << name() << " ;" << endl; - for (map::const_iterator cur = cur_subprograms_.begin() + for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++ cur) { - // Do not emit unbounded functions, we will just need fixed instances later - if(!cur->second->unbounded()) - errors += cur->second->emit_package(fd); - else - fd << "/* function " << cur->second->name() << - " has to be instantiated, skipping */" << endl; + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + SubprogramHeader*header = *it; + + // Do not emit unbounded functions, we will just need fixed instances later + if(!header->unbounded()) + errors += header->emit_package(fd); + else + fd << "/* function " << header->name() + << " has to be instantiated, skipping */" << endl; + } + } fd << "endpackage /* " << name() << " */" << endl; diff --git a/vhdlpp/parse.y b/vhdlpp/parse.y index 146f984ac..6b4c13e5d 100644 --- a/vhdlpp/parse.y +++ b/vhdlpp/parse.y @@ -7,7 +7,7 @@ %{ /* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) - * Copyright CERN 2012-2014 / Stephen Williams (steve@icarus.com), + * Copyright CERN 2012-2016 / Stephen Williams (steve@icarus.com), * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it @@ -39,6 +39,8 @@ # include "package.h" # include "vsignal.h" # include "vtype.h" +# include "std_funcs.h" +# include "std_types.h" # include # include # include @@ -83,11 +85,8 @@ extern int yylex(union YYSTYPE*yylvalp,YYLTYPE*yyllocp,yyscan_t yyscanner); */ static ActiveScope*active_scope = new ActiveScope; static stack scope_stack; -static Subprogram*active_sub = NULL; - -// perm_strings for attributes -const static perm_string left_attr = perm_string::literal("left"); -const static perm_string right_attr = perm_string::literal("right"); +static SubprogramHeader*active_sub = NULL; +static ActiveScope*arc_scope = NULL; /* * When a scope boundary starts, call the push_scope function to push @@ -146,6 +145,7 @@ void parser_cleanup(void) { delete_design_entities(); delete_global_scope(); + delete_std_funcs(); lex_strings.cleanup(); } @@ -228,8 +228,8 @@ static void touchup_interface_for_functions(std::list*ports) IfSequential::Elsif*elsif; std::list*elsif_list; - ExpConditional::else_t*exp_else; - std::list*exp_else_list; + ExpConditional::case_t*exp_options; + std::list*exp_options_list; CaseSeqStmt::CaseStmtAlternative* case_alt; std::list* case_alt_list; @@ -242,8 +242,9 @@ static void touchup_interface_for_functions(std::list*ports) const VType* vtype; - prange_t* range; - std::list*range_list; + ExpRange*range; + std::list*range_list; + ExpRange::range_dir_t range_dir; ExpArithmetic::fun_t arithmetic_op; std::list*adding_terms; @@ -260,7 +261,11 @@ static void touchup_interface_for_functions(std::list*ports) Architecture::Statement* arch_statement; std::list* arch_statement_list; - Subprogram*subprogram; + ReportStmt::severity_t severity; + + SubprogramHeader*subprogram; + + file_open_info_t*file_info; }; /* The keywords are all tokens. */ @@ -298,14 +303,13 @@ static void touchup_interface_for_functions(std::list*ports) /* The rules may have types. */ -%type direction - %type adding_operator %type simple_expression_terms %type interface_element interface_list %type port_clause port_clause_opt %type generic_clause generic_clause_opt +%type parameter_list parameter_list_opt %type mode mode_opt %type entity_aspect entity_aspect_opt binding_indication binding_indication_semicolon_opt @@ -315,8 +319,9 @@ static void touchup_interface_for_functions(std::list*ports) %type concurrent_statement component_instantiation_statement %type concurrent_conditional_signal_assignment %type concurrent_signal_assignment_statement concurrent_simple_signal_assignment +%type concurrent_assertion_statement %type for_generate_statement generate_statement if_generate_statement -%type process_statement +%type process_statement selected_signal_assignment %type architecture_statement_part generate_statement_body %type choice @@ -327,16 +332,18 @@ static void touchup_interface_for_functions(std::list*ports) %type expression factor primary relation %type expression_logical expression_logical_and expression_logical_or %type expression_logical_xnor expression_logical_xor -%type name prefix selected_name +%type name prefix selected_name indexed_name %type shift_expression signal_declaration_assign_opt -%type simple_expression simple_expression_2 term waveform_element -%type interface_element_expression +%type simple_expression simple_expression_2 term +%type variable_declaration_assign_opt waveform_element interface_element_expression %type waveform waveform_elements -%type name_list expression_list +%type name_list expression_list argument_list argument_list_opt %type process_sensitivity_list process_sensitivity_list_opt %type selected_names use_clause +%type file_open_information file_open_information_opt + %type association_element %type association_list port_map_aspect port_map_aspect_opt %type generic_map_aspect generic_map_aspect_opt @@ -347,19 +354,22 @@ static void touchup_interface_for_functions(std::list*ports) %type element_declaration element_declaration_list %type architecture_body_start package_declaration_start -%type package_body_start +%type package_body_start process_start %type identifier_opt identifier_colon_opt logical_name suffix instantiated_unit + %type logical_name_list identifier_list %type enumeration_literal_list enumeration_literal -%type if_statement_else sequence_of_statements subprogram_statement_part +%type if_statement_else list_of_statements +%type sequence_of_statements subprogram_statement_part %type sequential_statement if_statement signal_assignment signal_assignment_statement %type case_statement procedure_call procedure_call_statement %type loop_statement variable_assignment variable_assignment_statement -%type return_statement +%type assertion_statement report_statement return_statement wait_statement %type range %type range_list index_constraint +%type direction %type case_statement_alternative %type case_statement_alternative_list @@ -367,10 +377,13 @@ static void touchup_interface_for_functions(std::list*ports) %type if_statement_elsif %type if_statement_elsif_list if_statement_elsif_list_opt -%type else_when_waveform -%type else_when_waveforms +%type else_when_waveform selected_waveform +%type else_when_waveforms else_when_waveforms_opt selected_waveform_list -%type function_specification subprogram_specification subprogram_body_start +%type function_specification procedure_specification +%type subprogram_specification subprogram_body_start + +%type severity severity_opt %% @@ -400,6 +413,8 @@ architecture_body delete[]$3; delete $8; pop_scope(); + assert(arc_scope); + arc_scope = NULL; if ($11) delete[]$11; } ; @@ -408,6 +423,8 @@ architecture_body_start : K_architecture IDENTIFIER { $$ = $2; push_scope(); + assert(!arc_scope); + arc_scope = active_scope; } ; /* @@ -433,6 +450,29 @@ architecture_statement_part } ; +argument_list : '(' expression_list ')' { $$ = $2; }; + +argument_list_opt + : argument_list { $$ = $1; } + | { $$ = 0; } + ; + +assertion_statement + : K_assert expression report_statement + { ReportStmt*report = dynamic_cast($3); + assert(report); + AssertStmt*tmp = new AssertStmt($2, report->message(), report->severity()); + delete report; + FILE_NAME(tmp,@2); + $$ = tmp; + } + | K_assert expression severity_opt ';' + { AssertStmt*tmp = new AssertStmt($2, NULL, $3); + FILE_NAME(tmp,@2); + $$ = tmp; + } + ; + association_element : IDENTIFIER ARROW expression { named_expr_t*tmp = new named_expr_t(lex_strings.make($1), $3); @@ -498,7 +538,7 @@ block_declarative_item { /* Save the signal declaration in the block_signals map. */ for (std::list::iterator cur = $2->begin() ; cur != $2->end() ; ++cur) { - Signal*sig = new Signal(*cur, $4, $5); + Signal*sig = new Signal(*cur, $4, $5 ? $5->clone() : 0); FILE_NAME(sig, @1); active_scope->bind_name(*cur, sig); } @@ -513,6 +553,8 @@ block_declarative_item | subprogram_body + | subtype_declaration + | type_declaration | use_clause_lib @@ -638,7 +680,7 @@ component_configuration block_configuration_opt K_end K_for ';' { - sorrymsg(@1, "Component configuration in not yet supported"); + sorrymsg(@1, "Component configuration in not yet supported\n"); if($3) delete $3; delete $2; } @@ -721,33 +763,52 @@ composite_type_definition /* unbounded_array_definition IEEE 1076-2008 P5.3.2.1 */ | K_array '(' index_subtype_definition_list ')' K_of subtype_indication - { std::list r; - r.push_back(new prange_t(NULL, NULL, true)); // NULL boundaries indicate unbounded array type - VTypeArray*tmp = new VTypeArray($6, &r); - $$ = tmp; + { std::list r; + // NULL boundaries indicate unbounded array type + ExpRange*tmp = new ExpRange(NULL, NULL, ExpRange::DOWNTO); + r.push_back(tmp); + FILE_NAME(tmp, @1); + VTypeArray*arr = new VTypeArray($6, &r); + $$ = arr; } | record_type_definition { $$ = $1; } ; +concurrent_assertion_statement + : assertion_statement + { + /* See more explanations at IEEE 1076-2008 P11.5 */ + std::list stmts; + stmts.push_back($1); + stmts.push_back(new WaitStmt(WaitStmt::FINAL, NULL)); + push_scope(); + ProcessStatement*tmp = new ProcessStatement(empty_perm_string, *active_scope, + NULL, &stmts); + pop_scope(); + FILE_NAME(tmp, @1); + $$ = tmp; + } + ; + /* The when...else..when...else syntax is not a general expression in VHDL but a specific sort of assignment statement model. We - create Exppression objects for it, but the parser will only + create Expression objects for it, but the parser will only recognize it it in specific situations. */ concurrent_conditional_signal_assignment /* IEEE 1076-2008 P11.6 */ - : name LEQ waveform K_when expression else_when_waveforms ';' - { ExpConditional*tmp = new ExpConditional($5, $3, $6); - FILE_NAME(tmp, @3); - delete $3; - delete $6; + : name LEQ waveform K_when expression else_when_waveforms_opt ';' + { std::list*options; + options = $6 ? $6 : new std::list; + options->push_front(new ExpConditional::case_t($5, $3)); - ExpName*name = dynamic_cast ($1); - assert(name); - SignalAssignment*tmpa = new SignalAssignment(name, tmp); - FILE_NAME(tmpa, @1); + ExpName*name = dynamic_cast($1); + assert(name); + CondSignalAssignment*tmp = new CondSignalAssignment(name, *options); - $$ = tmpa; + FILE_NAME(tmp, @1); + delete options; + $$ = tmp; } /* Error recovery rules. */ @@ -780,25 +841,31 @@ concurrent_simple_signal_assignment else_when_waveforms : else_when_waveforms else_when_waveform - { list*tmp = $1; + { list*tmp = $1; tmp ->push_back($2); $$ = tmp; } | else_when_waveform - { list*tmp = new list; + { list*tmp = new list; tmp->push_back($1); $$ = tmp; } ; +else_when_waveforms_opt + : else_when_waveforms { $$ = $1; } + | { $$ = 0; } + ; + + else_when_waveform : K_else waveform K_when expression - { ExpConditional::else_t*tmp = new ExpConditional::else_t($4, $2); + { ExpConditional::case_t*tmp = new ExpConditional::case_t($4, $2); FILE_NAME(tmp, @1); $$ = tmp; } | K_else waveform - { ExpConditional::else_t*tmp = new ExpConditional::else_t(0, $2); + { ExpConditional::case_t*tmp = new ExpConditional::case_t(0, $2); FILE_NAME(tmp, @1); $$ = tmp; } @@ -807,11 +874,21 @@ else_when_waveform concurrent_signal_assignment_statement /* IEEE 1076-2008 P11.6 */ : concurrent_simple_signal_assignment - | IDENTIFIER ':' concurrent_simple_signal_assignment { $$ = $3; } + | IDENTIFIER ':' concurrent_simple_signal_assignment + { delete[] $1; + $$ = $3; + } | concurrent_conditional_signal_assignment - | IDENTIFIER ':' concurrent_conditional_signal_assignment { $$ = $3; } + | IDENTIFIER ':' concurrent_conditional_signal_assignment + { delete[] $1; + $$ = $3; + } + + | selected_signal_assignment + + | IDENTIFIER ':' selected_signal_assignment { $$ = $3; } | name LEQ error ';' { errormsg(@2, "Syntax error in signal assignment waveform.\n"); @@ -830,6 +907,7 @@ concurrent_signal_assignment_statement /* IEEE 1076-2008 P11.6 */ concurrent_statement : component_instantiation_statement | concurrent_signal_assignment_statement + | concurrent_assertion_statement | generate_statement | process_statement ; @@ -841,7 +919,7 @@ configuration_declaration K_end K_configuration_opt identifier_opt ';' { if(design_entities.find(lex_strings.make($4)) == design_entities.end()) - errormsg(@4, "Couldn't find entity %s used in configuration declaration", $4); + errormsg(@4, "Couldn't find entity %s used in configuration declaration\n", $4); //choose_architecture_for_entity(); sorrymsg(@1, "Configuration declaration is not yet supported.\n"); } @@ -883,7 +961,7 @@ configuration_items_opt constant_declaration : K_constant identifier_list ':' subtype_indication VASSIGN expression ';' - { // The syntax allows mutliple names to have the same type/value. + { // The syntax allows multiple names to have the same type/value. for (std::list::iterator cur = $2->begin() ; cur != $2->end() ; ++cur) { active_scope->bind_name(*cur, $4, $6); @@ -898,7 +976,7 @@ constant_declaration /* Some error handling... */ | K_constant identifier_list ':' subtype_indication VASSIGN error ';' - { // The syntax allows mutliple names to have the same type/value. + { // The syntax allows multiple names to have the same type/value. errormsg(@6, "Error in value expression for constants.\n"); yyerrok; for (std::list::iterator cur = $2->begin() @@ -942,12 +1020,15 @@ design_units | design_unit ; - /* Indicate the direction as a flag, with "downto" being TRUE. */ -direction : K_to { $$ = false; } | K_downto { $$ = true; } ; +direction + : K_to { $$ = ExpRange::TO; } + | K_downto { $$ = ExpRange::DOWNTO; } + ; element_association : choices ARROW expression { ExpAggregate::element_t*tmp = new ExpAggregate::element_t($1, $3); + delete $1; $$ = tmp; } | expression @@ -971,7 +1052,9 @@ element_association_list element_declaration : identifier_list ':' subtype_indication ';' - { $$ = record_elements($1, $3); } + { $$ = record_elements($1, $3); + delete $1; + } ; element_declaration_list @@ -1076,6 +1159,8 @@ expression_list expression : expression_logical { $$ = $1; } + | range + { $$ = $1; } ; /* @@ -1195,6 +1280,68 @@ factor } ; +file_declaration + : K_file identifier_list ':' IDENTIFIER file_open_information_opt ';' + { + if (strcasecmp($4, "TEXT")) + sorrymsg(@1, "file declaration currently handles only TEXT type.\n"); + + for (std::list::iterator cur = $2->begin() + ; cur != $2->end() ; ++cur) { + Variable*var = new Variable(*cur, &primitive_INTEGER); + FILE_NAME(var, @1); + active_scope->bind_name(*cur, var); + + // there was a file name specified, so it needs an implicit call + // to open it at the beginning of simulation and close it at the end + if($5) { + std::list params; + + // add file_open() call in 'initial' block + params.push_back(new ExpScopedName(active_scope->peek_name(), new ExpName(*cur))); + params.push_back($5->filename()->clone()); + params.push_back($5->kind()->clone()); + ProcedureCall*fopen_call = new ProcedureCall( + perm_string::literal("file_open"), ¶ms); + arc_scope->add_initializer(fopen_call); + + // add file_close() call in 'final' block + params.clear(); + params.push_back(new ExpScopedName(active_scope->peek_name(), new ExpName(*cur))); + ProcedureCall*fclose_call = new ProcedureCall( + perm_string::literal("file_close"), ¶ms); + arc_scope->add_finalizer(fclose_call); + + delete $5; + } + } + + delete $2; + } + | K_file error ';' + { errormsg(@2, "Syntax error in file declaration.\n"); + yyerrok; + } + ; + +file_open_information + : K_open IDENTIFIER K_is STRING_LITERAL + { + ExpName*mode = new ExpName(lex_strings.make($2)); + delete[]$2; + FILE_NAME(mode, @1); + $$ = new file_open_info_t(new ExpString($4), mode); + } + | K_is STRING_LITERAL + { + $$ = new file_open_info_t(new ExpString($2)); + } + +file_open_information_opt + : file_open_information { $$ = $1; } + | { $$ = 0; } + ; + for_generate_statement : IDENTIFIER ':' K_for IDENTIFIER K_in range K_generate generate_statement_body @@ -1217,15 +1364,15 @@ for_generate_statement ; function_specification /* IEEE 1076-2008 P4.2.1 */ - : K_function IDENTIFIER '(' interface_list ')' K_return IDENTIFIER - { perm_string type_name = lex_strings.make($7); + : K_function IDENTIFIER parameter_list K_return IDENTIFIER + { perm_string type_name = lex_strings.make($5); perm_string name = lex_strings.make($2); const VType*type_mark = active_scope->find_type(type_name); - touchup_interface_for_functions($4); - Subprogram*tmp = new Subprogram(name, $4, type_mark); - FILE_NAME(tmp,@1); + touchup_interface_for_functions($3); + SubprogramHeader*tmp = new SubprogramHeader(name, $3, type_mark); + FILE_NAME(tmp, @1); delete[]$2; - delete[]$7; + delete[]$5; $$ = tmp; } ; @@ -1247,8 +1394,8 @@ generic_clause_opt ; generic_clause - : K_generic '(' interface_list ')' ';' - { $$ = $3; } + : K_generic parameter_list ';' + { $$ = $2; } | K_generic '(' error ')' ';' { errormsg(@3, "Error in interface list for generic.\n"); yyerrok; @@ -1286,7 +1433,7 @@ identifier_list } ; -identifier_opt : IDENTIFIER { $$ = $1; } | { $$ = 0; } ; +identifier_opt : IDENTIFIER { $$ = $1; } | { $$ = 0; } ; identifier_colon_opt : IDENTIFIER ':' { $$ = $1; } | { $$ = 0; }; @@ -1397,7 +1544,7 @@ index_constraint | '(' error ')' { errormsg(@2, "Errors in the index constraint.\n"); yyerrok; - $$ = new list; + $$ = new list; } ; @@ -1513,14 +1660,8 @@ loop_statement if($1) delete[]$1; if($8) delete[]$8; - ExpLogical* cond = dynamic_cast($3); - if(!cond) { - errormsg(@3, "Iteration condition is not a correct logical expression.\n"); - } - WhileLoopStatement* tmp = new WhileLoopStatement(loop_name, cond, $5); + WhileLoopStatement* tmp = new WhileLoopStatement(loop_name, $3, $5); FILE_NAME(tmp, @1); - - sorrymsg(@1, "Loop statements are not supported.\n"); $$ = tmp; } @@ -1557,59 +1698,71 @@ loop_statement BasicLoopStatement* tmp = new BasicLoopStatement(loop_name, $3); FILE_NAME(tmp, @2); - sorrymsg(@1, "Loop statements are not supported.\n"); $$ = tmp; }; mode : K_in { $$ = PORT_IN; } | K_out { $$ = PORT_OUT; } + | K_inout { $$ = PORT_INOUT; } ; mode_opt : mode {$$ = $1;} | {$$ = PORT_NONE;} ; name /* IEEE 1076-2008 P8.1 */ : IDENTIFIER /* simple_name (IEEE 1076-2008 P8.2) */ - { ExpName*tmp = new ExpName(lex_strings.make($1)); - FILE_NAME(tmp, @1); - delete[]$1; - $$ = tmp; + { Expression*tmp; + /* Check if the IDENTIFIER is one of CHARACTER enums (LF, CR, etc.) */ + tmp = parse_char_enums($1); + if(!tmp) { + perm_string name = lex_strings.make($1); + /* There are functions that have the same name types, e.g. integer */ + if(!active_scope->find_subprogram(name).empty() && !parse_type_by_name(name)) + tmp = new ExpFunc(name); + else + tmp = new ExpName(name); + } + FILE_NAME(tmp, @1); + delete[]$1; + $$ = tmp; } | selected_name { $$ = $1; } + | indexed_name + { $$ = $1; } + + | selected_name '(' expression_list ')' + { + ExpName*name = dynamic_cast($1); + assert(name); + name->add_index($3); + delete $3; // contents of the list is moved to the selected_name + } + ; + +indexed_name /* Note that this rule can match array element selects and various function calls. The only way we can tell the difference is from left context, namely whether the name is a type name or function name. If none of the above, treat it as a array element select. */ - | IDENTIFIER '(' expression_list ')' - { perm_string name = lex_strings.make($1); - delete[]$1; - if (active_scope->is_vector_name(name) || is_subprogram_param(name)) { - ExpName*tmp = new ExpName(name, $3); - $$ = tmp; - } else { - ExpFunc*tmp = new ExpFunc(name, $3); - $$ = tmp; - } - FILE_NAME($$, @1); + : IDENTIFIER '(' expression_list ')' + { Expression*tmp; + perm_string name = lex_strings.make($1); + delete[]$1; + if (active_scope->is_vector_name(name) || is_subprogram_param(name)) + tmp = new ExpName(name, $3); + else + tmp = new ExpFunc(name, $3); + FILE_NAME(tmp, @1); + $$ = tmp; } - | IDENTIFIER '(' range ')' - { ExpName*tmp = new ExpName(lex_strings.make($1), $3->msb(), $3->lsb()); - FILE_NAME(tmp, @1); - delete[]$1; - $$ = tmp; - } - | selected_name '(' range ')' - { ExpName*tmp = dynamic_cast ($1); - tmp->set_range($3->msb(), $3->lsb()); - $$ = tmp; - } - | selected_name '(' expression ')' - { ExpName*tmp = dynamic_cast ($1); - tmp->set_range($3, NULL); - $$ = tmp; + | indexed_name '(' expression_list ')' + { ExpName*name = dynamic_cast($1); + assert(name); + name->add_index($3); + $$ = $1; } ; @@ -1736,9 +1889,18 @@ package_body_start } ; +parameter_list + : '(' interface_list ')' { $$ = $2; } + ; + +parameter_list_opt + : parameter_list { $$ = $1; } + | { $$ = 0; } + ; + port_clause - : K_port '(' interface_list ')' ';' - { $$ = $3; } + : K_port parameter_list ';' + { $$ = $2; } | K_port '(' error ')' ';' { errormsg(@1, "Syntax error in port list.\n"); yyerrok; @@ -1771,11 +1933,19 @@ prefix /* IEEE 1076-2008 P8.1 */ primary : name { $$ = $1; } - | name '\'' IDENTIFIER - { perm_string name = lex_strings.make($3); - ExpName*base = dynamic_cast ($1); - ExpAttribute*tmp = new ExpAttribute(base, name); - FILE_NAME(tmp, @3); + | name '\'' IDENTIFIER argument_list_opt + { ExpAttribute*tmp = NULL; + perm_string attr = lex_strings.make($3); + ExpName*base = dynamic_cast($1); + const VType*type = parse_type_by_name(base->peek_name()); + + if(type) { + tmp = new ExpTypeAttribute(type, attr, $4); + } else { + tmp = new ExpObjAttribute(base, attr, $4); + } + + FILE_NAME(tmp, @1); delete[]$3; $$ = tmp; } @@ -1792,8 +1962,8 @@ primary } | REAL_LITERAL { ExpReal*tmp = new ExpReal($1); - FILE_NAME(tmp, @1); - $$ = tmp; + FILE_NAME(tmp, @1); + $$ = tmp; } | STRING_LITERAL { ExpString*tmp = new ExpString($1); @@ -1807,6 +1977,33 @@ primary delete[]$1; $$ = tmp; } + | INT_LITERAL IDENTIFIER + { ExpTime::timeunit_t unit = ExpTime::FS; + + if(!strcasecmp($2, "us")) + unit = ExpTime::US; + else if(!strcasecmp($2, "ms")) + unit = ExpTime::MS; + else if(!strcasecmp($2, "ns")) + unit = ExpTime::NS; + else if(!strcasecmp($2, "s")) + unit = ExpTime::S; + else if(!strcasecmp($2, "ps")) + unit = ExpTime::PS; + else if(!strcasecmp($2, "fs")) + unit = ExpTime::FS; + else + errormsg(@2, "Invalid time unit (accepted are fs, ps, ns, us, ms, s).\n"); + + if($1 < 0) + errormsg(@1, "Time cannot be negative.\n"); + + ExpTime*tmp = new ExpTime($1, unit); + FILE_NAME(tmp, @1); + delete[] $2; + $$ = tmp; + } + /*XXXX Caught up in element_association_list? | '(' expression ')' { $$ = $2; } @@ -1817,6 +2014,7 @@ primary VHDL syntax). */ | IDENTIFIER '(' association_list ')' { sorrymsg(@1, "Function calls not supported\n"); + delete[] $1; $$ = 0; } @@ -1835,19 +2033,29 @@ primary_unit ; procedure_call - : IDENTIFIER + : IDENTIFIER ';' { ProcedureCall* tmp = new ProcedureCall(lex_strings.make($1)); - sorrymsg(@1, "Procedure calls are not supported.\n"); + FILE_NAME(tmp, @1); + delete[] $1; $$ = tmp; } - | IDENTIFIER '(' association_list ')' + | IDENTIFIER '(' association_list ')' ';' { ProcedureCall* tmp = new ProcedureCall(lex_strings.make($1), $3); - sorrymsg(@1, "Procedure calls are not supported.\n"); + FILE_NAME(tmp, @1); + delete[] $1; $$ = tmp; } - | IDENTIFIER '(' error ')' + | IDENTIFIER argument_list ';' + { + ProcedureCall* tmp = new ProcedureCall(lex_strings.make($1), $2); + FILE_NAME(tmp, @1); + delete[] $1; + delete $2; // parameters are copied in this variant + $$ = tmp; + } + | IDENTIFIER '(' error ')' ';' { errormsg(@1, "Errors in procedure call.\n"); yyerrok; delete[]$1; @@ -1856,12 +2064,27 @@ procedure_call ; procedure_call_statement - : IDENTIFIER ':' procedure_call { $$ = $3; } + : IDENTIFIER ':' procedure_call + { delete[] $1; + $$ = $3; + } | procedure_call { $$ = $1; } ; +procedure_specification /* IEEE 1076-2008 P4.2.1 */ + : K_procedure IDENTIFIER parameter_list_opt + { perm_string name = lex_strings.make($2); + touchup_interface_for_functions($3); + SubprogramHeader*tmp = new SubprogramHeader(name, $3, NULL); + FILE_NAME(tmp, @1); + delete[]$2; + $$ = tmp; + } + ; + process_declarative_item : variable_declaration + | file_declaration ; process_declarative_part @@ -1874,33 +2097,40 @@ process_declarative_part_opt | ; -process_statement +process_start : identifier_colon_opt K_postponed_opt K_process - process_sensitivity_list_opt K_is_opt + { push_scope(); + $$ = $1; + } + ; + +process_statement + : process_start process_sensitivity_list_opt K_is_opt process_declarative_part_opt K_begin sequence_of_statements K_end K_postponed_opt K_process identifier_opt ';' - { perm_string iname = $1? lex_strings.make($1) : perm_string(); + { perm_string iname = $1? lex_strings.make($1) : empty_perm_string; if ($1) delete[]$1; - if ($12) { + if ($10) { if (iname.nil()) { - errormsg(@12, "Process end name %s for un-named processes.\n", $12); - } else if (iname != $12) { - errormsg(@12, "Process name %s does not match opening name %s.\n", - $12, $1); + errormsg(@10, "Process end name %s for un-named processes.\n", $10); + } else if (iname != $10) { + errormsg(@10, "Process name %s does not match opening name %s.\n", + $10, $1); } - delete[]$12; + delete[]$10; } - ProcessStatement*tmp = new ProcessStatement(iname, $4, $8); + ProcessStatement*tmp = new ProcessStatement(iname, *active_scope, $2, $6); + arc_scope->bind_scope(tmp->peek_name(), tmp); + pop_scope(); FILE_NAME(tmp, @3); - delete $4; - delete $8; + delete $2; + delete $6; $$ = tmp; } - | identifier_colon_opt K_postponed_opt K_process - process_sensitivity_list_opt K_is_opt + | process_start process_sensitivity_list_opt K_is_opt process_declarative_part_opt K_begin error K_end K_postponed_opt K_process identifier_opt ';' @@ -1911,7 +2141,7 @@ process_statement ; /* - * A process_sentitivity_list is: + * A process_sensitivity_list is: * if the list is not present, or * or a non-empty list of actual expressions. */ @@ -1941,18 +2171,17 @@ process_sensitivity_list range : simple_expression direction simple_expression - { prange_t* tmp = new prange_t($1, $3, $2); + { ExpRange* tmp = new ExpRange($1, $3, $2); + FILE_NAME(tmp, @1); $$ = tmp; } | name '\'' K_range { - prange_t*tmp = NULL; + ExpRange*tmp = NULL; ExpName*name = NULL; if((name = dynamic_cast($1))) { - ExpAttribute*left = new ExpAttribute(name, left_attr); - ExpAttribute*right = new ExpAttribute(name, right_attr); - tmp = new prange_t(left, right, true); - tmp->set_auto_dir(); + tmp = new ExpRange(name, false); + FILE_NAME(tmp, @1); } else { errormsg(@1, "'range attribute can be used with named expressions only"); } @@ -1960,13 +2189,11 @@ range } | name '\'' K_reverse_range { - prange_t*tmp = NULL; + ExpRange*tmp = NULL; ExpName*name = NULL; if((name = dynamic_cast($1))) { - ExpAttribute*left = new ExpAttribute(name, left_attr); - ExpAttribute*right = new ExpAttribute(name, right_attr); - tmp = new prange_t(left, right, false); - tmp->set_auto_dir(); + tmp = new ExpRange(name, true); + FILE_NAME(tmp, @1); } else { errormsg(@1, "'reverse_range attribute can be used with named expressions only"); } @@ -1976,12 +2203,12 @@ range range_list : range - { list*tmp = new list; + { list*tmp = new list; tmp->push_back($1); $$ = tmp; } | range_list ',' range - { list*tmp = $1; + { list*tmp = $1; tmp->push_back($3); $$ = tmp; } @@ -2029,6 +2256,13 @@ relation } ; +report_statement + : K_report expression severity_opt ';' + { ReportStmt*tmp = new ReportStmt($2, $3); + FILE_NAME(tmp,@2); + $$ = tmp; + } + return_statement : K_return expression ';' { ReturnStmt*tmp = new ReturnStmt($2); @@ -2112,9 +2346,50 @@ selected_names_lib | selected_name_lib ; +selected_signal_assignment + : K_with expression K_select name LEQ selected_waveform_list ';' + { ExpSelected*tmp = new ExpSelected($2, $6); + FILE_NAME(tmp, @3); + delete $2; + delete $6; -sequence_of_statements - : sequence_of_statements sequential_statement + ExpName*name = dynamic_cast($4); + assert(name); + SignalAssignment*tmpa = new SignalAssignment(name, tmp); + FILE_NAME(tmpa, @1); + + $$ = tmpa; + } + ; + +selected_waveform + : waveform K_when expression + { ExpConditional::case_t*tmp = new ExpConditional::case_t($3, $1); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | waveform K_when K_others + { ExpConditional::case_t*tmp = new ExpConditional::case_t(0, $1); + FILE_NAME(tmp, @1); + $$ = tmp; + } + ; + +selected_waveform_list + : selected_waveform_list ',' selected_waveform + { list*tmp = $1; + tmp->push_back($3); + $$ = tmp; + } + | selected_waveform + { list*tmp = new list; + tmp->push_back($1); + $$ = tmp; + } + ; + +list_of_statements + : list_of_statements sequential_statement { std::list*tmp = $1; if($2) tmp->push_back($2); @@ -2128,6 +2403,11 @@ sequence_of_statements } ; +sequence_of_statements + : list_of_statements { $1 = $$; } + | { $$ = NULL; } + ; + sequential_statement : if_statement { $$ = $1; } | signal_assignment_statement { $$ = $1; } @@ -2136,6 +2416,9 @@ sequential_statement | procedure_call_statement { $$ = $1; } | loop_statement { $$ = $1; } | return_statement { $$ = $1; } + | report_statement { $$ = $1; } + | assertion_statement { $$ = $1; } + | wait_statement { $$ = $1; } | K_null ';' { $$ = 0; } | error ';' { errormsg(@1, "Syntax error in sequential statement.\n"); @@ -2144,6 +2427,27 @@ sequential_statement } ; +severity + : K_severity IDENTIFIER + { if(!strcasecmp($2, "NOTE")) + $$ = ReportStmt::NOTE; + else if(!strcasecmp($2, "WARNING")) + $$ = ReportStmt::WARNING; + else if(!strcasecmp($2, "ERROR")) + $$ = ReportStmt::ERROR; + else if(!strcasecmp($2, "FAILURE")) + $$ = ReportStmt::FAILURE; + else { + errormsg(@1, "Invalid severity level (possible values: NOTE, WARNING, ERROR, FAILURE).\n"); + $$ = ReportStmt::UNSPECIFIED; + } + delete[] $2; + } + +severity_opt + : severity { $$ = $1; } + | { $$ = ReportStmt::UNSPECIFIED; } + shift_expression : simple_expression | simple_expression K_srl simple_expression @@ -2180,11 +2484,6 @@ shift_expression } ; -sign - : '+' - | '-' - ; - signal_declaration_assign_opt : VASSIGN expression { $$ = $2; } | { $$ = 0; } @@ -2196,9 +2495,9 @@ signal_declaration_assign_opt * * This is functionally a list of terms, with the adding_operator used * as a list element separator instead of a ','. The LRM rule, - * however, is right-recursive, which is not to nice is real LALR + * however, is right-recursive, which is not too nice in real LALR * parsers. The solution is to rewrite it as below, to make it - * left-recursive. This is must more effecient use of the parse stack. + * left-recursive. This is much more efficient use of the parse stack. * * Note that although the concatenation operator '&' is syntactically * an addition operator, it is handled differently during elaboration @@ -2210,10 +2509,10 @@ signal_declaration_assign_opt * list fixes up the associations. */ simple_expression - : sign simple_expression_2 - { sorrymsg(@1, "Unary expression +- not supported.\n"); - $$ = $2; - } + : '-' simple_expression_2 + { $$ = new ExpUMinus($2); } + | '+' simple_expression_2 + { $$ = $2; } | simple_expression_2 { $$ = $1; } ; @@ -2233,6 +2532,7 @@ simple_expression_2 tmp = new ExpArithmetic(item.op, tmp, item.term); } delete lst; + FILE_NAME(tmp, @1); $$ = tmp; } ; @@ -2273,7 +2573,10 @@ signal_assignment signal_assignment_statement : signal_assignment - | IDENTIFIER ':' signal_assignment { $$ = $3; } + | IDENTIFIER ':' signal_assignment + { delete[] $1; + $$ = $3; + } subprogram_body_start : subprogram_specification K_is @@ -2289,17 +2592,19 @@ subprogram_body /* IEEE 1076-2008 P4.3 */ : subprogram_body_start subprogram_declarative_part K_begin subprogram_statement_part K_end subprogram_kind_opt identifier_opt ';' - { Subprogram*prog = $1; - Subprogram*tmp = active_scope->recall_subprogram(prog->name()); - if (tmp && prog->compare_specification(tmp)) { + { SubprogramHeader*prog = $1; + SubprogramHeader*tmp = active_scope->recall_subprogram(prog); + if (tmp) { delete prog; prog = tmp; - } else if (tmp) { - errormsg(@1, "Subprogram specification for %s doesn't match specification in package header.\n", prog->name().str()); } - prog->transfer_from(*active_scope, ScopeBase::VARIABLES); - prog->set_program_body($4); - active_scope->bind_name(prog->name(), prog); + + SubprogramBody*body = new SubprogramBody(); + body->transfer_from(*active_scope, ScopeBase::VARIABLES); + body->set_statements($4); + + prog->set_body(body); + active_scope->bind_subprogram(prog->name(), prog); active_sub = NULL; } @@ -2317,11 +2622,12 @@ subprogram_body /* IEEE 1076-2008 P4.3 */ subprogram_declaration : subprogram_specification ';' - { if ($1) active_scope->bind_name($1->name(), $1); } + { if ($1) active_scope->bind_subprogram($1->name(), $1); } ; subprogram_declarative_item /* IEEE 1079-2008 P4.3 */ : variable_declaration + | file_declaration ; subprogram_declarative_item_list @@ -2343,16 +2649,15 @@ subprogram_kind_opt : subprogram_kind | ; subprogram_specification : function_specification { $$ = $1; } + | procedure_specification { $$ = $1; } ; /* This is an implementation of the rule: subprogram_statement_part ::= { sequential_statement } where the sequence_of_statements rule is a list of - sequential_statement. Also handle the special case of an empty - list here. */ + sequential_statement. */ subprogram_statement_part : sequence_of_statements { $$ = $1; } - | { $$ = 0; } ; subtype_declaration @@ -2361,8 +2666,18 @@ subtype_declaration if ($4 == 0) { errormsg(@1, "Failed to declare type name %s.\n", name.str()); } else { - active_scope->bind_name(name, $4); + VTypeDef*tmp; + map::iterator cur = active_scope->incomplete_types.find(name); + if (cur == active_scope->incomplete_types.end()) { + tmp = new VSubTypeDef(name, $4); + active_scope->bind_name(name, tmp); + } else { + tmp = cur->second; + tmp->set_definition($4); + active_scope->incomplete_types.erase(cur); + } } + delete[]$2; } ; @@ -2402,7 +2717,7 @@ suffix { $$ = $1; } | K_all { //do not have now better idea than using char constant - $$ = strcpy(new char[strlen("all"+1)], "all"); + $$ = strdup("all"); } ; @@ -2447,9 +2762,6 @@ type_declaration tmp->set_definition($4); active_scope->incomplete_types.erase(cur); } - if(const VTypeEnum*enum_type = dynamic_cast($4)) { - active_scope->use_enum(enum_type); - } } delete[]$2; } @@ -2474,6 +2786,7 @@ type_declaration type_definition : '(' enumeration_literal_list ')' { VTypeEnum*tmp = new VTypeEnum($2); + active_scope->use_enum(tmp); delete $2; $$ = tmp; } @@ -2507,7 +2820,10 @@ use_clauses_opt variable_assignment_statement /* IEEE 1076-2008 P10.6.1 */ : variable_assignment - | IDENTIFIER ':' variable_assignment { $$ = $3; } + | IDENTIFIER ':' variable_assignment + { delete[] $1; + $$ = $3; + } variable_assignment : name VASSIGN expression ';' @@ -2530,11 +2846,12 @@ variable_assignment ; variable_declaration /* IEEE 1076-2008 P6.4.2.4 */ - : K_shared_opt K_variable identifier_list ':' subtype_indication ';' + : K_shared_opt K_variable identifier_list ':' subtype_indication + variable_declaration_assign_opt ';' { /* Save the signal declaration in the block_signals map. */ for (std::list::iterator cur = $3->begin() ; cur != $3->end() ; ++cur) { - Variable*sig = new Variable(*cur, $5); + Variable*sig = new Variable(*cur, $5, $6); FILE_NAME(sig, @2); active_scope->bind_name(*cur, sig); } @@ -2546,6 +2863,34 @@ variable_declaration /* IEEE 1076-2008 P6.4.2.4 */ } ; +variable_declaration_assign_opt + : VASSIGN expression { $$ = $2; } + | { $$ = 0; } + ; + +wait_statement + : K_wait K_for expression ';' + { WaitForStmt*tmp = new WaitForStmt($3); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | K_wait K_on expression ';' + { WaitStmt*tmp = new WaitStmt(WaitStmt::ON, $3); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | K_wait K_until expression ';' + { WaitStmt*tmp = new WaitStmt(WaitStmt::UNTIL, $3); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | K_wait ';' + { WaitStmt*tmp = new WaitStmt(WaitStmt::FINAL, NULL); + FILE_NAME(tmp, @1); + $$ = tmp; + } + ; + waveform : waveform_elements { $$ = $1; } @@ -2569,6 +2914,10 @@ waveform_elements waveform_element : expression { $$ = $1; } + | expression K_after expression + { ExpDelay*tmp = new ExpDelay($1, $3); + FILE_NAME(tmp, @1); + $$ = tmp; } | K_null { $$ = 0; } ; diff --git a/vhdlpp/parse_misc.cc b/vhdlpp/parse_misc.cc index f49a631c3..f1d86184d 100644 --- a/vhdlpp/parse_misc.cc +++ b/vhdlpp/parse_misc.cc @@ -29,6 +29,7 @@ # include "compiler.h" # include # include +# include using namespace std; @@ -76,7 +77,7 @@ static const VType* calculate_subtype_array(const YYLTYPE&loc, const char*base_n const VType*base_type = parse_type_by_name(lex_strings.make(base_name)); if (base_type == 0) { - errormsg(loc, "Unable to find base type %s of array.\n", base_name); + errormsg(loc, "Unable to find array base type '%s'.\n", base_name); return 0; } @@ -96,7 +97,7 @@ static const VType* calculate_subtype_array(const YYLTYPE&loc, const char*base_n vector range (base_array->dimensions()); // For now, I only know how to handle 1 dimension - assert(base_array->dimensions() == 1); + assert(base_array->dimensions().size() == 1); range[0] = VTypeArray::range_t(array_left, array_right, downto); @@ -113,14 +114,18 @@ static const VType* calculate_subtype_array(const YYLTYPE&loc, const char*base_n } const VType* calculate_subtype_array(const YYLTYPE&loc, const char*base_name, - ScopeBase*scope, list*ranges) + ScopeBase*scope, list*ranges) { if (ranges->size() == 1) { - prange_t*tmpr = ranges->front(); - Expression*lef = tmpr->expr_left(); - Expression*rig = tmpr->expr_right(); + ExpRange*tmpr = ranges->front(); + Expression*lef = tmpr->left(); + Expression*rig = tmpr->right(); return calculate_subtype_array(loc, base_name, scope, - lef, tmpr->is_downto(), rig); + lef, + (tmpr->direction() == ExpRange::DOWNTO + ? true + : false), + rig); } sorrymsg(loc, "Don't know how to handle multiple ranges here.\n"); @@ -130,28 +135,37 @@ const VType* calculate_subtype_array(const YYLTYPE&loc, const char*base_name, const VType* calculate_subtype_range(const YYLTYPE&loc, const char*base_name, ScopeBase*scope, Expression*range_left, - bool /* downto*/ , + int direction, Expression*range_right) { const VType*base_type = parse_type_by_name(lex_strings.make(base_name)); if (base_type == 0) { - errormsg(loc, "Unable to find base type %s of range.\n", base_name); + errormsg(loc, "Unable to find range base type '%s'.\n", base_name); return 0; } assert(range_left && range_right); int64_t left_val, right_val; - bool rc = range_left->evaluate(scope, left_val); - if (rc == false) - return 0; + VTypeRange*subtype; - rc = range_right->evaluate(scope, right_val); - if (rc == false) - return 0; + if(range_left->evaluate(scope, left_val) && range_right->evaluate(scope, right_val)) { + subtype = new VTypeRangeConst(base_type, left_val, right_val); + } else { + subtype = new VTypeRangeExpr(base_type, range_left, range_right, direction); + } - VTypeRange*sub_type = new VTypeRange(base_type, left_val, right_val); - - return sub_type; + return subtype; +} + +ExpString*parse_char_enums(const char*str) +{ + if(!strcasecmp(str, "LF")) + return new ExpString("\\n"); + + if(!strcasecmp(str, "CR")) + return new ExpString("\\r"); + + return NULL; } diff --git a/vhdlpp/parse_misc.h b/vhdlpp/parse_misc.h index e4aa9e644..d15a8d445 100644 --- a/vhdlpp/parse_misc.h +++ b/vhdlpp/parse_misc.h @@ -26,7 +26,8 @@ class ActiveScope; class Architecture; class Expression; class Package; -class prange_t; +class ExpRange; +class ExpString; class ScopeBase; class VType; @@ -35,11 +36,11 @@ extern void bind_architecture_to_entity(const char*ename, Architecture*arch); extern const VType* calculate_subtype_array(const YYLTYPE&loc, const char*base_name, ScopeBase*scope, - std::list*ranges); + std::list*ranges); extern const VType* calculate_subtype_range(const YYLTYPE&loc, const char*base_name, ScopeBase*scope, Expression*range_left, - bool downto, + int direction, Expression*range_right); /* @@ -63,8 +64,11 @@ extern void library_import(const YYLTYPE&loc, const std::list*names extern void library_use(const YYLTYPE&loc, ActiveScope*res, const char*libname, const char*pack, const char*ident); -extern void generate_global_types(ActiveScope*res); - -extern bool is_global_type(perm_string type_name); +/* + * Converts CHARACTER enums to an ExpString* if applicable. + * See the standard VHDL library (package STANDARD) or VHDL-2008/16.3 + * for more details). + */ +extern ExpString*parse_char_enums(const char*str); #endif /* IVL_parse_misc_H */ diff --git a/vhdlpp/parse_types.h b/vhdlpp/parse_types.h index 3639a9c1f..711d45a0b 100644 --- a/vhdlpp/parse_types.h +++ b/vhdlpp/parse_types.h @@ -1,7 +1,7 @@ #ifndef IVL_parse_types_H #define IVL_parse_types_H /* - * Copyright (c) 2011,2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2015 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -26,7 +26,8 @@ class named_expr_t { public: - named_expr_t (perm_string n, Expression*e) : name_(n), expr_(e) { } + named_expr_t(perm_string n, Expression*e) : name_(n), expr_(e) { } + ~named_expr_t() { delete expr_; } perm_string name() const { return name_; } Expression* expr() const { return expr_; } @@ -68,38 +69,28 @@ class instant_list_t { std::list* labels_; }; -class prange_t { - public: - prange_t(Expression* left, Expression* right, bool dir) - : left_(left), right_(right), direction_(dir), auto_dir_(false) {} - prange_t(const prange_t&other) : - left_(other.left_->clone()), right_(other.right_->clone()), - direction_(other.direction_), auto_dir_(other.auto_dir_) {} - ~prange_t() { delete left_; delete right_; } - void dump(ostream&out, int indent) const; - - inline Expression*msb() { return direction_? left_ : right_; } - inline Expression*lsb() { return direction_? right_: left_; } - - inline bool is_downto() const { return direction_; } - inline void set_auto_dir(bool enabled = true) { auto_dir_ = enabled; }; - inline bool is_auto_dir() const { return auto_dir_; } - - inline Expression*expr_left() { return left_; } - inline Expression*expr_right() { return right_; } - - private: - Expression *left_, *right_; - bool direction_; - bool auto_dir_; - - private: //not implemented - prange_t operator=(const prange_t&); -}; - struct adding_term { ExpArithmetic::fun_t op; Expression*term; }; +// Stores information for file declarations containing a file name and open mode +// (VHDL-2008 6.4.2.5) +class file_open_info_t { + public: + file_open_info_t(ExpString*filename__, ExpName*kind__ = NULL) + : kind_(kind__), filename_(filename__) { + // By default files are opened in read-only mode + if(!kind_) kind_ = new ExpName(perm_string::literal("read_mode")); + } + ~file_open_info_t() { delete kind_; delete filename_; } + + ExpName*kind() { return kind_; } + ExpString*filename() { return filename_; } + + private: + ExpName*kind_; + ExpString*filename_; +}; + #endif /* IVL_parse_types_H */ diff --git a/vhdlpp/scope.cc b/vhdlpp/scope.cc index 0682a45d0..a69bc7558 100644 --- a/vhdlpp/scope.cc +++ b/vhdlpp/scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -22,53 +22,32 @@ # include "package.h" # include "subprogram.h" # include "entity.h" +# include "std_funcs.h" +# include "std_types.h" +# include "compiler.h" # include # include # include +# include +# include # include +# include using namespace std; -/* - * If the merge_flag is passed in, then the new scope is a merge of - * the parent scopes. This brings in all of the parent scopes into the - * "old_*_" variables. This clears up the "new_*_" variables to - * accumulate new scope values. - */ +static int scope_counter = 0; + ScopeBase::ScopeBase(const ActiveScope&ref) -: use_constants_(ref.use_constants_), cur_constants_(ref.cur_constants_) +: old_signals_(ref.old_signals_), new_signals_(ref.new_signals_), + old_variables_(ref.old_variables_), new_variables_(ref.new_variables_), + old_components_(ref.old_components_), new_components_(ref.new_components_), + use_types_(ref.use_types_), cur_types_(ref.cur_types_), + use_constants_(ref.use_constants_), cur_constants_(ref.cur_constants_), + use_subprograms_(ref.use_subprograms_), cur_subprograms_(ref.cur_subprograms_), + scopes_(ref.scopes_), use_enums_(ref.use_enums_), + initializers_(ref.initializers_), finalizers_(ref.finalizers_), + package_header_(ref.package_header_), name_(ref.name_) { - merge(ref.old_signals_.begin(), ref.old_signals_.end(), - ref.new_signals_.begin(), ref.new_signals_.end(), - insert_iterator >( - old_signals_, old_signals_.end()) - ); - merge(ref.old_variables_.begin(), ref.old_variables_.end(), - ref.new_variables_.begin(), ref.new_variables_.end(), - insert_iterator >( - old_variables_, old_variables_.end()) - ); - merge(ref.old_components_.begin(), ref.old_components_.end(), - ref.new_components_.begin(), ref.new_components_.end(), - insert_iterator >( - old_components_, old_components_.end()) - ); - use_types_ = ref.use_types_; - cur_types_ = ref.cur_types_; - - use_subprograms_ = ref.use_subprograms_; - cur_subprograms_ = ref.cur_subprograms_; - - use_enums_ = ref.use_enums_; - - // This constructor is invoked when the parser is finished with - // an active scope and is making the actual scope. At this point - // we know that "this" is the parent scope for the subprograms, - // so set it now. - for (map::iterator cur = cur_subprograms_.begin() - ; cur != cur_subprograms_.end() ; ++ cur) { - cur->second->set_parent(this); - } } ScopeBase::~ScopeBase() @@ -85,10 +64,24 @@ void ScopeBase::cleanup() * objects from the other scopes untouched. */ delete_all(new_signals_); + delete_all(new_variables_); delete_all(new_components_); delete_all(cur_types_); delete_all(cur_constants_); - delete_all(cur_subprograms_); + for (map::iterator cur = cur_subprograms_.begin() + ; cur != cur_subprograms_.end() ; ++cur) { + delete_all(cur->second); + } +} + +ScopeBase*ScopeBase::find_scope(perm_string name) const +{ + map::const_iterator it = scopes_.find(name); + + if(it != scopes_.end()) + return it->second; + + return NULL; } const VType*ScopeBase::find_type(perm_string by_name) @@ -97,11 +90,10 @@ const VType*ScopeBase::find_type(perm_string by_name) if (cur == cur_types_.end()) { cur = use_types_.find(by_name); if (cur == use_types_.end()) - return 0; - else - return cur->second; - } else - return cur->second; + return NULL; // nothing found + } + + return cur->second; } bool ScopeBase::find_constant(perm_string by_name, const VType*&typ, Expression*&exp) const @@ -113,19 +105,12 @@ bool ScopeBase::find_constant(perm_string by_name, const VType*&typ, Expression* if (cur == cur_constants_.end()) { cur = use_constants_.find(by_name); if (cur == use_constants_.end()) - return false; - else { - typ = cur->second->typ; - exp = cur->second->val; - return true; - } - } else { - typ = cur->second->typ; - exp = cur->second->val; - return true; + return false; // nothing found } - return false; + typ = cur->second->typ; + exp = cur->second->val; + return true; } Signal* ScopeBase::find_signal(perm_string by_name) const @@ -134,12 +119,10 @@ Signal* ScopeBase::find_signal(perm_string by_name) const if (cur == new_signals_.end()) { cur = old_signals_.find(by_name); if (cur == old_signals_.end()) - return 0; - else - return cur->second; - } else { - return cur->second; + return NULL; // nothing found } + + return cur->second; } Variable* ScopeBase::find_variable(perm_string by_name) const @@ -148,55 +131,68 @@ Variable* ScopeBase::find_variable(perm_string by_name) const if (cur == new_variables_.end()) { cur = old_variables_.find(by_name); if (cur == old_variables_.end()) - return 0; - else - return cur->second; - } else { - return cur->second; + return 0; // nothing found } + + return cur->second; } -const InterfacePort* ScopeBase::find_param(perm_string by_name) const +const InterfacePort* ScopeBase::find_param(perm_string) const { - for(map::const_iterator it = use_subprograms_.begin(); - it != use_subprograms_.end(); ++it) { - if(const InterfacePort*port = it->second->find_param(by_name)) - return port; + return NULL; +} + +const InterfacePort* ScopeBase::find_param_all(perm_string by_name) const +{ + for(map::const_iterator cur = use_subprograms_.begin(); + cur != use_subprograms_.end(); ++cur) { + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + if(const InterfacePort*port = (*it)->find_param(by_name)) + return port; + } } - for(map::const_iterator it = cur_subprograms_.begin(); - it != cur_subprograms_.end(); ++it) { - if(const InterfacePort*port = it->second->find_param(by_name)) - return port; + for(map::const_iterator cur = cur_subprograms_.begin(); + cur != cur_subprograms_.end(); ++cur) { + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + if(const InterfacePort*port = (*it)->find_param(by_name)) + return port; + } } return NULL; } -Subprogram* ScopeBase::find_subprogram(perm_string name) const +SubHeaderList ScopeBase::find_subprogram(perm_string name) const { - map::const_iterator cur; + map::const_iterator cur; cur = cur_subprograms_.find(name); if (cur != cur_subprograms_.end()) - return cur->second; + return cur->second; cur = use_subprograms_.find(name); if (cur != use_subprograms_.end()) - return cur->second; + return cur->second; - return 0; + return find_std_subprogram(name); } -bool ScopeBase::is_enum_name(perm_string name) const +const VTypeEnum* ScopeBase::is_enum_name(perm_string name) const { for(list::const_iterator it = use_enums_.begin(); it != use_enums_.end(); ++it) { if((*it)->has_name(name)) - return true; + return *it; } - return false; + return find_std_enum_name(name); } /* @@ -218,14 +214,13 @@ void ScopeBase::do_use_from(const ScopeBase*that) old_components_[cur->first] = cur->second; } - for (map::const_iterator cur = that->cur_subprograms_.begin() + for (map::const_iterator cur = that->cur_subprograms_.begin() ; cur != that->cur_subprograms_.end() ; ++ cur) { - if (cur->second == 0) + if (cur->second.empty()) continue; use_subprograms_[cur->first] = cur->second; } - for (map::const_iterator cur = that->cur_types_.begin() ; cur != that->cur_types_.end() ; ++ cur) { if (cur->second == 0) @@ -268,21 +263,89 @@ void ScopeBase::transfer_from(ScopeBase&ref, transfer_type_t what) } } -void ActiveScope::set_package_header(Package*pkg) +SubprogramHeader*ScopeBase::match_subprogram(perm_string name, + const list*params) const { - assert(package_header_ == 0); - package_header_ = pkg; + int req_param_count = params ? params->size() : 0; + + // Find all subprograms with matching name + SubHeaderList l = find_std_subprogram(name); + map::const_iterator cur; + + cur = use_subprograms_.find(name); + if (cur != use_subprograms_.end()) + copy(cur->second.begin(), cur->second.end(), + front_insert_iterator(l)); + + cur = cur_subprograms_.find(name); + if(cur != cur_subprograms_.end()) + copy(cur->second.begin(), cur->second.end(), + front_insert_iterator(l)); + + // Find the matching one + for(SubHeaderList::iterator it = l.begin(); it != l.end(); ++it) { + SubprogramHeader*subp = *it; + + if(req_param_count != subp->param_count()) + continue; + + // Do not check the return type here, it might depend on the arguments + + if(params) { + list::const_iterator p = params->begin(); + bool ok = true; + + for(int i = 0; i < req_param_count; ++i) { + const VType*param_type = subp->peek_param_type(i); + + if(*p && param_type && !param_type->type_match(*p)) { + ok = false; + break; + } + + ++p; + } + + if(!ok) + continue; // check another function + } + + // Yay, we have a match! + return subp; + } + + return NULL; } -Subprogram* ActiveScope::recall_subprogram(perm_string name) const +void ScopeBase::generate_name() { - if (Subprogram*tmp = find_subprogram(name)) - return tmp; + char buf[64]; - if (package_header_) - return package_header_->find_subprogram(name); + // Generate a name for the scope + snprintf(buf, sizeof(buf), "__scope_%d", scope_counter++); + name_ = gen_strings.make(buf); +} - return 0; +SubprogramHeader* ActiveScope::recall_subprogram(const SubprogramHeader*subp) const +{ + list arg_types; + SubprogramHeader*tmp; + + for(int i = 0; i < subp->param_count(); ++i) + arg_types.push_back(subp->peek_param_type(i)); + + if ((tmp = match_subprogram(subp->name(), &arg_types))) { + assert(!tmp->body()); + return tmp; + } + + if (package_header_) { + tmp = package_header_->match_subprogram(subp->name(), &arg_types); + assert(!tmp || !tmp->body()); + return tmp; + } + + return NULL; } bool ActiveScope::is_vector_name(perm_string name) const @@ -303,15 +366,6 @@ bool ActiveScope::is_vector_name(perm_string name) const return false; } -Scope::Scope(const ActiveScope&ref) -: ScopeBase(ref) -{ -} - -Scope::~Scope() -{ -} - ComponentBase* Scope::find_component(perm_string by_name) { map::const_iterator cur = new_components_.find(by_name); @@ -324,3 +378,37 @@ ComponentBase* Scope::find_component(perm_string by_name) } else return cur->second; } + +ActiveScope::ActiveScope(const ActiveScope*par) +: ScopeBase(*par), context_entity_(par->context_entity_) +{ + generate_name(); + + // Move all the objects available in higher level scopes to use*/old* maps. + // This way we can store the new items in now empty cur*/new* maps. + merge(par->old_signals_.begin(), par->old_signals_.end(), + par->new_signals_.begin(), par->new_signals_.end(), + insert_iterator >( + old_signals_, old_signals_.end()) + ); + merge(par->old_variables_.begin(), par->old_variables_.end(), + par->new_variables_.begin(), par->new_variables_.end(), + insert_iterator >( + old_variables_, old_variables_.end()) + ); + merge(par->old_components_.begin(), par->old_components_.end(), + par->new_components_.begin(), par->new_components_.end(), + insert_iterator >( + old_components_, old_components_.end()) + ); + merge(par->use_types_.begin(), par->use_types_.end(), + par->cur_types_.begin(), par->cur_types_.end(), + insert_iterator >( + use_types_, use_types_.end()) + ); + merge(par->use_subprograms_.begin(), par->use_subprograms_.end(), + par->cur_subprograms_.begin(), par->cur_subprograms_.end(), + insert_iterator >( + use_subprograms_, use_subprograms_.end()) + ); +} diff --git a/vhdlpp/scope.h b/vhdlpp/scope.h index 69e859fb9..f92a74403 100644 --- a/vhdlpp/scope.h +++ b/vhdlpp/scope.h @@ -1,7 +1,7 @@ #ifndef IVL_scope_H #define IVL_scope_H /* - * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -32,8 +32,11 @@ class ActiveScope; class Architecture; class ComponentBase; class Package; -class Subprogram; +class SubprogramHeader; class VType; +class SequentialStmt; + +typedef list SubHeaderList; template struct delete_object{ @@ -48,28 +51,61 @@ struct delete_pair_second{ class ScopeBase { public: - ScopeBase() { } + ScopeBase() : package_header_(0) { } explicit ScopeBase(const ActiveScope&ref); virtual ~ScopeBase() =0; + ScopeBase* find_scope(perm_string name) const; const VType* find_type(perm_string by_name); virtual bool find_constant(perm_string by_name, const VType*&typ, Expression*&exp) const; Signal* find_signal(perm_string by_name) const; - Variable* find_variable(perm_string by_name) const; + virtual Variable* find_variable(perm_string by_name) const; virtual const InterfacePort* find_param(perm_string by_name) const; - Subprogram* find_subprogram(perm_string by_name) const; - bool is_enum_name(perm_string name) const; + const InterfacePort* find_param_all(perm_string by_name) const; + SubHeaderList find_subprogram(perm_string by_name) const; + // Checks if a string is one of possible enum values. If so, the enum + // type is returned, otherwise NULL. + const VTypeEnum* is_enum_name(perm_string name) const; + + virtual bool is_subprogram() const { return false; } // Moves signals, variables and components from another scope to // this one. After the transfer new_* maps are cleared in the source scope. enum transfer_type_t { SIGNALS = 1, VARIABLES = 2, COMPONENTS = 4, ALL = 0xffff }; void transfer_from(ScopeBase&ref, transfer_type_t what = ALL); - inline void bind_subprogram(perm_string name, Subprogram*obj) - { map::iterator it; + inline void bind_subprogram(perm_string name, SubprogramHeader*obj) + { map::iterator it; if((it = use_subprograms_.find(name)) != use_subprograms_.end() ) - use_subprograms_.erase(it); - cur_subprograms_[name] = obj; + it->second.remove(obj); + cur_subprograms_[name].push_back(obj); + } + + // Adds a statement to implicit initializers list + // (emitted in a 'initial block). + void add_initializer(SequentialStmt* s) + { + initializers_.push_back(s); + } + + // Adds a statement to implicit finalizers list + // (emitted in a 'final' block). + void add_finalizer(SequentialStmt* s) + { + finalizers_.push_back(s); + } + + void dump_scope(ostream&out) const; + + // Looks for a subprogram with specified name and parameter types. + SubprogramHeader*match_subprogram(perm_string name, + const list*params) const; + + perm_string peek_name() const { return name_; } + + void set_package_header(Package*pkg) { + assert(package_header_ == 0); + package_header_ = pkg; } protected: @@ -114,29 +150,44 @@ class ScopeBase { std::map use_constants_; //imported constants std::map cur_constants_; //current constants - std::map use_subprograms_; //imported - std::map cur_subprograms_; //current + std::map use_subprograms_; //imported + std::map cur_subprograms_; //current + + std::map scopes_; std::list use_enums_; + // List of statements that should be emitted in a 'initial' block + std::list initializers_; + + // List of statements that should be emitted in a 'final' block + std::list finalizers_; + void do_use_from(const ScopeBase*that); + + // If this is a package body, then there is a Package header + // already declared. + Package*package_header_; + + // Generates an unique name for the scope + void generate_name(); + +private: + perm_string name_; }; class Scope : public ScopeBase { public: - explicit Scope(const ActiveScope&ref); - ~Scope(); + explicit Scope(const ActiveScope&ref) : ScopeBase(ref) {} + virtual ~Scope() {} ComponentBase* find_component(perm_string by_name); - public: - void dump_scope(ostream&out) const; - protected: // Helper method for emitting signals in the scope. - int emit_signals(ostream&out, Entity*ent, Architecture*arc); - int emit_variables(ostream&out, Entity*ent, Architecture*arc); + int emit_signals(ostream&out, Entity*ent, ScopeBase*scope); + int emit_variables(ostream&out, Entity*ent, ScopeBase*scope); }; /* @@ -148,13 +199,11 @@ class Scope : public ScopeBase { class ActiveScope : public ScopeBase { public: - ActiveScope() : package_header_(0), context_entity_(0) { } - ActiveScope(ActiveScope*par) : ScopeBase(*par), package_header_(0), context_entity_(0) { } + ActiveScope() : context_entity_(0) { } + explicit ActiveScope(const ActiveScope*par); ~ActiveScope() { } - void set_package_header(Package*); - // Pull items from "that" scope into "this" scope as is // defined by a "use" directive. The parser uses this method // to implement the "use ::*" directive. @@ -166,9 +215,9 @@ class ActiveScope : public ScopeBase { bool is_vector_name(perm_string name) const; // Locate the subprogram by name. The subprogram body uses - // this to locate the sobprogram declaration. Note that the + // this to locate the subprogram declaration. Note that the // subprogram may be in a package header. - Subprogram* recall_subprogram(perm_string name) const; + SubprogramHeader* recall_subprogram(const SubprogramHeader*subp) const; /* All bind_name function check if the given name was present * in previous scopes. If it is found, it is erased (but the pointer @@ -204,6 +253,12 @@ class ActiveScope : public ScopeBase { cur_types_[name] = t; } + void bind_scope(perm_string name, ScopeBase*scope) + { + assert(scopes_.find(name) == scopes_.end()); + scopes_[name] = scope; + } + inline void use_enum(const VTypeEnum* t) { use_enums_.push_back(t); } @@ -217,13 +272,6 @@ class ActiveScope : public ScopeBase { cur_constants_[name] = new const_t(obj, val); } - inline void bind_name(perm_string name, Subprogram*obj) - { map::iterator it; - if((it = use_subprograms_.find(name)) != use_subprograms_.end() ) - use_subprograms_.erase(it); - cur_subprograms_[name] = obj; - } - void bind(Entity*ent) { context_entity_ = ent; } @@ -237,10 +285,6 @@ class ActiveScope : public ScopeBase { std::map incomplete_types; private: - // If this is a package body, then there is a Package header - // already declared. - Package*package_header_; - Entity*context_entity_; }; diff --git a/vhdlpp/sequential.cc b/vhdlpp/sequential.cc index f6fe41d0d..9f83c1372 100644 --- a/vhdlpp/sequential.cc +++ b/vhdlpp/sequential.cc @@ -169,22 +169,38 @@ void CaseSeqStmt::CaseStmtAlternative::visit(SeqStmtVisitor& func) } ProcedureCall::ProcedureCall(perm_string name) -: name_(name), param_list_(0) +: name_(name), param_list_(NULL), def_(NULL) { } ProcedureCall::ProcedureCall(perm_string name, std::list* param_list) -: name_(name), param_list_(param_list) +: name_(name), param_list_(param_list), def_(NULL) { } +ProcedureCall::ProcedureCall(perm_string name, std::list* param_list) +: name_(name), def_(NULL) +{ + param_list_ = new std::list; + for(std::list::const_iterator it = param_list->begin(); + it != param_list->end(); ++it) + { + param_list_->push_back(new named_expr_t(empty_perm_string, *it)); + } +} + ProcedureCall::~ProcedureCall() { + if(!param_list_) + return; + while(param_list_->size() > 0) { named_expr_t* cur = param_list_->front(); param_list_->pop_front(); delete cur; } + + delete param_list_; } ReturnStmt::ReturnStmt(Expression*val) @@ -224,7 +240,7 @@ void LoopStatement::visit(SeqStmtVisitor& func) func(this); } -ForLoopStatement::ForLoopStatement(perm_string scope_name, perm_string it, prange_t* range, list* stmts) +ForLoopStatement::ForLoopStatement(perm_string scope_name, perm_string it, ExpRange* range, list* stmts) : LoopStatement(scope_name, stmts), it_(it), range_(range) { } @@ -245,7 +261,7 @@ VariableSeqAssignment::~VariableSeqAssignment() delete rval_; } -WhileLoopStatement::WhileLoopStatement(perm_string lname, ExpLogical* cond, list* stmts) +WhileLoopStatement::WhileLoopStatement(perm_string lname, Expression* cond, list* stmts) : LoopStatement(lname, stmts), cond_(cond) { } @@ -263,3 +279,32 @@ BasicLoopStatement::BasicLoopStatement(perm_string lname, list* BasicLoopStatement::~BasicLoopStatement() { } + +ReportStmt::ReportStmt(Expression*msg, severity_t sev) +: msg_(msg), severity_(sev) +{ + if(sev == ReportStmt::UNSPECIFIED) + severity_ = ReportStmt::NOTE; +} + +AssertStmt::AssertStmt(Expression*condition, Expression*msg, ReportStmt::severity_t sev) +: ReportStmt(msg, sev), cond_(condition) +{ + if(msg == NULL) + msg_ = new ExpString(default_msg_); + + if(sev == ReportStmt::UNSPECIFIED) + severity_ = ReportStmt::ERROR; +} + +const char*AssertStmt::default_msg_ = "Assertion violation."; + +WaitForStmt::WaitForStmt(Expression*delay) +: delay_(delay) +{ +} + +WaitStmt::WaitStmt(wait_type_t typ, Expression*expr) +: type_(typ), expr_(expr) +{ +} diff --git a/vhdlpp/sequential.h b/vhdlpp/sequential.h index c9f039fea..b0fa8ac07 100644 --- a/vhdlpp/sequential.h +++ b/vhdlpp/sequential.h @@ -22,8 +22,7 @@ # include "LineInfo.h" # include "parse_types.h" -# include -# include +# include class ScopeBase; class Entity; @@ -132,10 +131,11 @@ class IfSequential : public SequentialStmt { class ReturnStmt : public SequentialStmt { public: - ReturnStmt(Expression*val); + explicit ReturnStmt(Expression*val); ~ReturnStmt(); public: + int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; @@ -202,8 +202,9 @@ class CaseSeqStmt : public SequentialStmt { class ProcedureCall : public SequentialStmt { public: - ProcedureCall(perm_string name); + explicit ProcedureCall(perm_string name); ProcedureCall(perm_string name, std::list* param_list); + ProcedureCall(perm_string name, std::list* param_list); ~ProcedureCall(); int elaborate(Entity*ent, ScopeBase*scope); @@ -213,6 +214,7 @@ class ProcedureCall : public SequentialStmt { private: perm_string name_; std::list* param_list_; + SubprogramHeader*def_; }; class VariableSeqAssignment : public SequentialStmt { @@ -234,20 +236,22 @@ class VariableSeqAssignment : public SequentialStmt { class WhileLoopStatement : public LoopStatement { public: WhileLoopStatement(perm_string loop_name, - ExpLogical*, list*); + Expression*, list*); ~WhileLoopStatement(); int elaborate(Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope); + void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; private: - ExpLogical* cond_; + Expression* cond_; }; class ForLoopStatement : public LoopStatement { public: ForLoopStatement(perm_string loop_name, - perm_string index, prange_t*, list*); + perm_string index, ExpRange*, list*); ~ForLoopStatement(); int elaborate(Entity*ent, ScopeBase*scope); @@ -261,7 +265,7 @@ class ForLoopStatement : public LoopStatement { int emit_runtime_(ostream&out, Entity*ent, ScopeBase*scope); perm_string it_; - prange_t* range_; + ExpRange* range_; }; class BasicLoopStatement : public LoopStatement { @@ -270,7 +274,80 @@ class BasicLoopStatement : public LoopStatement { ~BasicLoopStatement(); int elaborate(Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope); + void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; }; +class ReportStmt : public SequentialStmt { + public: + typedef enum { UNSPECIFIED, NOTE, WARNING, ERROR, FAILURE } severity_t; + + ReportStmt(Expression*message, severity_t severity); + virtual ~ReportStmt() {} + + void dump(ostream&out, int indent) const; + int elaborate(Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*entity, ScopeBase*scope); + void write_to_stream(std::ostream&fd); + + inline Expression*message() const { return msg_; } + inline severity_t severity() const { return severity_; } + + protected: + void dump_sev_msg(ostream&out, int indent) const; + + Expression*msg_; + severity_t severity_; +}; + +class AssertStmt : public ReportStmt { + public: + AssertStmt(Expression*condition, Expression*message, + ReportStmt::severity_t severity = ReportStmt::ERROR); + + void dump(ostream&out, int indent) const; + int elaborate(Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*entity, ScopeBase*scope); + void write_to_stream(std::ostream&fd); + + private: + Expression*cond_; + + // Message displayed when there is no report assigned. + static const char*default_msg_; +}; + +class WaitForStmt : public SequentialStmt { + public: + explicit WaitForStmt(Expression*delay); + + void dump(ostream&out, int indent) const; + int elaborate(Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*entity, ScopeBase*scope); + void write_to_stream(std::ostream&fd); + + private: + Expression*delay_; +}; + +class WaitStmt : public SequentialStmt { + public: + typedef enum { ON, UNTIL, FINAL } wait_type_t; + WaitStmt(wait_type_t typ, Expression*expression); + + void dump(ostream&out, int indent) const; + int elaborate(Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*entity, ScopeBase*scope); + void write_to_stream(std::ostream&fd); + + inline wait_type_t type() const { return type_; } + + private: + wait_type_t type_; + Expression*expr_; + // Sensitivity list for 'wait until' statement + std::set sens_list_; +}; + #endif /* IVL_sequential_H */ diff --git a/vhdlpp/sequential_debug.cc b/vhdlpp/sequential_debug.cc index 31f3655d2..ac4096568 100644 --- a/vhdlpp/sequential_debug.cc +++ b/vhdlpp/sequential_debug.cc @@ -165,3 +165,50 @@ void BasicLoopStatement::dump(ostream&out, int indent) const out << setw(indent) << "" << "BasicLoopStatement at file=" << get_fileline() << endl; LoopStatement::dump(out, indent+2); } + +void ReportStmt::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "ReportStmt at file=" << get_fileline() << endl; + dump_sev_msg(out, indent+3); +} + +void ReportStmt::dump_sev_msg(ostream&out, int indent) const +{ + out << setw(indent) << "" << "severity: " << severity_ << endl; + + if(msg_) { + out << setw(indent) << "" << "message: "; + msg_->dump(out, indent); + } +} + +void AssertStmt::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "AssertStmt at file=" << get_fileline() << endl; + out << setw(indent+3) << "" << "condition: "; + dump_sev_msg(out, indent+3); +} + +void WaitForStmt::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "WaitForStmt at file=" << get_fileline() << endl; + out << setw(indent+3) << "" << "delay: "; + delay_->dump(out, indent+3); +} + +void WaitStmt::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "WaitStmt at file=" << get_fileline() << endl; + out << setw(indent+3) << "type = "; + + switch(type_) { + case ON: out << "ON" << endl; break; + case UNTIL: out << "UNTIL" << endl; break; + case FINAL: out << "FINAL" << endl; break; + } + + if(type_ != FINAL) { + out << setw(indent+3) << "" << "expression: "; + expr_->dump(out, indent+3); + } +} diff --git a/vhdlpp/sequential_elaborate.cc b/vhdlpp/sequential_elaborate.cc index 29b4e4914..84a48f55b 100644 --- a/vhdlpp/sequential_elaborate.cc +++ b/vhdlpp/sequential_elaborate.cc @@ -19,6 +19,10 @@ # include "sequential.h" # include "expression.h" +# include "scope.h" +# include "library.h" +# include "subprogram.h" +# include "std_types.h" int SequentialStmt::elaborate(Entity*, ScopeBase*) { @@ -95,7 +99,7 @@ int IfSequential::elaborate(Entity*ent, ScopeBase*scope) { int errors = 0; - errors += cond_->elaborate_expr(ent, scope, 0); + errors += cond_->elaborate_expr(ent, scope, &type_BOOLEAN); for (list::iterator cur = if_.begin() ; cur != if_.end() ; ++cur) { @@ -119,7 +123,7 @@ int IfSequential::Elsif::elaborate(Entity*ent, ScopeBase*scope) { int errors = 0; - errors += cond_->elaborate_expr(ent, scope, 0); + errors += cond_->elaborate_expr(ent, scope, &type_BOOLEAN); for (list::iterator cur = if_.begin() ; cur != if_.end() ; ++cur) { @@ -129,6 +133,22 @@ int IfSequential::Elsif::elaborate(Entity*ent, ScopeBase*scope) return errors; } +int ReturnStmt::elaborate(Entity*ent, ScopeBase*scope) +{ + const VType*ltype = NULL; + + // Try to determine the expression type by + // looking up the function return type. + const SubprogramBody*subp = dynamic_cast(scope); + if(subp) { + if(const SubprogramHeader*header = subp->header()) { + ltype = header->peek_return_type(); + } + } + + return val_->elaborate_expr(ent, scope, ltype); +} + int SignalSeqAssignment::elaborate(Entity*ent, ScopeBase*scope) { int errors = 0; @@ -148,16 +168,51 @@ int SignalSeqAssignment::elaborate(Entity*ent, ScopeBase*scope) // Elaborate the r-value expressions. for (list::iterator cur = waveform_.begin() ; cur != waveform_.end() ; ++cur) { - errors += (*cur)->elaborate_expr(ent, scope, lval_type); } return errors; } -int ProcedureCall::elaborate(Entity*, ScopeBase*) +int ProcedureCall::elaborate(Entity*ent, ScopeBase*scope) { - return 0; + int errors = 0; + + assert(!def_); // do not elaborate twice + + // Create a list of argument types to find a matching subprogram + list arg_types; + if(param_list_) { + for(list::iterator it = param_list_->begin(); + it != param_list_->end(); ++it) { + named_expr_t* e = *it; + arg_types.push_back(e->expr()->probe_type(ent, scope)); + } + } + + def_ = scope->match_subprogram(name_, &arg_types); + + if(!def_) + def_ = library_match_subprogram(name_, &arg_types); + + if(!def_) { + cerr << get_fileline() << ": error: could not find procedure "; + emit_subprogram_sig(cerr, name_, arg_types); + cerr << endl; + return 1; + } + + // Elaborate arguments + size_t idx = 0; + if(param_list_) { + for(list::iterator cur = param_list_->begin() + ; cur != param_list_->end() ; ++cur) { + errors += def_->elaborate_argument((*cur)->expr(), idx, ent, scope); + ++idx; + } + } + + return errors; } int VariableSeqAssignment::elaborate(Entity*ent, ScopeBase*scope) @@ -182,13 +237,58 @@ int VariableSeqAssignment::elaborate(Entity*ent, ScopeBase*scope) return errors; } -int WhileLoopStatement::elaborate(Entity*, ScopeBase*) +int WhileLoopStatement::elaborate(Entity*ent, ScopeBase*scope) { - //TODO:check whether there is any wait statement in the statements (there should be) - return 0; + int errors = 0; + errors += elaborate_substatements(ent, scope); + errors += cond_->elaborate_expr(ent, scope, cond_->probe_type(ent, scope)); + return errors; } -int BasicLoopStatement::elaborate(Entity*, ScopeBase*) +int BasicLoopStatement::elaborate(Entity*ent, ScopeBase*scope) { - return 0; + return elaborate_substatements(ent, scope); +} + +int ReportStmt::elaborate(Entity*ent, ScopeBase*scope) +{ + return msg_->elaborate_expr(ent, scope, &primitive_STRING); +} + +int AssertStmt::elaborate(Entity*ent, ScopeBase*scope) +{ + int errors = 0; + errors += ReportStmt::elaborate(ent, scope); + errors += cond_->elaborate_expr(ent, scope, cond_->probe_type(ent, scope)); + return errors; +} + +int WaitForStmt::elaborate(Entity*ent, ScopeBase*scope) +{ + return delay_->elaborate_expr(ent, scope, &primitive_TIME); +} + +int WaitStmt::elaborate(Entity*ent, ScopeBase*scope) +{ + if(type_ == UNTIL) { + struct fill_sens_list_t : public ExprVisitor { + explicit fill_sens_list_t(set& sig_list) + : sig_list_(sig_list) {}; + + void operator() (Expression*s) { + if(ExpName*name = dynamic_cast(s)) + sig_list_.insert(name); + } + + private: + set& sig_list_; + } fill_sens_list(sens_list_); + + // Fill the sensitivity list + expr_->visit(fill_sens_list); + } else if(type_ == FINAL) { + return 0; // nothing to be elaborated + } + + return expr_->elaborate_expr(ent, scope, 0); } diff --git a/vhdlpp/sequential_emit.cc b/vhdlpp/sequential_emit.cc index 895d1d839..25dc6f60d 100644 --- a/vhdlpp/sequential_emit.cc +++ b/vhdlpp/sequential_emit.cc @@ -23,10 +23,14 @@ # include "sequential.h" # include "expression.h" # include "architec.h" +# include "package.h" # include "compiler.h" +# include "subprogram.h" +# include "std_types.h" # include # include # include +# include # include int SequentialStmt::emit(ostream&out, Entity*, ScopeBase*) @@ -80,7 +84,7 @@ void IfSequential::write_to_stream(std::ostream&fd) { fd << "if "; cond_->write_to_stream(fd); - fd << " then " << endl; + fd << " then" << endl; for (list::iterator cur = if_.begin() ; cur != if_.end() ; ++cur) @@ -205,12 +209,33 @@ void VariableSeqAssignment::write_to_stream(ostream&fd) fd << ";" << endl; } -int ProcedureCall::emit(ostream&out, Entity*, ScopeBase*) +int ProcedureCall::emit(ostream&out, Entity*ent, ScopeBase*scope) { - out << " // " << get_fileline() << ": internal error: " - << "I don't know how to emit this sequential statement! " - << "type=" << typeid(*this).name() << endl; - return 1; + int errors = 0; + vectorargv; + + if(!def_) { + cerr << get_fileline() << ": error: unknown procedure: " << name_ << endl; + return 1; + } + + // Convert the parameter list to vector + if(param_list_) { + argv.reserve(param_list_->size()); + + for(std::list::iterator it = param_list_->begin(); + it != param_list_->end(); ++it) + argv.push_back((*it)->expr()); + } + + def_->emit_full_name(argv, out, ent, scope); + out << " ("; + + if(param_list_) + errors += def_->emit_args(argv, out, ent, scope); + + out << ");" << endl; + return errors; } int LoopStatement::emit_substatements(ostream&out, Entity*ent, ScopeBase*scope) @@ -333,16 +358,38 @@ void CaseSeqStmt::CaseStmtAlternative::write_to_stream(ostream&fd) } } +int WhileLoopStatement::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + int errors = 0; + + out << "while("; + errors += cond_->emit(out, ent, scope); + out << ") begin" << endl; + errors += emit_substatements(out, ent, scope); + out << "end" << endl; + + return errors; +} + +void WhileLoopStatement::write_to_stream(ostream&out) +{ + out << "while("; + cond_->write_to_stream(out); + out << ") loop" << endl; + write_to_stream_substatements(out); + out << "end loop;" << endl; +} + int ForLoopStatement::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; ivl_assert(*this, range_); int64_t start_val; - bool start_rc = range_->msb()->evaluate(ent, scope, start_val); + bool start_rc = range_->left()->evaluate(ent, scope, start_val); int64_t finish_val; - bool finish_rc = range_->lsb()->evaluate(ent, scope, finish_val); + bool finish_rc = range_->right()->evaluate(ent, scope, finish_val); perm_string scope_name = loop_name(); if (scope_name.nil()) { @@ -359,48 +406,31 @@ int ForLoopStatement::emit(ostream&out, Entity*ent, ScopeBase*scope) // determined during the run-time errors += emit_runtime_(out, ent, scope); } else { - bool dir = range_->is_downto(); + ExpRange::range_dir_t dir = range_->direction(); - if (!dir) { - int64_t tmp = start_val; - start_val = finish_val; - finish_val = tmp; - } + if(dir == ExpRange::AUTO) + dir = start_val < finish_val ? ExpRange::TO : ExpRange::DOWNTO; - if (dir && (start_val < finish_val)) { - if(range_->is_auto_dir()) { - dir = false; - } else { - out << "begin /* Degenerate loop at " << get_fileline() - << ": " << start_val - << " downto " << finish_val << " */ end" << endl - << "end" << endl; - return errors; - } - } - - else if (!dir && start_val > finish_val) { - if(range_->is_auto_dir()) { - dir = true; - } else { - out << "begin /* Degenerate loop at " << get_fileline() - << ": " << start_val - << " to " << finish_val << " */ end" << endl - << "end" << endl; - return errors; - } + if ((dir == ExpRange::DOWNTO && start_val < finish_val) || + (dir == ExpRange::TO && start_val > finish_val)) { + out << "begin /* Degenerate loop at " << get_fileline() + << ": " << start_val; + out << (dir == ExpRange::DOWNTO ? " downto " : " to "); + out << finish_val << " */ end" << endl + << "end" << endl; + return errors; } out << "for (\\" << it_ << " = " << start_val << " ; "; - if (dir) + if (dir == ExpRange::DOWNTO) out << "\\" << it_ << " >= " << finish_val; else out << "\\" << it_ << " <= " << finish_val; out << "; \\" << it_ << " = \\" << it_; - if (dir) + if (dir == ExpRange::DOWNTO) out << " - 1)"; else out << " + 1)"; @@ -419,9 +449,7 @@ int ForLoopStatement::emit(ostream&out, Entity*ent, ScopeBase*scope) void ForLoopStatement::write_to_stream(ostream&fd) { fd << "for " << it_ << " in "; - range_->expr_left()->write_to_stream(fd); - fd << " to "; - range_->expr_right()->write_to_stream(fd); + range_->write_to_stream(fd); fd << " loop" << endl; write_to_stream_substatements(fd); fd << "end loop;" << endl; @@ -432,22 +460,217 @@ int ForLoopStatement::emit_runtime_(ostream&out, Entity*ent, ScopeBase*scope) int errors = 0; out << "for (\\" << it_ << " = "; - errors += range_->expr_left()->emit(out, ent, scope); + errors += range_->left()->emit(out, ent, scope); // Twisted way of determining the loop direction at runtime out << " ;\n("; - errors += range_->expr_left()->emit(out, ent, scope); + errors += range_->left()->emit(out, ent, scope); out << " < "; - errors += range_->expr_right()->emit(out, ent, scope); + errors += range_->right()->emit(out, ent, scope); out << " ? \\" << it_ << " <= "; - errors += range_->expr_right()->emit(out, ent, scope); + errors += range_->right()->emit(out, ent, scope); out << " : \\" << it_ << " >= "; - errors += range_->expr_right()->emit(out, ent, scope); + errors += range_->right()->emit(out, ent, scope); out << ");\n\\" << it_ << " = \\" << it_ << " + ("; - errors += range_->expr_left()->emit(out, ent, scope); + errors += range_->left()->emit(out, ent, scope); out << " < "; - errors += range_->expr_right()->emit(out, ent, scope); + errors += range_->right()->emit(out, ent, scope); out << " ? 1 : -1))"; return errors; } + +int BasicLoopStatement::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + int errors = 0; + + out << "forever begin" << endl; + errors += emit_substatements(out, ent, scope); + out << "end" << endl; + + return errors; +} + +void BasicLoopStatement::write_to_stream(std::ostream&fd) +{ + fd << "loop" << endl; + write_to_stream_substatements(fd); + fd << "end loop;" << endl; +} + +int ReportStmt::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + out << "$display(\"** "; + + switch(severity_) + { + case NOTE: out << "Note"; break; + case WARNING: out << "Warning"; break; + case ERROR: out << "Error"; break; + case FAILURE: out << "Failure"; break; + case UNSPECIFIED: ivl_assert(*this, false); break; + } + + out << ": \","; + + struct emitter : public ExprVisitor { + emitter(ostream&outp, Entity*enti, ScopeBase*scop) + : out_(outp), ent_(enti), scope_(scop), + level_lock_(numeric_limits::max()) {} + + void operator() (Expression*s) { + if(!dynamic_cast(s)) { + if(level() > level_lock_) + return; + + if(dynamic_cast(s)) { + level_lock_ = level(); + } else { + level_lock_ = numeric_limits::max(); + } + + const VType*type = s->probe_type(ent_, scope_); + + if(dynamic_cast(s) && type + && type->type_match(&primitive_STRING)) { + out_ << "$sformatf(\"%s\", ("; + s->emit(out_, ent_, scope_); + out_ << "))"; + } else { + s->emit(out_, ent_, scope_); + } + + out_ << ", "; + } + } + + private: + ostream&out_; + Entity*ent_; + ScopeBase*scope_; + int level_lock_; + } emit_visitor(out, ent, scope); + + msg_->visit(emit_visitor); + + out << "\" (" << get_fileline() << ")\");"; + + if(severity_ == FAILURE) + out << "$finish();"; + + out << std::endl; + + return 0; +} + +void ReportStmt::write_to_stream(std::ostream&fd) +{ + fd << "report "; + msg_->write_to_stream(fd); + + fd << "severity "; + switch(severity_) + { + case NOTE: fd << "NOTE"; break; + case WARNING: fd << "WARNING"; break; + case ERROR: fd << "ERROR"; break; + case FAILURE: fd << "FAILURE"; break; + case UNSPECIFIED: break; + } + fd << ";" << std::endl; +} + +int AssertStmt::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + int errors = 0; + + out << "if(!("; + errors += cond_->emit(out, ent, scope); + out << ")) begin" << std::endl; + errors += ReportStmt::emit(out, ent, scope); + out << "end" << std::endl; + + return errors; +} + +void AssertStmt::write_to_stream(std::ostream&fd) +{ + fd << "assert "; + cond_->write_to_stream(fd); + fd << std::endl; + ReportStmt::write_to_stream(fd); +} + +int WaitForStmt::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + int errors = 0; + + out << "#("; + errors += delay_->emit(out, ent, scope); + out << ");" << std::endl; + + return errors; +} + +void WaitForStmt::write_to_stream(std::ostream&fd) +{ + fd << "wait for "; + delay_->write_to_stream(fd); +} + +int WaitStmt::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + int errors = 0; + + switch(type_) { + case ON: + out << "@("; + break; + + case UNTIL: + if(!sens_list_.empty()) { + out << "@("; + + for(std::set::iterator it = sens_list_.begin(); + it != sens_list_.end(); ++it) { + if(it != sens_list_.begin()) + out << ","; + + (*it)->emit(out, ent, scope); + } + + out << "); "; + } + + out << "wait("; + break; + + case FINAL: + out << "/* final wait */" << endl; + return 0; // no expression to be emitted + } + + errors += expr_->emit(out, ent, scope); + out << ");" << endl; + + return errors; +} + +void WaitStmt::write_to_stream(std::ostream&fd) +{ + switch(type_) { + case ON: + fd << "wait on "; + break; + + case UNTIL: + fd << "wait until "; + break; + + case FINAL: + fd << "wait"; + return; // no expression to be emitted + } + + expr_->write_to_stream(fd); +} diff --git a/vhdlpp/std_funcs.cc b/vhdlpp/std_funcs.cc new file mode 100644 index 000000000..2880bba98 --- /dev/null +++ b/vhdlpp/std_funcs.cc @@ -0,0 +1,481 @@ +/* + * Copyright CERN 2016-2018 + * @author Maciej Suminski (maciej.suminski@cern.ch) + * + * 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 "std_funcs.h" +#include "std_types.h" +#include "scope.h" + +static std::map std_subprograms; + +void register_std_subprogram(SubprogramHeader*header) +{ + std_subprograms[header->name()].push_back(header); +} + +// Special case: to_integer function +class SubprogramToInteger : public SubprogramStdHeader { + public: + SubprogramToInteger() + : SubprogramStdHeader(perm_string::literal("to_integer"), NULL, &primitive_REAL) { + ports_ = new list(); + ports_->push_back(new InterfacePort(&primitive_INTEGER)); + } + + int emit_name(const std::vector&argv, + std::ostream&out, Entity*ent, ScopeBase*scope) const { + bool signed_flag = false; + + // to_integer converts unsigned to natural + // signed to integer + // try to determine the converted type + const VType*type = argv[0]->probe_type(ent, scope); + const VTypeArray*array = dynamic_cast(type); + + if(array) { + signed_flag = array->signed_vector(); + } else { + cerr << get_fileline() << ": sorry: Could not determine the " + << "expression sign. Output may be erroneous." << endl; + return 1; + } + + out << (signed_flag ? "$signed" : "$unsigned"); + return 0; + } +}; + +// Special case: size casting (e.g. conv_std_logic_vector() / resize()). +class SubprogramSizeCast : public SubprogramStdHeader { + public: + explicit SubprogramSizeCast(perm_string nam, const VType*base, const VType*target) + : SubprogramStdHeader(nam, NULL, target) { + ports_ = new list(); + ports_->push_back(new InterfacePort(base)); + ports_->push_back(new InterfacePort(&primitive_NATURAL)); + } + + int emit_name(const std::vector&, + std::ostream&, Entity*, ScopeBase*) const { + return 0; + } + + int emit_args(const std::vector&argv, + std::ostream&out, Entity*ent, ScopeBase*scope) const { + int64_t new_size, old_size; + + const VType*type = argv[0]->probe_type(ent, scope); + + if(!type) { + cerr << get_fileline() << ": sorry: Could not determine " + << "the argument type. Size casting impossible." << endl; + return 1; + } + + old_size = type->get_width(scope); + + if(old_size <= 0) { + cerr << get_fileline() << ": sorry: Could not determine " + << "the argument size. Size casting impossible." << endl; + return 1; + } + + if(!argv[1]->evaluate(ent, scope, new_size)) { + cerr << get_fileline() << ": sorry: Could not evaluate the requested" + << "expression size. Size casting impossible." << endl; + return 1; + } + + + out << new_size << "'(" << old_size << "'("; + + if(const VTypeArray*arr = dynamic_cast(type)) + out << (arr->signed_vector() ? "$signed" : "$unsigned"); + + out << "("; + bool res = argv[0]->emit(out, ent, scope); + out << ")))"; + + return res; + } +}; + +class SubprogramReadWrite : public SubprogramBuiltin { + public: + SubprogramReadWrite(perm_string nam, perm_string newnam, bool hex = false) + : SubprogramBuiltin(nam, newnam, NULL, NULL), hex_format_(hex) { + ports_ = new list(); + ports_->push_back(new InterfacePort(&primitive_STRING)); + ports_->push_back(new InterfacePort(NULL)); + } + + // Format types handled by $ivlh_read/write (see vpi/vhdl_textio.c) + enum format_t { FORMAT_STD, FORMAT_BOOL, FORMAT_TIME, FORMAT_HEX, FORMAT_STRING }; + + int emit_args(const std::vector&argv, + std::ostream&out, Entity*ent, ScopeBase*scope) const { + + int errors = 0; + + for(int i = 0; i < 2; ++i) { + errors += argv[i]->emit(out, ent, scope); + out << ", "; + } + + const VType*arg_type = argv[1]->probe_type(ent, scope); + + while(const VTypeDef*tdef = dynamic_cast(arg_type)) + arg_type = tdef->peek_definition(); + + // Pick the right format + if(hex_format_) { + out << FORMAT_HEX; + } else if(arg_type) { + if(arg_type->type_match(&primitive_TIME)) { + out << FORMAT_TIME; + } else if(arg_type->type_match(&type_BOOLEAN)) { + out << FORMAT_BOOL; + } else if(arg_type->type_match(&primitive_CHARACTER)) { + out << FORMAT_STRING; + } else { + const VTypeArray*arr = dynamic_cast(arg_type); + + if(arr && arr->element_type() == &primitive_CHARACTER) + out << FORMAT_STRING; + else + out << FORMAT_STD; + } + } else { + out << FORMAT_STD; + } + + return errors; + } + + private: + bool hex_format_; +}; + +void preload_std_funcs(void) +{ + list*args; + + /* function now */ + SubprogramBuiltin*fn_now = new SubprogramBuiltin(perm_string::literal("now"), + perm_string::literal("$time"), NULL, NULL); + register_std_subprogram(fn_now); + + /* numeric_std library + * function unsigned + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_INTEGER)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("unsigned"), + perm_string::literal("$unsigned"), + args, &primitive_UNSIGNED)); + + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("unsigned"), + perm_string::literal("$unsigned"), + args, &primitive_UNSIGNED)); + + /* function integer + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_REAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("integer"), + perm_string::literal("int'"), + args, &primitive_INTEGER)); + + /* function std_logic_vector + Special case: The std_logic_vector function casts its + argument to std_logic_vector. Internally, we don't + have to do anything for that to work. + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_SIGNED)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("std_logic_vector"), + empty_perm_string, + args, &primitive_STDLOGIC_VECTOR)); + + args = new list(); + args->push_back(new InterfacePort(&primitive_UNSIGNED)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("std_logic_vector"), + empty_perm_string, + args, &primitive_STDLOGIC_VECTOR)); + + /* numeric_std library + * function shift_left (arg: unsigned; count: natural) return unsigned; + * function shift_left (arg: signed; count: natural) return signed; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_UNSIGNED)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("shift_left"), + perm_string::literal("$ivlh_shift_left"), + args, &primitive_UNSIGNED)); + + args = new list(); + args->push_back(new InterfacePort(&primitive_SIGNED)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("shift_left"), + perm_string::literal("$ivlh_shift_left"), + args, &primitive_SIGNED)); + + /* numeric_std library + * function shift_right (arg: unsigned; count: natural) return unsigned; + * function shift_right (arg: signed; count: natural) return signed; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_UNSIGNED)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("shift_right"), + perm_string::literal("$ivlh_shift_right"), + args, &primitive_UNSIGNED)); + + args = new list(); + args->push_back(new InterfacePort(&primitive_SIGNED)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("shift_right"), + perm_string::literal("$ivlh_shift_right"), + args, &primitive_SIGNED)); + + /* function resize + */ + register_std_subprogram(new SubprogramSizeCast(perm_string::literal("resize"), + &primitive_UNSIGNED, &primitive_UNSIGNED)); + + register_std_subprogram(new SubprogramSizeCast(perm_string::literal("resize"), + &primitive_SIGNED, &primitive_SIGNED)); + + /* std_logic_arith library + * function conv_std_logic_vector(arg: integer; size: integer) return std_logic_vector; + */ + register_std_subprogram(new SubprogramSizeCast( + perm_string::literal("conv_std_logic_vector"), + &primitive_INTEGER, &primitive_STDLOGIC_VECTOR)); + + /* numeric_bit library + * function to_integer (arg: unsigned) return natural; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_UNSIGNED)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_integer"), + perm_string::literal("$unsigned"), + args, &primitive_NATURAL)); + + /* numeric_bit library + * function to_integer (arg: signed) return integer; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_SIGNED)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_integer"), + perm_string::literal("$signed"), + args, &primitive_INTEGER)); + + /* std_logic_1164 library + * function to_bit (signal s : std_ulogic) return bit; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_bit"), + empty_perm_string, + args, &primitive_BIT)); + + /* std_logic_1164 library + * function to_bitvector (signal s : std_logic_vector) return bit_vector; + * function to_bitvector (signal s : std_ulogic_vector) return bit_vector; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_bitvector"), + empty_perm_string, + args, &primitive_BIT_VECTOR)); + + /* std_logic_1164 library + * function rising_edge (signal s : std_ulogic) return boolean; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("rising_edge"), + perm_string::literal("$ivlh_rising_edge"), + args, &type_BOOLEAN)); + + /* std_logic_1164 library + * function falling_edge (signal s : std_ulogic) return boolean; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("falling_edge"), + perm_string::literal("$ivlh_falling_edge"), + args, &type_BOOLEAN)); + + /* reduce_pack library + * function or_reduce(arg : std_logic_vector) return std_logic; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("or_reduce"), + perm_string::literal("|"), + args, &primitive_STDLOGIC)); + + /* reduce_pack library + * function and_reduce(arg : std_logic_vector) return std_logic; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("and_reduce"), + perm_string::literal("&"), + args, &primitive_STDLOGIC)); + + /* fixed_pkg library + * function to_unsigned ( + * arg : ufixed; -- fixed point input + * constant size : natural) -- length of output + * return unsigned; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_REAL)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_unsigned"), + perm_string::literal("$ivlh_to_unsigned"), + args, &primitive_UNSIGNED)); + /* numeric_std library + * function to_unsigned(arg, size : natural) return unsigned; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_NATURAL)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_unsigned"), + perm_string::literal("$ivlh_to_unsigned"), + args, &primitive_UNSIGNED)); + + /* numeric_std library + * function to_unsigned(arg : std_logic_vector, size : natural) return unsigned; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_unsigned"), + perm_string::literal("$ivlh_to_unsigned"), + args, &primitive_UNSIGNED)); + + /* procedure file_open (file f: text; filename: in string, file_open_kind: in mode); + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_INTEGER, PORT_IN)); + args->push_back(new InterfacePort(&primitive_STRING, PORT_IN)); + args->push_back(new InterfacePort(&type_FILE_OPEN_KIND, PORT_IN)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("file_open"), + perm_string::literal("$ivlh_file_open"), + args, NULL)); + + /* procedure file_open (status: out file_open_status, file f: text; filename: in string, file_open_kind: in mode); + */ + args = new list(); + args->push_back(new InterfacePort(&type_FILE_OPEN_STATUS, PORT_OUT)); + args->push_back(new InterfacePort(&primitive_INTEGER, PORT_IN)); + args->push_back(new InterfacePort(&primitive_STRING, PORT_IN)); + args->push_back(new InterfacePort(&type_FILE_OPEN_KIND, PORT_IN)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("file_open"), + perm_string::literal("$ivlh_file_open"), + args, NULL)); + + /* std.textio library + * procedure file_close (file f: text); + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_INTEGER, PORT_IN)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("file_close"), + perm_string::literal("$fclose"), + args, NULL)); + + /* std.textio library + * procedure read (l: inout line; value: out bit/bit_vector/boolean/character/integer/real/string/time); + */ + register_std_subprogram(new SubprogramReadWrite(perm_string::literal("read"), + perm_string::literal("$ivlh_read"))); + + /* std.textio library + * procedure write (l: inout line; value: out bit/bit_vector/boolean/character/integer/real/string/time); + */ + register_std_subprogram(new SubprogramReadWrite(perm_string::literal("write"), + perm_string::literal("$ivlh_write"))); + + /* std.textio library + * procedure hread (l: inout line; value: out bit/bit_vector/boolean/character/integer/real/string/time); + */ + register_std_subprogram(new SubprogramReadWrite(perm_string::literal("hread"), + perm_string::literal("$ivlh_read"), true)); + + /* std.textio library + * procedure hwrite (l: inout line; value: out bit/bit_vector/boolean/character/integer/real/string/time); + */ + register_std_subprogram(new SubprogramReadWrite(perm_string::literal("hwrite"), + perm_string::literal("$ivlh_write"), true)); + + /* std.textio library + * procedure readline (file f: text; l: inout line); + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_INTEGER, PORT_IN)); + args->push_back(new InterfacePort(&primitive_STRING, PORT_OUT)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("readline"), + perm_string::literal("$ivlh_readline"), + args, NULL)); + + /* std.textio library + * procedure writeline (file f: text; l: inout line); + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_INTEGER, PORT_IN)); + args->push_back(new InterfacePort(&primitive_STRING, PORT_IN)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("writeline"), + perm_string::literal("$ivlh_writeline"), + args, NULL)); + + /* function endline (file f: text) return boolean; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_INTEGER, PORT_IN)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("endfile"), + perm_string::literal("$feof"), + args, &type_BOOLEAN)); +} + +void delete_std_funcs() +{ + for(std::map::iterator cur = std_subprograms.begin(); + cur != std_subprograms.end(); ++cur) { + for(SubHeaderList::const_iterator it = cur->second.begin(); + it != cur->second.end(); ++it) { + delete *it; + } + } +} + +SubHeaderList find_std_subprogram(perm_string name) +{ + map::const_iterator cur = std_subprograms.find(name); + if(cur != std_subprograms.end()) + return cur->second; + + return SubHeaderList(); +} diff --git a/vhdlpp/std_funcs.h b/vhdlpp/std_funcs.h new file mode 100644 index 000000000..8569b2424 --- /dev/null +++ b/vhdlpp/std_funcs.h @@ -0,0 +1,37 @@ +#ifndef IVL_std_funcs_H +#define IVL_std_funcs_H +/* + * Copyright CERN 2016 + * @author Maciej Suminski (maciej.suminski@cern.ch) + * + * 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 "subprogram.h" + +// Generates subprogram headers for standard VHDL library functions. +void preload_std_funcs(); + +// Destroys subprogram headers for standard VHDL library functions. +void delete_std_funcs(); + +// Adds a subprogram to the standard library subprogram set +void register_std_subprogram(SubprogramHeader*header); + +// Returns subprogram header for a requested function or NULL if it does not exist. +SubHeaderList find_std_subprogram(perm_string name); + +#endif /* IVL_std_funcs_H */ diff --git a/vhdlpp/std_types.cc b/vhdlpp/std_types.cc new file mode 100644 index 000000000..51f6eaa2d --- /dev/null +++ b/vhdlpp/std_types.cc @@ -0,0 +1,144 @@ +/* + * Copyright CERN 2015 + * @author Maciej Suminski (maciej.suminski@cern.ch) + * + * 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 "std_types.h" +#include "scope.h" + +static map std_types; +// this list contains enums used by typedefs in the std_types map +static list std_enums; + +const VTypePrimitive primitive_BIT(VTypePrimitive::BIT, true); +const VTypePrimitive primitive_INTEGER(VTypePrimitive::INTEGER); +const VTypePrimitive primitive_NATURAL(VTypePrimitive::NATURAL); +const VTypePrimitive primitive_REAL(VTypePrimitive::REAL); +const VTypePrimitive primitive_STDLOGIC(VTypePrimitive::STDLOGIC, true); +const VTypePrimitive primitive_TIME(VTypePrimitive::TIME, true); + +VTypeDef type_BOOLEAN(perm_string::literal("boolean")); +VTypeDef type_FILE_OPEN_KIND(perm_string::literal("file_open_kind")); +VTypeDef type_FILE_OPEN_STATUS(perm_string::literal("file_open_status")); + +const VTypeArray primitive_CHARACTER(&primitive_BIT, 7, 0); +const VTypeArray primitive_BIT_VECTOR(&primitive_BIT, vector (1)); +const VTypeArray primitive_BOOL_VECTOR(&type_BOOLEAN, vector (1)); +const VTypeArray primitive_STDLOGIC_VECTOR(&primitive_STDLOGIC, vector (1)); +const VTypeArray primitive_STRING(&primitive_CHARACTER, vector (1)); +const VTypeArray primitive_SIGNED(&primitive_STDLOGIC, vector (1), true); +const VTypeArray primitive_UNSIGNED(&primitive_STDLOGIC, vector (1), false); + +void generate_global_types(ActiveScope*res) +{ + // boolean + list enum_BOOLEAN_vals; + enum_BOOLEAN_vals.push_back(perm_string::literal("false")); + enum_BOOLEAN_vals.push_back(perm_string::literal("true")); + VTypeEnum*enum_BOOLEAN = new VTypeEnum(&enum_BOOLEAN_vals); + type_BOOLEAN.set_definition(enum_BOOLEAN); + std_types[type_BOOLEAN.peek_name()] = &type_BOOLEAN; + std_enums.push_back(enum_BOOLEAN); + + // file_open_kind + list enum_FILE_OPEN_KIND_vals; + enum_FILE_OPEN_KIND_vals.push_back(perm_string::literal("read_mode")); + enum_FILE_OPEN_KIND_vals.push_back(perm_string::literal("write_mode")); + enum_FILE_OPEN_KIND_vals.push_back(perm_string::literal("append_mode")); + VTypeEnum*enum_FILE_OPEN_KIND = new VTypeEnum(&enum_FILE_OPEN_KIND_vals); + type_FILE_OPEN_KIND.set_definition(enum_FILE_OPEN_KIND); + std_types[type_FILE_OPEN_KIND.peek_name()] = &type_FILE_OPEN_KIND; + std_enums.push_back(enum_FILE_OPEN_KIND); + + // file_open_status + list enum_FILE_OPEN_STATUS_vals; + enum_FILE_OPEN_STATUS_vals.push_back(perm_string::literal("open_ok")); + enum_FILE_OPEN_STATUS_vals.push_back(perm_string::literal("status_error")); + enum_FILE_OPEN_STATUS_vals.push_back(perm_string::literal("name_error")); + enum_FILE_OPEN_STATUS_vals.push_back(perm_string::literal("mode_error")); + VTypeEnum*enum_FILE_OPEN_STATUS = new VTypeEnum(&enum_FILE_OPEN_STATUS_vals); + type_FILE_OPEN_STATUS.set_definition(enum_FILE_OPEN_STATUS); + std_types[type_FILE_OPEN_STATUS.peek_name()] = &type_FILE_OPEN_STATUS; + std_enums.push_back(enum_FILE_OPEN_STATUS); + + res->use_name(type_BOOLEAN.peek_name(), &type_BOOLEAN); + res->use_name(perm_string::literal("bit"), &primitive_BIT); + res->use_name(perm_string::literal("bit_vector"), &primitive_BIT_VECTOR); + res->use_name(perm_string::literal("integer"), &primitive_INTEGER); + res->use_name(perm_string::literal("real"), &primitive_REAL); + res->use_name(perm_string::literal("std_logic"), &primitive_STDLOGIC); + res->use_name(perm_string::literal("character"), &primitive_CHARACTER); + res->use_name(perm_string::literal("string"), &primitive_STRING); + res->use_name(perm_string::literal("natural"), &primitive_NATURAL); + res->use_name(perm_string::literal("time"), &primitive_TIME); +} + +void delete_global_types() +{ + typedef_context_t typedef_ctx; + for(map::iterator cur = std_types.begin(); + cur != std_types.end(); ++cur) { + delete cur->second->peek_definition(); + delete cur->second; + } + + // std_enums are destroyed above +} + +const VTypeEnum*find_std_enum_name(perm_string name) +{ + for(list::const_iterator it = std_enums.begin(); + it != std_enums.end(); ++it) { + if((*it)->has_name(name)) + return *it; + } + + return NULL; +} + +void emit_std_types(ostream&fd) +{ + fd << "`ifndef __VHDL_STD_TYPES" << endl; + fd << "`define __VHDL_STD_TYPES" << endl; + typedef_context_t typedef_ctx; + for(map::iterator cur = std_types.begin(); + cur != std_types.end() ; ++ cur) { + cur->second->emit_typedef(fd, typedef_ctx); + } + fd << "`endif" << endl; +} + +bool is_global_type(perm_string name) +{ + if (name == "boolean") return true; + if (name == "bit") return true; + if (name == "bit_vector") return true; + if (name == "integer") return true; + if (name == "real") return true; + if (name == "std_logic") return true; + if (name == "std_logic_vector") return true; + if (name == "character") return true; + if (name == "string") return true; + if (name == "natural") return true; + if (name == "signed") return true; + if (name == "unsigned") return true; + if (name == "time") return true; + + return std_types.count(name) > 0; +} + diff --git a/vhdlpp/std_types.h b/vhdlpp/std_types.h new file mode 100644 index 000000000..8a91890a3 --- /dev/null +++ b/vhdlpp/std_types.h @@ -0,0 +1,50 @@ +/* + * Copyright CERN 2015 + * @author Maciej Suminski (maciej.suminski@cern.ch) + * + * 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 "vtype.h" + +class ActiveScope; + +void emit_std_types(ostream&out); +void generate_global_types(ActiveScope*res); +bool is_global_type(perm_string type_name); +void delete_global_types(); +const VTypeEnum*find_std_enum_name(perm_string name); + +extern const VTypePrimitive primitive_BIT; +extern const VTypePrimitive primitive_INTEGER; +extern const VTypePrimitive primitive_NATURAL; +extern const VTypePrimitive primitive_REAL; +extern const VTypePrimitive primitive_STDLOGIC; +extern const VTypePrimitive primitive_TIME; +extern const VTypePrimitive primitive_TEXT; +extern const VTypePrimitive primitive_LINE; + +extern VTypeDef type_BOOLEAN; +extern VTypeDef type_FILE_OPEN_KIND; +extern VTypeDef type_FILE_OPEN_STATUS; + +extern const VTypeArray primitive_CHARACTER; +extern const VTypeArray primitive_BIT_VECTOR; +extern const VTypeArray primitive_BOOL_VECTOR; +extern const VTypeArray primitive_STDLOGIC_VECTOR; +extern const VTypeArray primitive_STRING; +extern const VTypeArray primitive_SIGNED; +extern const VTypeArray primitive_UNSIGNED; diff --git a/vhdlpp/subprogram.cc b/vhdlpp/subprogram.cc index 8d2f4f814..d06917a31 100644 --- a/vhdlpp/subprogram.cc +++ b/vhdlpp/subprogram.cc @@ -30,44 +30,83 @@ using namespace std; -Subprogram::Subprogram(perm_string nam, list*ports, - const VType*return_type) -: name_(nam), parent_(0), ports_(ports), return_type_(return_type), statements_(0) +SubprogramBody::SubprogramBody() + : statements_(NULL), header_(NULL) { } -Subprogram::~Subprogram() +SubprogramBody::~SubprogramBody() { } -void Subprogram::set_parent(const ScopeBase*par) +const InterfacePort*SubprogramBody::find_param(perm_string nam) const { - ivl_assert(*this, parent_ == 0); - parent_ = par; + if(!header_) + return NULL; + + return header_->find_param(nam); } -void Subprogram::set_program_body(list*stmt) +void SubprogramBody::set_statements(list*stmt) { ivl_assert(*this, statements_==0); statements_ = stmt; } -bool Subprogram::unbounded() const { - if(return_type_->is_unbounded()) - return true; +int SubprogramBody::elaborate() +{ + int errors = 0; - if(ports_) { - for(std::list::const_iterator it = ports_->begin(); - it != ports_->end(); ++it) { - if((*it)->type->is_unbounded()) - return true; - } + for (list::const_iterator cur = statements_->begin() + ; cur != statements_->end() ; ++cur) { + errors += (*cur)->elaborate(0, this); } - return false; + return errors; } -bool Subprogram::compare_specification(Subprogram*that) const +void SubprogramBody::write_to_stream(ostream&fd) const +{ + for (map::const_iterator cur = new_variables_.begin() + ; cur != new_variables_.end() ; ++cur) { + cur->second->write_to_stream(fd); + } + + fd << "begin" << endl; + + if (statements_) { + for (list::const_iterator cur = statements_->begin() + ; cur != statements_->end() ; ++cur) { + (*cur)->write_to_stream(fd); + } + } else { + fd << "--empty body" << endl; + } + + fd << "end function " << header_->name() << ";" << endl; +} + +SubprogramHeader::SubprogramHeader(perm_string nam, list*ports, + const VType*return_type) +: name_(nam), ports_(ports), return_type_(return_type), body_(NULL), package_(NULL) +{ +} + +SubprogramHeader::~SubprogramHeader() +{ + delete body_; + + if(ports_) { + for(list::iterator it = ports_->begin(); + it != ports_->end(); ++it) + { + delete *it; + } + delete ports_; + } +} + +bool SubprogramHeader::compare_specification(SubprogramHeader*that) const { if (name_ != that->name_) return false; @@ -98,7 +137,7 @@ bool Subprogram::compare_specification(Subprogram*that) const return true; } -const InterfacePort*Subprogram::find_param(perm_string nam) const +const InterfacePort*SubprogramHeader::find_param(perm_string nam) const { if(!ports_) return NULL; @@ -112,7 +151,7 @@ const InterfacePort*Subprogram::find_param(perm_string nam) const return NULL; } -const VType*Subprogram::peek_param_type(int idx) const +const InterfacePort*SubprogramHeader::peek_param(int idx) const { if(!ports_ || idx < 0 || (size_t)idx >= ports_->size()) return NULL; @@ -120,10 +159,91 @@ const VType*Subprogram::peek_param_type(int idx) const std::list::const_iterator p = ports_->begin(); std::advance(p, idx); - return (*p)->type; + return *p; } -Subprogram*Subprogram::make_instance(std::vector arguments, ScopeBase*scope) { +const VType*SubprogramHeader::peek_param_type(int idx) const +{ + const InterfacePort*port = peek_param(idx); + + if(port) + return port->type; + + return NULL; +} + +const VType*SubprogramHeader::exact_return_type(const std::vector&argv, Entity*ent, ScopeBase*scope) +{ + const VTypeArray*orig_ret = dynamic_cast(return_type_); + + if(!orig_ret) + return return_type_; + + const VTypeArray*arg = dynamic_cast(argv[0]->fit_type(ent, scope, orig_ret)); + + if(!arg) + return return_type_; + + VTypeArray*ret = new VTypeArray(orig_ret->element_type(), arg->dimensions(), orig_ret->signed_vector()); + ret->set_parent_type(orig_ret); + + return ret; +} + +bool SubprogramHeader::unbounded() const { + if(return_type_ && return_type_->is_unbounded()) + return true; + + if(ports_) { + for(std::list::const_iterator it = ports_->begin(); + it != ports_->end(); ++it) { + if((*it)->type->is_unbounded()) + return true; + } + } + + return false; +} + +void SubprogramHeader::set_body(SubprogramBody*bdy) +{ + ivl_assert(*this, !body_); + body_ = bdy; + ivl_assert(*this, !bdy->header_); + bdy->header_ = this; +} + +int SubprogramHeader::elaborate_argument(Expression*expr, int idx, + Entity*ent, ScopeBase*scope) +{ + const VType*type = expr->probe_type(ent, scope); + const InterfacePort*param = peek_param(idx); + + if(!param) { + cerr << expr->get_fileline() + << ": error: Too many arguments when calling " + << name_ << "." << endl; + return 1; + } + + // Enable reg_flag for variables that might be modified in subprograms + if(param->mode == PORT_OUT || param->mode == PORT_INOUT) { + if(const ExpName*e = dynamic_cast(expr)) { + if(Signal*sig = scope->find_signal(e->peek_name())) + sig->count_ref_sequ(); + else if(Variable*var = scope->find_variable(e->peek_name())) + var->count_ref_sequ(); + } + } + + if(!type) + type = param->type; + + return expr->elaborate_expr(ent, scope, type); +} + +SubprogramHeader*SubprogramHeader::make_instance(std::vector arguments, + ScopeBase*scope) const { assert(arguments.size() == ports_->size()); std::list*ports = new std::list; @@ -142,25 +262,31 @@ Subprogram*Subprogram::make_instance(std::vector arguments, ScopeBa char buf[80]; snprintf(buf, sizeof(buf), "__%s_%p", name_.str(), ports); perm_string new_name = lex_strings.make(buf); - Subprogram*instance = new Subprogram(new_name, ports, return_type_); + SubprogramHeader*instance = new SubprogramHeader(new_name, ports, return_type_); - // Copy variables - for(std::map::iterator it = new_variables_.begin(); - it != new_variables_.end(); ++it) { - Variable*v = new Variable(it->first, it->second->peek_type()->clone()); - instance->new_variables_[it->first] = v; + if(body_) { + SubprogramBody*body_inst = new SubprogramBody(); + + // Copy variables + for(std::map::iterator it = body_->new_variables_.begin(); + it != body_->new_variables_.end(); ++it) { + Variable*v = new Variable(it->first, it->second->peek_type()->clone()); + body_inst->new_variables_[it->first] = v; + } + + body_inst->set_statements(body_->statements_); + instance->set_package(package_); + instance->set_body(body_inst); + instance->fix_return_type(); } - instance->set_parent(scope); - instance->set_program_body(statements_); - instance->fix_return_type(); scope->bind_subprogram(new_name, instance); return instance; } struct check_return_type : public SeqStmtVisitor { - check_return_type(const Subprogram*subp) : subp_(subp), ret_type_(NULL) {} + explicit check_return_type(const SubprogramBody*subp) : subp_(subp), ret_type_(NULL) {} void operator() (SequentialStmt*s) { @@ -195,38 +321,44 @@ struct check_return_type : public SeqStmtVisitor { const VType*get_type() const { return ret_type_; } private: - const Subprogram*subp_; + const SubprogramBody*subp_; const VType*ret_type_; }; -void Subprogram::fix_return_type() +void SubprogramHeader::fix_return_type() { - if(!statements_) + if(!body_ || !body_->statements_) return; - check_return_type r(this); + check_return_type r(body_); - for (std::list::iterator s = statements_->begin() - ; s != statements_->end(); ++s) { + for (std::list::iterator s = body_->statements_->begin() + ; s != body_->statements_->end(); ++s) { (*s)->visit(r); } VType*return_type = const_cast(r.get_type()); if(return_type && !return_type->is_unbounded()) { // Let's check if the variable length can be evaluated without any scope. - // If not, then it is depends on information about e.g. function params + // If not, then it depends on information about e.g. function params if(return_type->is_variable_length(NULL)) { if(VTypeArray*arr = dynamic_cast(return_type)) - arr->evaluate_ranges(this); + arr->evaluate_ranges(body_); } return_type_ = return_type; } } -void Subprogram::write_to_stream(ostream&fd) const +void SubprogramHeader::write_to_stream(ostream&fd) const { - fd << " function " << name_ << "("; + if(return_type_) + fd << "function "; + else + fd << "procedure "; + + fd << name_; if (ports_ && ! ports_->empty()) { + fd << "("; list::const_iterator cur = ports_->begin(); InterfacePort*curp = *cur; fd << curp->name << " : "; @@ -236,44 +368,11 @@ void Subprogram::write_to_stream(ostream&fd) const fd << "; " << curp->name << " : "; curp->type->write_to_stream(fd); } + fd << ")"; + } + + if( return_type_) { + fd << " return "; + return_type_->write_to_stream(fd); } - fd << ") return "; - return_type_->write_to_stream(fd); - fd << ";" << endl; -} - -void Subprogram::write_to_stream_body(ostream&fd) const -{ - fd << "function " << name_ << "("; - if (ports_ && ! ports_->empty()) { - list::const_iterator cur = ports_->begin(); - InterfacePort*curp = *cur; - fd << curp->name << " : "; - curp->type->write_to_stream(fd); - for (++cur ; cur != ports_->end() ; ++cur) { - curp = *cur; - fd << "; " << curp->name << " : "; - curp->type->write_to_stream(fd); - } - } - fd << ") return "; - return_type_->write_to_stream(fd); - fd << " is" << endl; - - for (map::const_iterator cur = new_variables_.begin() - ; cur != new_variables_.end() ; ++cur) { - cur->second->write_to_stream(fd); - } - - fd << "begin" << endl; - - if (statements_) { - for (list::const_iterator cur = statements_->begin() - ; cur != statements_->end() ; ++cur) { - (*cur)->write_to_stream(fd); - } - } else { - fd << "--empty body" << endl; - } - fd << "end function;" << endl; } diff --git a/vhdlpp/subprogram.h b/vhdlpp/subprogram.h index 22c49879e..a03b8da92 100644 --- a/vhdlpp/subprogram.h +++ b/vhdlpp/subprogram.h @@ -27,63 +27,156 @@ # include "scope.h" # include # include +# include class InterfacePort; class SequentialStmt; +class Package; class VType; -class Subprogram : public LineInfo, public ScopeBase { +class SubprogramBody : public LineInfo, public ScopeBase { public: - Subprogram(perm_string name, std::list*ports, + SubprogramBody(); + ~SubprogramBody(); + + const InterfacePort*find_param(perm_string nam) const; + + void set_statements(std::list*statements); + inline bool empty_statements() const { return !statements_ || statements_->empty(); } + + int elaborate(); + int emit(ostream&out, Entity*ent, ScopeBase*scope); + + // Emit body as it would show up in a package. + int emit_package(std::ostream&fd); + + void write_to_stream(std::ostream&fd) const; + void dump(std::ostream&fd) const; + + const SubprogramHeader*header() const { return header_; } + bool is_subprogram() const { return true; } + + private: + std::list*statements_; + SubprogramHeader*header_; + + friend class SubprogramHeader; +}; + +class SubprogramHeader : public LineInfo { + public: + SubprogramHeader(perm_string name, std::list*ports, const VType*return_type); - ~Subprogram(); - - void set_parent(const ScopeBase*par); - inline const ScopeBase*get_parent() const { return parent_; } - - inline const perm_string&name() const { return name_; } - - void set_program_body(std::list*statements); - inline bool empty_program_body() const { return !statements_ || statements_->empty(); } + virtual ~SubprogramHeader(); // Return true if the specification (name, types, ports) // matches this subprogram and that subprogram. - bool compare_specification(Subprogram*that) const; + bool compare_specification(SubprogramHeader*that) const; + int param_count() const { return ports_ ? ports_->size() : 0; } const InterfacePort*find_param(perm_string nam) const; + const InterfacePort*peek_param(int idx) const; const VType*peek_param_type(int idx) const; const VType*peek_return_type() const { return return_type_; } - int emit(ostream&out, Entity*ent, ScopeBase*scope); + // Computes the exact return type (e.g. std_logic_vector(7 downto 0) + // instead of generic std_logic_vector) + virtual const VType*exact_return_type(const std::vector&, Entity*, ScopeBase*); - // Emit a definition as it would show up in a package. - int emit_package(std::ostream&fd) const; + inline void set_package(const Package*pkg) { assert(!package_); package_ = pkg; } + inline const Package*get_package() const { return package_; } - void write_to_stream(std::ostream&fd) const; - void write_to_stream_body(std::ostream&fd) const; - void dump(std::ostream&fd) const; + // Checks if either return type or parameters are unbounded vectors. + bool unbounded() const; + + // Is the subprogram coming from the standard library? + virtual bool is_std() const { return false; } + + inline SubprogramBody*body() const { return body_; } + void set_body(SubprogramBody*bdy); + + inline perm_string name() const { return name_; } + + int elaborate() { return (body_ ? body_->elaborate() : 0); } + + // Elaborates an argument basing on the types stored in the subprogram header. + int elaborate_argument(Expression*expr, int idx, Entity*ent, ScopeBase*scope); + + // Emits the function name, including the package if required. + int emit_full_name(const std::vector&argv, + std::ostream&out, Entity*, ScopeBase*) const; + + // Function name used in the emission step. The main purpose of this + // method is to handle functions offered by standard VHDL libraries. + // Allows to return different function names depending on the arguments + // (think of size casting or signed/unsigned functions). + virtual int emit_name(const std::vector&argv, + std::ostream&out, Entity*, ScopeBase*) const; + + // Emit arguments for a specific call. It allows to reorder or skip + // some of the arguments if function signature is different in + // SystemVerilog compared to VHDL. + virtual int emit_args(const std::vector&argv, + std::ostream&out, Entity*, ScopeBase*) const; // Creates a new instance of the function that takes arguments of // a different type. It is used to allow VHDL functions that work with // unbounded std_logic_vectors, so there can be a separate instance // for limited length logic vector. - Subprogram*make_instance(std::vector arguments, ScopeBase*scope); + SubprogramHeader*make_instance(std::vector arguments, ScopeBase*scope) const; - // Checks if either return type or parameters are unbounded vectors. - bool unbounded() const; + // Emit header as it would show up in a package. + int emit_package(std::ostream&fd) const; - private: + void write_to_stream(std::ostream&fd) const; + void dump(std::ostream&fd) const; + + protected: // Tries to set the return type to a fixed type. VHDL functions that // return std_logic_vectors do not specify its length, as SystemVerilog // demands. void fix_return_type(); + // Procedure/function name perm_string name_; - const ScopeBase*parent_; + std::list*ports_; const VType*return_type_; - std::list*statements_; + SubprogramBody*body_; + const Package*package_; }; +// Class to define functions headers defined in the standard VHDL libraries. +class SubprogramStdHeader : public SubprogramHeader +{ + public: + SubprogramStdHeader(perm_string nam, std::list*ports, + const VType*return_type) : + SubprogramHeader(nam, ports, return_type) {} + virtual ~SubprogramStdHeader() {}; + + bool is_std() const { return true; } +}; + +// The simplest case, when only function name has to be changed. +class SubprogramBuiltin : public SubprogramStdHeader +{ + public: + SubprogramBuiltin(perm_string vhdl_name, perm_string sv_name, + std::list*ports, const VType*return_type) : + SubprogramStdHeader(vhdl_name, ports, return_type), sv_name_(sv_name) {} + ~SubprogramBuiltin() {} + + int emit_name(const std::vector&, std::ostream&out, Entity*, ScopeBase*) const; + + private: + // SystemVerilog counterpart function name + perm_string sv_name_; +}; + +// Helper function to print out a human-readable function signature. +void emit_subprogram_sig(std::ostream&out, perm_string name, + const std::list&arg_types); + #endif /* IVL_subprogram_H */ diff --git a/vhdlpp/subprogram_emit.cc b/vhdlpp/subprogram_emit.cc index 5cb43e3a6..2d9e4e533 100644 --- a/vhdlpp/subprogram_emit.cc +++ b/vhdlpp/subprogram_emit.cc @@ -21,23 +21,59 @@ # include "subprogram.h" # include "sequential.h" # include "vtype.h" +# include "package.h" # include using namespace std; -int Subprogram::emit_package(ostream&fd) const +int SubprogramBody::emit_package(ostream&fd) +{ + int errors = 0; + + for (map::const_iterator cur = new_variables_.begin() + ; cur != new_variables_.end() ; ++cur) { + // Enable reg_flag for variables + cur->second->count_ref_sequ(); + errors += cur->second->emit(fd, NULL, this, false); + } + + // Emulate automatic functions (add explicit initial value assignments) + for (map::const_iterator cur = new_variables_.begin() + ; cur != new_variables_.end() ; ++cur) { + Variable*var = cur->second; + + if(const Expression*init = var->peek_init_expr()) { + fd << cur->first << " = "; + init->emit(fd, NULL, this); + fd << "; // automatic function emulation" << endl; + } + } + + if (statements_) { + for (list::const_iterator cur = statements_->begin() + ; cur != statements_->end() ; ++cur) { + errors += (*cur)->emit(fd, NULL, const_cast(this)); + } + } else { + fd << " begin /* empty body */ end" << endl; + } + + return errors; +} + +int SubprogramHeader::emit_package(ostream&fd) const { int errors = 0; if (return_type_) { - fd << "function "; + fd << "function automatic "; return_type_->emit_def(fd, empty_perm_string); - fd << " " << name_; - fd << "("; } else { - fd << "task " << name_ << ";" << endl; + fd << "task automatic"; } + fd << " \\" << name_ << " ("; + for (list::const_iterator cur = ports_->begin() ; cur != ports_->end() ; ++cur) { if (cur != ports_->begin()) @@ -50,6 +86,9 @@ int Subprogram::emit_package(ostream&fd) const case PORT_OUT: fd << "output "; break; + case PORT_INOUT: + fd << "inout "; + break; case PORT_NONE: fd << "inout /* PORT_NONE? */ "; break; @@ -60,21 +99,8 @@ int Subprogram::emit_package(ostream&fd) const fd << ");" << endl; - for (map::const_iterator cur = new_variables_.begin() - ; cur != new_variables_.end() ; ++cur) { - // Workaround to enable reg_flag for variables - cur->second->count_ref_sequ(); - errors += cur->second->emit(fd, NULL, NULL); - } - - if (statements_) { - for (list::const_iterator cur = statements_->begin() - ; cur != statements_->end() ; ++cur) { - errors += (*cur)->emit(fd, NULL, const_cast(this)); - } - } else { - fd << " begin /* empty body */ end" << endl; - } + if (body_) + body_->emit_package(fd); if (return_type_) fd << "endfunction" << endl; @@ -83,3 +109,67 @@ int Subprogram::emit_package(ostream&fd) const return errors; } + +int SubprogramHeader::emit_full_name(const std::vector&argv, + std::ostream&out, Entity*ent, ScopeBase*scope) const +{ + // If this function has an elaborated definition, and if + // that definition is in a package, then include the + // package name as a scope qualifier. This assures that + // the SV elaborator finds the correct VHDL elaborated + // definition. It should not be emitted only if we call another + // function from the same package. + const SubprogramBody*subp = dynamic_cast(scope); + if (package_ && (!subp || !subp->header() || subp->header()->get_package() != package_)) + out << "\\" << package_->name() << " ::"; + + return emit_name(argv, out, ent, scope); +} + +int SubprogramHeader::emit_name(const std::vector&, + std::ostream&out, Entity*, ScopeBase*) const +{ + out << "\\" << name_; + return 0; +} + +int SubprogramHeader::emit_args(const std::vector&argv, + std::ostream&out, Entity*ent, ScopeBase*scope) const +{ + int errors = 0; + + for (size_t idx = 0; idx < argv.size() ; idx += 1) { + if (idx > 0) out << ", "; + errors += argv[idx]->emit(out, ent, scope); + } + + return errors; +} + +int SubprogramBuiltin::emit_name(const std::vector&, + std::ostream&out, Entity*, ScopeBase*) const +{ + // do not escape the names for builtin functions + out << sv_name_; + return 0; +} + +void emit_subprogram_sig(ostream&out, perm_string name, + const list&arg_types) +{ + out << name << "("; + bool first = true; + for(list::const_iterator it = arg_types.begin(); + it != arg_types.end(); ++it) { + if(first) + first = false; + else + out << ", "; + + if(*it) + (*it)->write_to_stream(out); + else + out << ""; + } + out << ")"; +} diff --git a/vhdlpp/vsignal.cc b/vhdlpp/vsignal.cc index 043c2b1a9..5d5eca4ff 100644 --- a/vhdlpp/vsignal.cc +++ b/vhdlpp/vsignal.cc @@ -21,7 +21,9 @@ # include "vsignal.h" # include "expression.h" +# include "scope.h" # include "vtype.h" +# include "std_types.h" # include using namespace std; @@ -35,11 +37,12 @@ SigVarBase::~SigVarBase() { } -void SigVarBase::elaborate_init_expr(Entity*ent, ScopeBase*scope) +void SigVarBase::elaborate(Entity*ent, ScopeBase*scope) { - if(init_expr_) { + if(init_expr_) init_expr_->elaborate_expr(ent, scope, peek_type()); - } + + type_->elaborate(ent, scope); } void SigVarBase::type_elaborate_(VType::decl_t&decl) @@ -47,34 +50,48 @@ void SigVarBase::type_elaborate_(VType::decl_t&decl) decl.type = type_; } -int Signal::emit(ostream&out, Entity*ent, ScopeBase*scope) +int Signal::emit(ostream&out, Entity*ent, ScopeBase*scope, bool initialize) { int errors = 0; VType::decl_t decl; type_elaborate_(decl); - if (peek_refcnt_sequ_() > 0 || !peek_type()->can_be_packed()) + + const VType*type = peek_type(); + if (peek_refcnt_sequ_() > 0 + || (!type->can_be_packed() && dynamic_cast(type))) decl.reg_flag = true; errors += decl.emit(out, peek_name()); - Expression*init_expr = peek_init_expr(); - if (init_expr) { - out << " = "; - init_expr->emit(out, ent, scope); + const Expression*init_expr = peek_init_expr(); + if (initialize && init_expr) { + /* Emit initialization value for wires as a weak assignment */ + if(!decl.reg_flag && !type->type_match(&primitive_REAL)) + out << ";" << endl << "/*init*/ assign (weak1, weak0) " << peek_name(); + + out << " = "; + init_expr->emit(out, ent, scope); } out << ";" << endl; return errors; } -int Variable::emit(ostream&out, Entity*, ScopeBase*) +int Variable::emit(ostream&out, Entity*ent, ScopeBase*scope, bool initialize) { int errors = 0; + out << (!scope->is_subprogram() ? "static " : "automatic "); + VType::decl_t decl; type_elaborate_(decl); - if (peek_refcnt_sequ_() > 0 || !peek_type()->can_be_packed()) - decl.reg_flag = true; + decl.reg_flag = true; errors += decl.emit(out, peek_name()); + + const Expression*init_expr = peek_init_expr(); + if (initialize && init_expr) { + out << " = "; + init_expr->emit(out, ent, scope); + } out << ";" << endl; return errors; } diff --git a/vhdlpp/vsignal.h b/vhdlpp/vsignal.h index d2bfdbcd0..27de1d778 100644 --- a/vhdlpp/vsignal.h +++ b/vhdlpp/vsignal.h @@ -42,18 +42,17 @@ class SigVarBase : public LineInfo { void dump(ostream&out, int indent = 0) const; - // Elaborates initializer expressions if needed. - void elaborate_init_expr(Entity*ent, ScopeBase*scope); + // Elaborates type & initializer expressions. + void elaborate(Entity*ent, ScopeBase*scope); perm_string peek_name() const { return name_; } + const Expression* peek_init_expr() const { return init_expr_; } protected: unsigned peek_refcnt_sequ_() const { return refcnt_sequ_; } void type_elaborate_(VType::decl_t&decl); - Expression* peek_init_expr() const { return init_expr_; } - private: perm_string name_; const VType*type_; @@ -71,15 +70,15 @@ class Signal : public SigVarBase { public: Signal(perm_string name, const VType*type, Expression*init_expr); - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope, bool initalize = true); }; class Variable : public SigVarBase { public: - Variable(perm_string name, const VType*type); + Variable(perm_string name, const VType*type, Expression*init_expr = NULL); - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope, bool initialize = true); void write_to_stream(std::ostream&fd); }; @@ -93,8 +92,8 @@ inline Signal::Signal(perm_string name, const VType*type, Expression*init_expr) { } -inline Variable::Variable(perm_string name, const VType*type) -: SigVarBase(name, type, 0) +inline Variable::Variable(perm_string name, const VType*type, Expression*init_expr) +: SigVarBase(name, type, init_expr) { } diff --git a/vhdlpp/vtype.cc b/vhdlpp/vtype.cc index 17001977f..d52d2e933 100644 --- a/vhdlpp/vtype.cc +++ b/vhdlpp/vtype.cc @@ -55,15 +55,9 @@ VTypePrimitive::~VTypePrimitive() void VTypePrimitive::show(ostream&out) const { switch (type_) { - case BOOLEAN: - out << "BOOLEAN"; - break; case BIT: out << "BIT"; break; - case CHARACTER: - out << "CHARACTER"; - break; case INTEGER: out << "INTEGER"; break; @@ -76,13 +70,15 @@ void VTypePrimitive::show(ostream&out) const case STDLOGIC: out << "STD_LOGIC"; break; + case TIME: + out << "TIME"; + break; } } int VTypePrimitive::get_width(ScopeBase*) const { switch(type_) { - case BOOLEAN: case BIT: case STDLOGIC: return 1; @@ -91,9 +87,6 @@ int VTypePrimitive::get_width(ScopeBase*) const case NATURAL: return 32; - case CHARACTER: - return 8; - default: std::cerr << "sorry: primitive type " << type_ << " has no get_width() implementation." << std::endl; @@ -115,21 +108,21 @@ VTypeArray::VTypeArray(const VType*element, const vector&r, /* * Create a VTypeArray range set from a list of parsed ranges. - * FIXME: We are copying pointers from the prange_t object into the - * range_t. This means that we cannot delete the prange_t object + * FIXME: We are copying pointers from the ExpRange object into the + * range_t. This means that we cannot delete the ExpRange object * unless we invent a way to remove the pointers from that object. So * this is a memory leak. Something to fix. */ -VTypeArray::VTypeArray(const VType*element, std::list*r, bool sv) +VTypeArray::VTypeArray(const VType*element, std::list*r, bool sv) : etype_(element), ranges_(r->size()), signed_flag_(sv), parent_(NULL) { for (size_t idx = 0 ; idx < ranges_.size() ; idx += 1) { - prange_t*curp = r->front(); + ExpRange*curp = r->front(); r->pop_front(); - Expression*msb = curp->msb(); - Expression*lsb = curp->lsb(); - bool dir = curp->is_downto(); - ranges_[idx] = range_t(msb, lsb, dir); + ranges_[idx] = range_t(curp->msb(), curp->lsb(), + (curp->direction() == ExpRange::DOWNTO + ? true + : false)); } } @@ -263,27 +256,46 @@ bool VTypeArray::is_variable_length(ScopeBase*scope) const { void VTypeArray::evaluate_ranges(ScopeBase*scope) { for(std::vector::iterator it = ranges_.begin(); it != ranges_.end(); ++it ) { int64_t lsb_val = -1, msb_val = -1; - bool dir = it->is_downto(); if(it->msb()->evaluate(scope, msb_val) && it->lsb()->evaluate(scope, lsb_val)) { assert(lsb_val >= 0); assert(msb_val >= 0); - *it = range_t(new ExpInteger(msb_val), new ExpInteger(lsb_val), dir); + *it = range_t(new ExpInteger(msb_val), new ExpInteger(lsb_val), msb_val > lsb_val); } } } -VTypeRange::VTypeRange(const VType*base, int64_t max_val, int64_t min_val) +VTypeRange::VTypeRange(const VType*base) : base_(base) { - max_ = max_val; - min_ = min_val; } VTypeRange::~VTypeRange() { } +VTypeRangeConst::VTypeRangeConst(const VType*base, int64_t start_val, int64_t end_val) +: VTypeRange(base), start_(start_val), end_(end_val) +{ +} + +VTypeRangeExpr::VTypeRangeExpr(const VType*base, Expression*start_expr, + Expression*end_expr, bool downto) +: VTypeRange(base), start_(start_expr), end_(end_expr), downto_(downto) +{ +} + +VTypeRangeExpr::~VTypeRangeExpr() +{ + delete start_; + delete end_; +} + +VType*VTypeRangeExpr::clone() const { + return new VTypeRangeExpr(base_type()->clone(), start_->clone(), + end_->clone(), downto_); +} + VTypeEnum::VTypeEnum(const std::list*names) : names_(names->size()) { diff --git a/vhdlpp/vtype.h b/vhdlpp/vtype.h index 5b00bc885..4f34c62ac 100644 --- a/vhdlpp/vtype.h +++ b/vhdlpp/vtype.h @@ -33,7 +33,7 @@ class Architecture; class ScopeBase; class Entity; class Expression; -class prange_t; +class ExpRange; class VTypeDef; class ScopeBase; @@ -72,6 +72,10 @@ class VType { // definitions. Most types accept the default definition of this. virtual void write_type_to_stream(std::ostream&fd) const; + // Emits a type definition. This is used to distinguish types and + // subtypes. + virtual void write_typedef_to_stream(std::ostream&fd, perm_string name) const; + // This virtual method writes a human-readable version of the // type to a given file for debug purposes. (Question: is this // really necessary given the write_to_stream method?) @@ -105,8 +109,6 @@ class VType { // to evaluate. virtual int get_width(ScopeBase*) const { return -1; } - private: - friend struct decl_t; // This virtual method is called to emit the declaration. This // is used by the decl_t object to emit variable/wire/port declarations. virtual int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; @@ -156,7 +158,7 @@ class VTypeERROR : public VType { class VTypePrimitive : public VType { public: - enum type_t { BOOLEAN, BIT, INTEGER, NATURAL, REAL, STDLOGIC, CHARACTER }; + enum type_t { BIT, INTEGER, NATURAL, REAL, STDLOGIC, TIME }; public: VTypePrimitive(type_t tt, bool packed = false); @@ -164,6 +166,7 @@ class VTypePrimitive : public VType { VType*clone() const { return new VTypePrimitive(*this); } + bool type_match(const VType*that) const; void write_to_stream(std::ostream&fd) const; void show(std::ostream&) const; int get_width(ScopeBase*scope) const; @@ -180,14 +183,6 @@ class VTypePrimitive : public VType { bool packed_; }; -extern const VTypePrimitive primitive_BOOLEAN; -extern const VTypePrimitive primitive_BIT; -extern const VTypePrimitive primitive_INTEGER; -extern const VTypePrimitive primitive_NATURAL; -extern const VTypePrimitive primitive_REAL; -extern const VTypePrimitive primitive_STDLOGIC; -extern const VTypePrimitive primitive_CHARACTER; - /* * An array is a compound N-dimensional array of element type. The * construction of the array is from an element type and a vector of @@ -218,19 +213,20 @@ class VTypeArray : public VType { public: VTypeArray(const VType*etype, const std::vector&r, bool signed_vector = false); - VTypeArray(const VType*etype, std::list*r, bool signed_vector = false); + VTypeArray(const VType*etype, std::list*r, bool signed_vector = false); VTypeArray(const VType*etype, int msb, int lsb, bool signed_vector = false); ~VTypeArray(); VType*clone() const; int elaborate(Entity*ent, ScopeBase*scope) const; + bool type_match(const VType*that) const; void write_to_stream(std::ostream&fd) const; void write_type_to_stream(std::ostream&fd) const; void show(std::ostream&) const; int get_width(ScopeBase*scope) const; - inline size_t dimensions() const { return ranges_.size(); }; + const std::vector&dimensions() const { return ranges_; }; const range_t&dimension(size_t idx) const { return ranges_[idx]; } @@ -257,6 +253,8 @@ class VTypeArray : public VType { // To handle subtypes inline void set_parent_type(const VTypeArray*parent) { parent_ = parent; } + const VTypeArray*get_parent_type() const { return parent_; } + // Wherever it is possible, replaces range lsb & msb expressions with // constant integers. void evaluate_ranges(ScopeBase*scope); @@ -264,6 +262,8 @@ class VTypeArray : public VType { private: int emit_with_dims_(std::ostream&out, bool packed, perm_string name) const; + // Handles a few special types of array (*_vector, string types). + bool write_special_case(std::ostream&out) const; void write_range_to_stream_(std::ostream&fd) const; const VType*etype_; @@ -275,27 +275,62 @@ class VTypeArray : public VType { class VTypeRange : public VType { public: - VTypeRange(const VType*base, int64_t max_val, int64_t min_val); - ~VTypeRange(); + VTypeRange(const VType*base); + virtual ~VTypeRange() = 0; - VType*clone() const { return new VTypeRange(base_->clone(), max_, min_); } + bool write_std_types(std::ostream&fd) const; + int emit_def(std::ostream&out, perm_string name) const; + bool type_match(const VType*that) const; // Get the type that is limited by the range. - inline const VType* base_type() const { return base_; } + inline const VType*base_type() const { return base_; } + + protected: + const VType*base_; +}; + +class VTypeRangeConst : public VTypeRange { + + public: + VTypeRangeConst(const VType*base, int64_t end, int64_t start); + + VType*clone() const { + return new VTypeRangeConst(base_type()->clone(), start_, end_); + } + + int64_t start() const { return start_; } + int64_t end() const { return end_; } + + void write_to_stream(std::ostream&fd) const; + + private: + const int64_t start_, end_; +}; + +class VTypeRangeExpr : public VTypeRange { + + public: + VTypeRangeExpr(const VType*base, Expression*end, Expression*start, bool downto); + ~VTypeRangeExpr(); + + VType*clone() const; + int elaborate(Entity*end, ScopeBase*scope) const; public: // Virtual methods void write_to_stream(std::ostream&fd) const; - int emit_def(std::ostream&out, perm_string name) const; private: - const VType*base_; - int64_t max_, min_; + // Boundaries + Expression*start_, *end_; + + // Range direction (downto/to) + bool downto_; }; class VTypeEnum : public VType { public: - VTypeEnum(const std::list*names); + explicit VTypeEnum(const std::list*names); ~VTypeEnum(); VType*clone() const { return new VTypeEnum(*this); } @@ -305,6 +340,7 @@ class VTypeEnum : public VType { int get_width(ScopeBase*) const { return 32; } int emit_def(std::ostream&out, perm_string name) const; + int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; // Checks if the name is stored in the enum. bool has_name(perm_string name) const; @@ -358,10 +394,12 @@ class VTypeDef : public VType { public: explicit VTypeDef(perm_string name); explicit VTypeDef(perm_string name, const VType*is); - ~VTypeDef(); + virtual ~VTypeDef(); VType*clone() const { return new VTypeDef(*this); } + bool type_match(const VType*that) const; + inline perm_string peek_name() const { return name_; } // If the type is not given a definition in the constructor, @@ -372,22 +410,28 @@ class VTypeDef : public VType { // type, and this method gets it for us. inline const VType* peek_definition(void) const { return type_; } - void write_to_stream(std::ostream&fd) const; + virtual void write_to_stream(std::ostream&fd) const; void write_type_to_stream(std::ostream&fd) const; int get_width(ScopeBase*scope) const { return type_->get_width(scope); } int emit_typedef(std::ostream&out, typedef_context_t&ctx) const; int emit_def(std::ostream&out, perm_string name) const; + int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; bool can_be_packed() const { return type_->can_be_packed(); } bool is_unbounded() const { return type_->is_unbounded(); } - private: - int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; - private: + protected: perm_string name_; const VType*type_; }; +class VSubTypeDef : public VTypeDef { + public: + explicit VSubTypeDef(perm_string name) : VTypeDef(name) {} + explicit VSubTypeDef(perm_string name, const VType*is) : VTypeDef(name, is) {} + void write_typedef_to_stream(std::ostream&fd, perm_string name) const; +}; + #endif /* IVL_vtype_H */ diff --git a/vhdlpp/vtype_elaborate.cc b/vhdlpp/vtype_elaborate.cc index b3b4cfd9d..e32d4d28e 100644 --- a/vhdlpp/vtype_elaborate.cc +++ b/vhdlpp/vtype_elaborate.cc @@ -29,7 +29,8 @@ int VType::elaborate(Entity*, ScopeBase*) const int VTypeArray::elaborate(Entity*ent, ScopeBase*scope) const { int errors = 0; - etype_->elaborate(ent, scope); + + errors += etype_->elaborate(ent, scope); for (vector::const_iterator cur = ranges_.begin() ; cur != ranges_.end() ; ++ cur) { @@ -43,3 +44,14 @@ int VTypeArray::elaborate(Entity*ent, ScopeBase*scope) const return errors; } + +int VTypeRangeExpr::elaborate(Entity*ent, ScopeBase*scope) const +{ + int errors = 0; + + errors += base_->elaborate(ent, scope); + errors += start_->elaborate_expr(ent, scope, 0); + errors += end_->elaborate_expr(ent, scope, 0); + + return errors; +} diff --git a/vhdlpp/vtype_emit.cc b/vhdlpp/vtype_emit.cc index 64577e004..81807fa4b 100644 --- a/vhdlpp/vtype_emit.cc +++ b/vhdlpp/vtype_emit.cc @@ -22,6 +22,7 @@ # include "vtype.h" # include "expression.h" +# include "std_types.h" # include # include # include @@ -64,7 +65,15 @@ int VTypeArray::emit_def(ostream&out, perm_string name) const const VTypePrimitive*base = dynamic_cast (raw_base); if (base) { - assert(dimensions() == 1); + assert(dimensions().size() == 1); + + // If this is a string type without any boundaries specified, then + // there is a direct counterpart in SV called.. 'string' + if(this == &primitive_STRING) { + out << "string"; + emit_name(out, name); + return errors; + } base->emit_def(out, empty_perm_string); if (signed_flag_) @@ -89,9 +98,21 @@ int VTypeArray::emit_with_dims_(std::ostream&out, bool packed, perm_string name) list dims; const VTypeArray*cur = this; - while (const VTypeArray*sub = dynamic_cast (cur->etype_)) { - dims.push_back(cur); - cur = sub; + bool added_dim = true; + + while(added_dim) { + added_dim = false; + const VType*el_type = cur->element_type(); + + while(const VTypeDef*tdef = dynamic_cast(el_type)) { + el_type = tdef->peek_definition(); + } + + if(const VTypeArray*sub = dynamic_cast(el_type)) { + dims.push_back(cur); + cur = sub; + added_dim = true; + } } dims.push_back(cur); @@ -106,16 +127,16 @@ int VTypeArray::emit_with_dims_(std::ostream&out, bool packed, perm_string name) name_emitted = true; } - for(unsigned i = 0; i < cur->dimensions(); ++i) { + for(unsigned i = 0; i < cur->dimensions().size(); ++i) { if(cur->dimension(i).is_box() && !name_emitted) { emit_name(out, name); name_emitted = true; } out << "["; - if (!cur->dimension(i).is_box()) { // if not unbounded { + if (!cur->dimension(i).is_box()) { // if not unbounded errors += cur->dimension(i).msb()->emit(out, 0, 0); - out << ":"; + out << ":"; errors += cur->dimension(i).lsb()->emit(out, 0, 0); } out << "]"; @@ -132,7 +153,7 @@ int VTypeArray::emit_with_dims_(std::ostream&out, bool packed, perm_string name) int VTypeEnum::emit_def(ostream&out, perm_string name) const { int errors = 0; - out << "enum {"; + out << "enum int {"; assert(names_.size() >= 1); out << "\\" << names_[0] << " "; for (size_t idx = 1 ; idx < names_.size() ; idx += 1) @@ -144,13 +165,23 @@ int VTypeEnum::emit_def(ostream&out, perm_string name) const return errors; } +int VTypeEnum::emit_decl(std::ostream&out, perm_string name, bool reg_flag) const +{ + if (!reg_flag) + out << "wire "; + + out << "int"; + emit_name(out, name); + + return 0; +} + int VTypePrimitive::emit_primitive_type(ostream&out) const { int errors = 0; switch (type_) { - case BOOLEAN: case BIT: - out << "bool"; + out << "bit"; break; case STDLOGIC: out << "logic"; @@ -164,8 +195,8 @@ int VTypePrimitive::emit_primitive_type(ostream&out) const case REAL: out << "real"; break; - case CHARACTER: - out << "char"; + case TIME: + out << "time"; break; default: assert(0); @@ -215,29 +246,15 @@ int VTypeRecord::emit_def(ostream&out, perm_string name) const */ int VTypeDef::emit_def(ostream&out, perm_string name) const { - int errors = 0; emit_name(out, name_); emit_name(out, name); - return errors; + + return 0; } int VTypeDef::emit_decl(ostream&out, perm_string name, bool reg_flag) const { - int errors = 0; - if (reg_flag) - out << "reg "; - else - out << "wire "; - - if(dynamic_cast(type_)) { - errors += type_->emit_def(out, name); - } else { - assert(name_ != empty_perm_string); - cout << "\\" << name_; - emit_name(out, name); - } - - return errors; + return type_->emit_decl(out, name, reg_flag); } int VTypeDef::emit_typedef(ostream&out, typedef_context_t&ctx) const diff --git a/vhdlpp/vtype_match.cc b/vhdlpp/vtype_match.cc index e0559d24d..c99a2fd78 100644 --- a/vhdlpp/vtype_match.cc +++ b/vhdlpp/vtype_match.cc @@ -22,5 +22,85 @@ bool VType::type_match(const VType*that) const { - return this == that; + if(this == that) + return true; + + if(const VTypeDef*tdef = dynamic_cast(that)) { + if(type_match(tdef->peek_definition())) + return true; + } + + return false; +} + +bool VTypeDef::type_match(const VType*that) const +{ + if(VType::type_match(that)) + return true; + + return type_->type_match(that); +} + +bool VTypePrimitive::type_match(const VType*that) const +{ + if(VType::type_match(that)) + return true; + + if(const VTypePrimitive*prim = dynamic_cast(that)) { + // TODO it is not always true, but works for many cases + type_t that_type = prim->type(); + return ((type_ == NATURAL || type_ == INTEGER) && + (that_type == NATURAL || that_type == INTEGER)); + } + + if(const VTypeRangeConst*range = dynamic_cast(that)) { + if (type_ == INTEGER) + return true; + if (type_ == NATURAL && range->start() >= 0 && range->end() >= 0) + return true; + } + + return false; +} + +bool VTypeArray::type_match(const VType*that) const +{ + if(VType::type_match(that)) + return true; + + // Check if both arrays are of the same size + if(const VTypeArray*arr = dynamic_cast(that)) { + const VTypeArray*this_parent = this; + while(const VTypeArray*tmp = this_parent->get_parent_type()) + this_parent = tmp; + + const VTypeArray*that_parent = arr; + while(const VTypeArray*tmp = that_parent->get_parent_type()) + that_parent = tmp; + + if(this_parent != that_parent) + return false; + + int this_width = get_width(NULL); + int that_width = arr->get_width(NULL); + + // Either one of the sizes is undefined, or both are the same size + if(this_width > 0 && that_width > 0 && this_width != that_width) + return false; + + return true; + } + + return false; +} + +bool VTypeRange::type_match(const VType*that) const +{ + if(VType::type_match(that)) + return true; + + if(base_->type_match(that)) + return true; + + return false; } diff --git a/vhdlpp/vtype_stream.cc b/vhdlpp/vtype_stream.cc index f625f190a..2869d6772 100644 --- a/vhdlpp/vtype_stream.cc +++ b/vhdlpp/vtype_stream.cc @@ -18,7 +18,7 @@ */ # define __STDC_LIMIT_MACROS -# include "vtype.h" +# include "std_types.h" # include "expression.h" # include # include @@ -36,20 +36,20 @@ void VType::write_type_to_stream(ostream&fd) const write_to_stream(fd); } +void VType::write_typedef_to_stream(ostream&fd, perm_string name) const +{ + if(is_global_type(name)) + return; + + fd << "type " << name << " is "; + write_type_to_stream(fd); + fd << ";" << endl; +} + void VTypeArray::write_to_stream(ostream&fd) const { - // Special cases: std_logic_vector & string - if (etype_ == &primitive_STDLOGIC) { - fd << "std_logic_vector"; - if (! ranges_.empty() && ! ranges_[0].is_box()) { - write_range_to_stream_(fd); - } - return; - } else if (etype_ == &primitive_CHARACTER && - ranges_.size() == 1 && ranges_[0].is_box()) { - fd << "string"; - return; - } + if(write_special_case(fd)) + return; bool typedefed = false; if(const VTypeDef*tdef = dynamic_cast(etype_)) { @@ -94,19 +94,37 @@ void VTypeArray::write_range_to_stream_(std::ostream&fd) const fd << ") "; } +bool VTypeArray::write_special_case(std::ostream&fd) const +{ + if(this == &primitive_SIGNED) { + fd << "signed"; + } else if(this == &primitive_UNSIGNED) { + fd << "unsigned"; + } else if(etype_ == &primitive_STDLOGIC) { + fd << "std_logic_vector"; + } else if(etype_ == &primitive_BIT) { + fd << "bit_vector"; + } else if(etype_ == &primitive_CHARACTER) { + fd << "string"; + } else { + return false; + } + + if(!ranges_.empty() && !ranges_[0].is_box()) { + write_range_to_stream_(fd); + } + + return true; +} + void VTypeArray::write_type_to_stream(ostream&fd) const { - // Special case: std_logic_vector - if (etype_ == &primitive_STDLOGIC) { - fd << "std_logic_vector"; - if (! ranges_.empty() && ! ranges_[0].is_box()) { - write_range_to_stream_(fd); - } - return; - } + if(write_special_case(fd)) + return; fd << "array "; + // Unbounded array if (! ranges_.empty()) { assert(ranges_.size() < 2); if (ranges_[0].is_box()) { @@ -153,11 +171,8 @@ void VTypePrimitive::write_to_stream(ostream&fd) const case STDLOGIC: fd << "std_logic"; break; - case CHARACTER: - fd << "character"; - break; - case BOOLEAN: - fd << "boolean"; + case TIME: + fd << "time"; break; default: assert(0); @@ -166,19 +181,41 @@ void VTypePrimitive::write_to_stream(ostream&fd) const } } -void VTypeRange::write_to_stream(ostream&fd) const +bool VTypeRange::write_std_types(ostream&fd) const { - // Detect some special cases that can be written as ieee or - // standard types. - if (const VTypePrimitive*tmp = dynamic_cast (base_)) { - if (min_==0 && max_==INT64_MAX && tmp->type()==VTypePrimitive::INTEGER) { - fd << "natural"; - return; - } - } + // Detect some special cases that can be written as ieee or + // standard types. + if (const VTypePrimitive*tmp = dynamic_cast(base_)) { + if (tmp->type()==VTypePrimitive::NATURAL) { + fd << "natural"; + return true; + } + } - base_->write_to_stream(fd); - fd << " range " << min_ << " to " << max_; + return false; +} + +void VTypeRangeConst::write_to_stream(ostream&fd) const +{ + if(write_std_types(fd)) + return; + + base_type()->write_to_stream(fd); + fd << " range " << start_; + fd << (start_ < end_ ? " to " : " downto "); + fd << end_; +} + +void VTypeRangeExpr::write_to_stream(ostream&fd) const +{ + if(write_std_types(fd)) + return; + + base_type()->write_to_stream(fd); + fd << " range "; + start_->write_to_stream(fd); + fd << (downto_ ? " downto " : " to "); + end_->write_to_stream(fd); } void VTypeRecord::write_to_stream(ostream&fd) const @@ -211,3 +248,12 @@ void VTypeEnum::write_to_stream(std::ostream&fd) const fd << ")"; } +void VSubTypeDef::write_typedef_to_stream(ostream&fd, perm_string name) const +{ + if(is_global_type(name)) + return; + + fd << "subtype " << name << " is "; + write_type_to_stream(fd); + fd << ";" << endl; +} diff --git a/vpi/Makefile.in b/vpi/Makefile.in index 50e367d6a..a2ee88145 100644 --- a/vpi/Makefile.in +++ b/vpi/Makefile.in @@ -45,18 +45,19 @@ else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif -CPPFLAGS = $(INCLUDE_PATH) @file64_support@ @CPPFLAGS@ @DEFS@ @PICFLAG@ +CPPFLAGS = $(INCLUDE_PATH) @file64_support@ @CPPFLAGS@ @DEFS@ -DICARUS_VPI_CONST=const @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ LDFLAGS = @LDFLAGS@ # Object files for system.vpi -O = sys_table.o sys_convert.o sys_countdrivers.o sys_darray.o sys_deposit.o sys_display.o \ +O = sys_table.o sys_convert.o sys_countdrivers.o sys_darray.o sys_deposit.o \ + sys_display.o \ sys_fileio.o sys_finish.o sys_icarus.o sys_plusargs.o sys_queue.o \ sys_random.o sys_random_mti.o sys_readmem.o sys_readmem_lex.o sys_scanf.o \ sys_sdf.o sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o mt19937int.o \ sys_priv.o sdf_parse.o sdf_lexor.o stringheap.o vams_simparam.o \ - table_mod.o table_mod_lexor.o table_mod_parse.o + table_mod.o table_mod_parse.o table_mod_lexor.o OPP = vcd_priv2.o ifeq (@HAVE_LIBZ@,yes) @@ -73,13 +74,16 @@ M = sys_clog2.o v2005_math.o # Object files for va_math.vpi V = va_math.o -V2009 = v2009_table.o v2009_array.o v2009_enum.o v2009_string.o +V2009 = v2009_table.o v2009_array.o v2009_bitvec.o v2009_enum.o v2009_string.o \ + sys_priv.o -VHDL_SYS = vhdl_table.o +VHDL_SYS = vhdl_table.o sys_priv.o + +VHDL_TEXTIO = vhdl_textio.o sys_priv.o VPI_DEBUG = vpi_debug.o -all: dep system.vpi va_math.vpi v2005_math.vpi v2009.vpi vhdl_sys.vpi vpi_debug.vpi $(ALL32) +all: dep system.vpi va_math.vpi v2005_math.vpi v2009.vpi vhdl_sys.vpi vhdl_textio.vpi vpi_debug.vpi $(ALL32) check: all @@ -88,14 +92,14 @@ clean: rm -f sdf_lexor.c sdf_parse.c sdf_parse.output sdf_parse.h rm -f table_mod_parse.c table_mod_parse.h table_mod_parse.output rm -f table_mod_lexor.c - rm -f va_math.vpi v2005_math.vpi v2009.vpi vhdl_sys.vpi vpi_debug.vpi + rm -f va_math.vpi v2005_math.vpi v2009.vpi vhdl_sys.vpi vhdl_textio.vpi vpi_debug.vpi distclean: clean rm -f Makefile config.log rm -f vpi_config.h stamp-vpi_config-h # The -U flag is used to skip checking paths that depend on that define having -# an explict value (i.e. the define is expected to be real code). +# an explicit value (i.e. the define is expected to be real code). cppcheck: $(O:.o=.c) $(OPP:.o=.cc) $(M:.o=.c) $(V:.o=.c) cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ -UYY_USER_INIT \ @@ -163,6 +167,9 @@ va_math.vpi: $V ../vvp/libvpi.a vhdl_sys.vpi: $(VHDL_SYS) ../vvp/libvpi.a $(CC) @shared@ -o $@ $(VHDL_SYS) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) +vhdl_textio.vpi: $(VHDL_TEXTIO) ../vvp/libvpi.a + $(CC) @shared@ -o $@ $(VHDL_TEXTIO) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) + vpi_debug.vpi: $(VPI_DEBUG) ../vvp/libvpi.a $(CC) @shared@ -o $@ $(VPI_DEBUG) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) @@ -177,6 +184,7 @@ install: all installdirs \ $(vpidir)/v2005_math.vpi $(vpidir)/v2005_math.sft \ $(vpidir)/v2009.vpi $(vpidir)/v2009.sft \ $(vpidir)/vhdl_sys.vpi $(vpidir)/vhdl_sys.sft \ + $(vpidir)/vhdl_textio.vpi $(vpidir)/vhdl_textio.sft \ $(vpidir)/vpi_debug.vpi $(vpidir)/system.vpi: ./system.vpi @@ -209,6 +217,12 @@ $(vpidir)/vhdl_sys.vpi: ./vhdl_sys.vpi $(vpidir)/vhdl_sys.sft: vhdl_sys.sft $(INSTALL_DATA) $< "$(DESTDIR)$@" +$(vpidir)/vhdl_textio.vpi: ./vhdl_textio.vpi + $(INSTALL_PROGRAM) ./vhdl_textio.vpi "$(DESTDIR)$(vpidir)/vhdl_textio.vpi" + +$(vpidir)/vhdl_textio.sft: vhdl_textio.sft + $(INSTALL_DATA) $< "$(DESTDIR)$@" + $(vpidir)/vpi_debug.vpi: ./vpi_debug.vpi $(INSTALL_PROGRAM) ./vpi_debug.vpi "$(DESTDIR)$(vpidir)/vpi_debug.vpi" @@ -226,6 +240,8 @@ uninstall: rm -f "$(DESTDIR)$(vpidir)/v2009.sft" rm -f "$(DESTDIR)$(vpidir)/vhdl_sys.vpi" rm -f "$(DESTDIR)$(vpidir)/vhdl_sys.sft" + rm -f "$(DESTDIR)$(vpidir)/vhdl_textio.vpi" + rm -f "$(DESTDIR)$(vpidir)/vhdl_textio.sft" rm -f "$(DESTDIR)$(vpidir)/vpi_debug.vpi" -include $(patsubst %.o, dep/%.d, $O) diff --git a/vpi/cppcheck.sup b/vpi/cppcheck.sup index fc7332cff..f0fd8584d 100644 --- a/vpi/cppcheck.sup +++ b/vpi/cppcheck.sup @@ -3,150 +3,149 @@ // problems will not be fixed. // fstapi.c from GTKWave -obsoleteFunctionsasctime:fstapi.c:929 -obsoleteFunctionsalloca:fstapi.c:2303 -unreadVariable:fstapi.c:1647 -variableScope:fstapi.c:665 +shiftNegative:fstapi.c:673 +asctimeCalled:fstapi.c:930 +allocaCalled:fstapi.c:2305 +variableScope:fstapi.c:666 +variableScope:fstapi.c:1030 variableScope:fstapi.c:1337 variableScope:fstapi.c:1338 -variableScope:fstapi.c:1395 -variableScope:fstapi.c:1978 -variableScope:fstapi.c:2107 -variableScope:fstapi.c:2538 -variableScope:fstapi.c:2539 -variableScope:fstapi.c:2730 -variableScope:fstapi.c:2731 -variableScope:fstapi.c:2735 -variableScope:fstapi.c:2851 -variableScope:fstapi.c:2890 -variableScope:fstapi.c:2891 -variableScope:fstapi.c:3609 -variableScope:fstapi.c:3783 -variableScope:fstapi.c:3785 -variableScope:fstapi.c:4227 -variableScope:fstapi.c:4228 -variableScope:fstapi.c:4237 -variableScope:fstapi.c:4500 -variableScope:fstapi.c:4771 -variableScope:fstapi.c:4774 -variableScope:fstapi.c:5268 +variableScope:fstapi.c:1339 +variableScope:fstapi.c:1396 +variableScope:fstapi.c:1979 +variableScope:fstapi.c:2108 +variableScope:fstapi.c:2541 +variableScope:fstapi.c:2542 +variableScope:fstapi.c:2733 +variableScope:fstapi.c:2734 +variableScope:fstapi.c:2738 +variableScope:fstapi.c:2854 +variableScope:fstapi.c:2893 +variableScope:fstapi.c:2894 +variableScope:fstapi.c:3613 +variableScope:fstapi.c:3787 +variableScope:fstapi.c:3789 +variableScope:fstapi.c:4231 +variableScope:fstapi.c:4232 +variableScope:fstapi.c:4241 +variableScope:fstapi.c:4504 +variableScope:fstapi.c:4775 +variableScope:fstapi.c:4778 variableScope:fstapi.c:5272 -variableScope:fstapi.c:5273 -variableScope:fstapi.c:5429 -variableScope:fstapi.c:5487 -variableScope:fstapi.c:5800 -variableScope:fstapi.c:5803 -variableScope:fstapi.c:6039 -variableScope:fstapi.c:6144 -variableScope:fstapi.c:6145 -variableScope:fstapi.c:6176 -variableScope:fstapi.c:6404 +variableScope:fstapi.c:5276 +variableScope:fstapi.c:5277 +variableScope:fstapi.c:5433 +variableScope:fstapi.c:5491 +variableScope:fstapi.c:5804 +variableScope:fstapi.c:5807 +variableScope:fstapi.c:6101 +variableScope:fstapi.c:6206 +variableScope:fstapi.c:6207 +variableScope:fstapi.c:6238 +variableScope:fstapi.c:6466 // These functions are not used by Icarus // fstReaderClrFacProcessMask() -unusedFunction:fstapi.c:3341 +unusedFunction:fstapi.c:3345 // fstReaderClrFacProcessMaskAll() -unusedFunction:fstapi.c:3370 +unusedFunction:fstapi.c:3374 // fstReaderGetAliasCount() -unusedFunction:fstapi.c:3433 +unusedFunction:fstapi.c:3437 // fstReaderGetCurrentFlatScope() -unusedFunction:fstapi.c:3179 +unusedFunction:fstapi.c:3183 // fstReaderGetAliasCount() -unusedFunction:fstapi.c:3276 +unusedFunction:fstapi.c:3280 // fstReaderGetCurrentScopeUserInfo() -unusedFunction:fstapi.c:3193 +unusedFunction:fstapi.c:3197 // fstReaderGetDateString() -unusedFunction:fstapi.c:3461 +unusedFunction:fstapi.c:3465 // fstReaderGetDoubleEndianMatchState() -unusedFunction:fstapi.c:3447 +unusedFunction:fstapi.c:3451 // fstReaderGetDumpActivityChangeTime() -unusedFunction:fstapi.c:3489 +unusedFunction:fstapi.c:3493 // fstReaderGetDumpActivityChangeValue() -unusedFunction:fstapi.c:3504 +unusedFunction:fstapi.c:3508 // fstReaderGetEndTime() -unusedFunction:fstapi.c:3398 +unusedFunction:fstapi.c:3402 // fstReaderGetFacProcessMask() -unusedFunction:fstapi.c:3304 +unusedFunction:fstapi.c:3308 // fstReaderGetFileType() -unusedFunction:fstapi.c:3468 +unusedFunction:fstapi.c:3472 // fstReaderGetFseekFailed() -unusedFunction:fstapi.c:3289 +unusedFunction:fstapi.c:3293 // fstReaderGetMaxHandle() -unusedFunction:fstapi.c:3426 +unusedFunction:fstapi.c:3430 // fstReaderGetMemoryUsedByWriter() -unusedFunction:fstapi.c:3405 +unusedFunction:fstapi.c:3409 // fstReaderGetNumberDumpActivityChanges() -unusedFunction:fstapi.c:3482 +unusedFunction:fstapi.c:3486 // fstReaderGetScopeCount() -unusedFunction:fstapi.c:3412 +unusedFunction:fstapi.c:3416 // fstReaderGetStartTime() -unusedFunction:fstapi.c:3391 +unusedFunction:fstapi.c:3395 // fstReaderGetTimescale() -unusedFunction:fstapi.c:3384 +unusedFunction:fstapi.c:3388 // fstReaderGetTimezero() -unusedFunction:fstapi.c:3475 +unusedFunction:fstapi.c:3479 // fstReaderGetValueChangeSectionCount() -unusedFunction:fstapi.c:3440 +unusedFunction:fstapi.c:3444 // fstReaderGetValueFromHandleAtTime() -unusedFunction:fstapi.c:5682 +unusedFunction:fstapi.c:5686 // fstReaderGetVarCount() -unusedFunction:fstapi.c:3419 +unusedFunction:fstapi.c:3423 // fstReaderGetVersionString() -unusedFunction:fstapi.c:3454 +unusedFunction:fstapi.c:3458 // fstReaderIterBlocks() -unusedFunction:fstapi.c:4649 +unusedFunction:fstapi.c:4653 // fstReaderIterBlocksSetNativeDoublesOnCallback() -unusedFunction:fstapi.c:3554 +unusedFunction:fstapi.c:3558 // fstReaderIterateHier() -unusedFunction:fstapi.c:3779 +unusedFunction:fstapi.c:3783 // fstReaderIterateHierRewind() -unusedFunction:fstapi.c:3759 +unusedFunction:fstapi.c:3763 // fstReaderOpen() -unusedFunction:fstapi.c:4547 +unusedFunction:fstapi.c:4551 // fstReaderOpenForUtilitiesOnly() -unusedFunction:fstapi.c:4539 +unusedFunction:fstapi.c:4543 // fstReaderPushScope() -unusedFunction:fstapi.c:3241 +unusedFunction:fstapi.c:3245 // fstReaderResetScope() -unusedFunction:fstapi.c:3230 +unusedFunction:fstapi.c:3234 // fstReaderSetFacProcessMask() -unusedFunction:fstapi.c:3323 +unusedFunction:fstapi.c:3327 // fstReaderSetFacProcessMaskAll() -unusedFunction:fstapi.c:3359 +unusedFunction:fstapi.c:3363 // fstReaderSetLimitTimeRange() -unusedFunction:fstapi.c:3519 +unusedFunction:fstapi.c:3523 // fstReaderSetUnlimitedTimeRange() -unusedFunction:fstapi.c:3532 +unusedFunction:fstapi.c:3536 // fstReaderSetVcdExtensions() -unusedFunction:fstapi.c:3543 +unusedFunction:fstapi.c:3547 // fstUtilityEscToBin() -unusedFunction:fstapi.c:6482 +unusedFunction:fstapi.c:6544 // fstWriterCreateVar2() -unusedFunction:fstapi.c:2525 +unusedFunction:fstapi.c:2528 // fstWriterEmitVariableLengthValueChange() -unusedFunction:fstapi.c:2844 +unusedFunction:fstapi.c:2847 // fstWriterGetFseekFailed() -unusedFunction:fstapi.c:2508 +unusedFunction:fstapi.c:2511 // fstWriterSetAttrEnd() -unusedFunction:fstapi.c:2711 +unusedFunction:fstapi.c:2714 // fstWriterSetComment() -unusedFunction:fstapi.c:2357 +unusedFunction:fstapi.c:2360 // fstWriterSetEnvVar() -unusedFunction:fstapi.c:2369 +unusedFunction:fstapi.c:2372 // fstWriterSetFileType() -unusedFunction:fstapi.c:2234 +unusedFunction:fstapi.c:2235 // fstWriterSetParallelMode() -unusedFunction:fstapi.c:2468 +unusedFunction:fstapi.c:2471 // fstWriterSetTimezero() -unusedFunction:fstapi.c:2433 +unusedFunction:fstapi.c:2436 // fstWriterSetValueList() -unusedFunction:fstapi.c:2363 - -// These functions are not used by Icarus -//unusedFunction:fstapi.c:226 +unusedFunction:fstapi.c:2366 // lxt2_write.c from GTKWave -obsoleteFunctionsalloca:lxt2_write.c:1813 -obsoleteFunctionsalloca:lxt2_write.c:1819 +allocaCalled:lxt2_write.c:1813 +allocaCalled:lxt2_write.c:1819 variableScope:lxt2_write.c:33 variableScope:lxt2_write.c:63 variableScope:lxt2_write.c:196 @@ -181,6 +180,8 @@ unusedFunction:lxt2_write.c:1581 unusedFunction:lxt2_write.c:877 // lxt_write.c from GTKWave +shiftNegative:lxt_write.c:2700 +shiftNegative:lxt_write.c:2744 variableScope:lxt_write.c:31 variableScope:lxt_write.c:83 variableScope:lxt_write.c:527 @@ -227,66 +228,75 @@ unusedFunction:lxt_write.c:1585 unusedFunction:lxt_write.c:1277 // fastlz.c from GTKWave +unusedLabel:fastlz.c:545 // These functions are not used by Icarus // fastlz_compress_level() unusedFunction:fastlz.c:150 +// FASTLZ_COMPRESSOR() +unusedFunction:fastlz.c:162 +// FASTLZ_DECOMPRESSOR() +unusedFunction:fastlz.c:416 // lz4.c from GTKWave // These functions are not used by Icarus // LZ4_compress_continue() -unusedFunction:lz4.c:883 +unusedFunction:lz4.c:1460 +// LZ4_compress_destSize() +unusedFunction:lz4.c:912 +// LZ4_compress_fast_force() +unusedFunction:lz4.c:705 // LZ4_compress_forceExtDict() -unusedFunction:lz4.c:895 +unusedFunction:lz4.c:1063 // LZ4_compress_limitedOutput() -unusedFunction:lz4.c:730 +unusedFunction:lz4.c:1455 // LZ4_compress_limitedOutput_continue() -unusedFunction:lz4.c:888 +unusedFunction:lz4.c:1459 // LZ4_compress_limitedOutput_withState() -unusedFunction:lz4.c:1343 +unusedFunction:lz4.c:1457 // LZ4_compress_withState() -unusedFunction:lz4.c:1332 +unusedFunction:lz4.c:1458 // LZ4_create() -unusedFunction:lz4.c:1314 +unusedFunction:lz4.c:1489 // LZ4_createStream() -unusedFunction:lz4.c:765 +unusedFunction:lz4.c:935 // LZ4_createStreamDecode() -unusedFunction:lz4.c:1152 +unusedFunction:lz4.c:1319 // LZ4_decompress_fast_continue() -unusedFunction:lz4.c:1217 +unusedFunction:lz4.c:1384 // LZ4_decompress_fast_usingDict() -unusedFunction:lz4.c:1272 +unusedFunction:lz4.c:1439 // LZ4_decompress_fast_withPrefix64k() -unusedFunction:lz4.c:1361 +unusedFunction:lz4.c:1510 // LZ4_decompress_safe_continue() -unusedFunction:lz4.c:1188 +unusedFunction:lz4.c:1355 // LZ4_decompress_safe_forceExtDict() -unusedFunction:lz4.c:1278 +unusedFunction:lz4.c:1445 // LZ4_decompress_safe_usingDict() -unusedFunction:lz4.c:1267 +unusedFunction:lz4.c:1434 // LZ4_decompress_safe_withPrefix64k() -unusedFunction:lz4.c:1356 +unusedFunction:lz4.c:1505 // LZ4_freeStream() -unusedFunction:lz4.c:773 +unusedFunction:lz4.c:948 // LZ4_freeStreamDecode() -unusedFunction:lz4.c:1158 +unusedFunction:lz4.c:1325 // LZ4_loadDict() -unusedFunction:lz4.c:780 +unusedFunction:lz4.c:956 // LZ4_resetStreamState() -unusedFunction:lz4.c:1307 +unusedFunction:lz4.c:1482 // LZ4_setStreamDecode() -unusedFunction:lz4.c:1171 +unusedFunction:lz4.c:1338 // LZ4_sizeofState() -unusedFunction:lz4.c:1330 +unusedFunction:lz4.c:373 // LZ4_sizeofStreamState() -unusedFunction:lz4.c:1299 +unusedFunction:lz4.c:1474 // LZ4_slideInputBuffer() -unusedFunction:lz4.c:1321 +unusedFunction:lz4.c:1496 // LZ4_uncompress() -unusedFunction:lz4.c:1293 +unusedFunction:lz4.c:1468 // LZ4_uncompress_unknownOutputSize() -unusedFunction:lz4.c:1294 +unusedFunction:lz4.c:1469 // LZ4_versionNumber() -unusedFunction:lz4.c:428 +unusedFunction:lz4.c:371 // The routines in sys_random.c are exact copies from IEEE1364-2005 and // they have scope warnings that we need to ignore. diff --git a/vpi/fstapi.c b/vpi/fstapi.c index 5ec842da2..35f601191 100644 --- a/vpi/fstapi.c +++ b/vpi/fstapi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2015 Tony Bybell. + * Copyright (c) 2009-2018 Tony Bybell. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -128,6 +128,7 @@ void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint3 #include #endif +#define FST_APIMESS "FSTAPI | " /***********************/ /*** ***/ @@ -193,7 +194,7 @@ if(nam) /* cppcheck warning fix: nam is always defined, so this is not needed */ dwRetVal = GetTempPath(MAX_PATH, lpTempPathBuffer); if((dwRetVal > MAX_PATH) || (dwRetVal == 0)) { - fprintf(stderr, "GetTempPath() failed in "__FILE__" line %d, exiting.\n", __LINE__); + fprintf(stderr, FST_APIMESS"GetTempPath() failed in "__FILE__" line %d, exiting.\n", __LINE__); exit(255); } else @@ -201,7 +202,7 @@ if(nam) /* cppcheck warning fix: nam is always defined, so this is not needed */ uRetVal = GetTempFileName(lpTempPathBuffer, TEXT("FSTW"), 0, szTempFileName); if (uRetVal == 0) { - fprintf(stderr, "GetTempFileName() failed in "__FILE__" line %d, exiting.\n", __LINE__); + fprintf(stderr, FST_APIMESS"GetTempFileName() failed in "__FILE__" line %d, exiting.\n", __LINE__); exit(255); } else @@ -279,11 +280,10 @@ return(ftruncate(fd, length)); static char *fstRealpath(const char *path, char *resolved_path) { #if defined __USE_BSD || defined __USE_XOPEN_EXTENDED || defined __CYGWIN__ || defined HAVE_REALPATH - #if (defined(__MACH__) && defined(__APPLE__)) if(!resolved_path) { - resolved_path = malloc(PATH_MAX+1); /* fixes bug on Leopard when resolved_path == NULL */ + resolved_path = (char *)malloc(PATH_MAX+1); /* fixes bug on Leopard when resolved_path == NULL */ } #endif @@ -293,10 +293,12 @@ return(realpath(path, resolved_path)); #ifdef __MINGW32__ if(!resolved_path) { - resolved_path = malloc(PATH_MAX+1); + resolved_path = (char *)malloc(PATH_MAX+1); } return(_fullpath(resolved_path, path, PATH_MAX)); #else +(void)path; +(void)resolved_path; return(NULL); #endif #endif @@ -315,7 +317,7 @@ static void *fstMmap2(size_t __len, int __fd, off_t __off) { (void)__off; -unsigned char *pnt = malloc(__len); +unsigned char *pnt = (unsigned char *)malloc(__len); off_t cur_offs = lseek(__fd, 0, SEEK_CUR); size_t i; @@ -791,6 +793,8 @@ char *geom_handle_nam; char *valpos_handle_nam; char *curval_handle_nam; char *tchn_handle_nam; + +fstEnumHandle max_enumhandle; }; @@ -802,7 +806,7 @@ if(rc<0) { xc->fseek_failed = 1; #ifdef FST_DEBUG - fprintf(stderr, "Seek to #%"PRId64" (whence = %d) failed!\n", offset, whence); + fprintf(stderr, FST_APIMESS"Seek to #%" PRId64 " (whence = %d) failed!\n", offset, whence); perror("Why"); #endif } @@ -968,12 +972,12 @@ fflush(xc->handle); if(!xc->valpos_mem) { fflush(xc->valpos_handle); - xc->valpos_mem = fstMmap(NULL, xc->maxhandle * 4 * sizeof(uint32_t), PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->valpos_handle), 0); + xc->valpos_mem = (uint32_t *)fstMmap(NULL, xc->maxhandle * 4 * sizeof(uint32_t), PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->valpos_handle), 0); } if(!xc->curval_mem) { fflush(xc->curval_handle); - xc->curval_mem = fstMmap(NULL, xc->maxvalpos, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->curval_handle), 0); + xc->curval_mem = (unsigned char *)fstMmap(NULL, xc->maxvalpos, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->curval_handle), 0); } } @@ -1104,7 +1108,7 @@ xc->next_huge_break = FST_ACTIVATE_HUGE_BREAK; */ void *fstWriterCreate(const char *nam, int use_compressed_hier) { -struct fstWriterContext *xc = calloc(1, sizeof(struct fstWriterContext)); +struct fstWriterContext *xc = (struct fstWriterContext *)calloc(1, sizeof(struct fstWriterContext)); xc->compress_hier = use_compressed_hier; fstDetermineBreakSize(xc); @@ -1118,7 +1122,7 @@ if((!nam)|| else { int flen = strlen(nam); - char *hf = calloc(1, flen + 6); + char *hf = (char *)calloc(1, flen + 6); memcpy(hf, nam, flen); strcpy(hf + flen, ".hier"); @@ -1129,7 +1133,7 @@ if((!nam)|| xc->curval_handle = tmpfile_open(&xc->curval_handle_nam); /* .bits */ xc->tchn_handle = tmpfile_open(&xc->tchn_handle_nam); /* .tchn */ xc->vchg_alloc_siz = xc->fst_break_size + xc->fst_break_add_size; - xc->vchg_mem = malloc(xc->vchg_alloc_siz); + xc->vchg_mem = (unsigned char *)malloc(xc->vchg_alloc_siz); if(xc->hier_handle && xc->geom_handle && xc->valpos_handle && xc->curval_handle && xc->vchg_mem && xc->tchn_handle) { @@ -1178,7 +1182,7 @@ if(xc) int rc; destlen = xc->maxvalpos; - dmem = malloc(compressBound(destlen)); + dmem = (unsigned char *)malloc(compressBound(destlen)); rc = compress2(dmem, &destlen, xc->curval_mem, xc->maxvalpos, 4); /* was 9...which caused performance drag on traces with many signals */ fputc(FST_BL_SKIP, xc->handle); /* temporarily tag the section, use FST_BL_VCDATA on finalize */ @@ -1267,7 +1271,7 @@ if((xc->vchg_siz <= 1)||(xc->already_in_flush)) return; xc->already_in_flush = 1; /* should really do this with a semaphore */ xc->section_header_only = 0; -scratchpad = malloc(xc->vchg_siz); +scratchpad = (unsigned char *)malloc(xc->vchg_siz); vchg_mem = xc->vchg_mem; @@ -1277,7 +1281,7 @@ fputc(xc->fourpack ? '4' : (xc->fastpack ? 'F' : 'Z'), f); fpos = 1; packmemlen = 1024; /* maintain a running "longest" allocation to */ -packmem = malloc(packmemlen); /* prevent continual malloc...free every loop iter */ +packmem = (unsigned char *)malloc(packmemlen); /* prevent continual malloc...free every loop iter */ for(i=0;imaxhandle;i++) { @@ -1397,13 +1401,13 @@ for(i=0;imaxhandle;i++) idx = ((vm4ip[1]+7) & ~7); switch(vm4ip[1] & 7) { - case 0: do { acc = (pnt[idx+7-8] & 1) << 0; - case 7: acc |= (pnt[idx+6-8] & 1) << 1; - case 6: acc |= (pnt[idx+5-8] & 1) << 2; - case 5: acc |= (pnt[idx+4-8] & 1) << 3; - case 4: acc |= (pnt[idx+3-8] & 1) << 4; - case 3: acc |= (pnt[idx+2-8] & 1) << 5; - case 2: acc |= (pnt[idx+1-8] & 1) << 6; + case 0: do { acc = (pnt[idx+7-8] & 1) << 0; /* fallthrough */ + case 7: acc |= (pnt[idx+6-8] & 1) << 1; /* fallthrough */ + case 6: acc |= (pnt[idx+5-8] & 1) << 2; /* fallthrough */ + case 5: acc |= (pnt[idx+4-8] & 1) << 3; /* fallthrough */ + case 4: acc |= (pnt[idx+3-8] & 1) << 4; /* fallthrough */ + case 3: acc |= (pnt[idx+2-8] & 1) << 5; /* fallthrough */ + case 2: acc |= (pnt[idx+1-8] & 1) << 6; /* fallthrough */ case 1: acc |= (pnt[idx+0-8] & 1) << 7; *(--scratchpnt) = acc; idx -= 8; @@ -1439,7 +1443,7 @@ for(i=0;imaxhandle;i++) else { free(packmem); - dmem = packmem = malloc(compressBound(packmemlen = wrlen)); + dmem = packmem = (unsigned char *)malloc(compressBound(packmemlen = wrlen)); } rc = compress2(dmem, &destlen, scratchpnt, wrlen, 4); @@ -1449,12 +1453,12 @@ for(i=0;imaxhandle;i++) PPvoid_t pv = JudyHSIns(&PJHSArray, dmem, destlen, NULL); if(*pv) { - uint32_t pvi = (long)(*pv); + uint32_t pvi = (intptr_t)(*pv); vm4ip[2] = -pvi; } else { - *pv = (void *)(long)(i+1); + *pv = (void *)(intptr_t)(i+1); #endif fpos += fstWriterVarint(f, wrlen); fpos += destlen; @@ -1469,12 +1473,12 @@ for(i=0;imaxhandle;i++) PPvoid_t pv = JudyHSIns(&PJHSArray, scratchpnt, wrlen, NULL); if(*pv) { - uint32_t pvi = (long)(*pv); + uint32_t pvi = (intptr_t)(*pv); vm4ip[2] = -pvi; } else { - *pv = (void *)(long)(i+1); + *pv = (void *)(intptr_t)(i+1); #endif fpos += fstWriterVarint(f, 0); fpos += wrlen; @@ -1494,7 +1498,7 @@ for(i=0;imaxhandle;i++) else { free(packmem); - dmem = packmem = malloc(packmemlen = (wrlen * 2) + 2); + dmem = packmem = (unsigned char *)malloc(packmemlen = (wrlen * 2) + 2); } rc = (xc->fourpack) ? LZ4_compress((char *)scratchpnt, (char *)dmem, wrlen) : fastlz_compress(scratchpnt, wrlen, dmem); @@ -1504,12 +1508,12 @@ for(i=0;imaxhandle;i++) PPvoid_t pv = JudyHSIns(&PJHSArray, dmem, rc, NULL); if(*pv) { - uint32_t pvi = (long)(*pv); + uint32_t pvi = (intptr_t)(*pv); vm4ip[2] = -pvi; } else { - *pv = (void *)(long)(i+1); + *pv = (void *)(intptr_t)(i+1); #endif fpos += fstWriterVarint(f, wrlen); fpos += rc; @@ -1524,12 +1528,12 @@ for(i=0;imaxhandle;i++) PPvoid_t pv = JudyHSIns(&PJHSArray, scratchpnt, wrlen, NULL); if(*pv) { - uint32_t pvi = (long)(*pv); + uint32_t pvi = (intptr_t)(*pv); vm4ip[2] = -pvi; } else { - *pv = (void *)(long)(i+1); + *pv = (void *)(intptr_t)(i+1); #endif fpos += fstWriterVarint(f, 0); fpos += wrlen; @@ -1546,12 +1550,12 @@ for(i=0;imaxhandle;i++) PPvoid_t pv = JudyHSIns(&PJHSArray, scratchpnt, wrlen, NULL); if(*pv) { - uint32_t pvi = (long)(*pv); + uint32_t pvi = (intptr_t)(*pv); vm4ip[2] = -pvi; } else { - *pv = (void *)(long)(i+1); + *pv = (void *)(intptr_t)(i+1); #endif fpos += fstWriterVarint(f, 0); fpos += wrlen; @@ -1662,7 +1666,7 @@ if(zerocnt) /* fpos += */ fstWriterVarint(f, (zerocnt << 1)); /* scan-build */ } #ifdef FST_DEBUG -fprintf(stderr, "value chains: %d\n", cnt); +fprintf(stderr, FST_APIMESS"value chains: %d\n", cnt); #endif xc->vchg_mem[0] = '!'; @@ -1676,11 +1680,11 @@ fflush(xc->tchn_handle); tlen = ftello(xc->tchn_handle); fstWriterFseeko(xc, xc->tchn_handle, 0, SEEK_SET); -tmem = fstMmap(NULL, tlen, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->tchn_handle), 0); +tmem = (unsigned char *)fstMmap(NULL, tlen, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->tchn_handle), 0); if(tmem) { unsigned long destlen = tlen; - unsigned char *dmem = malloc(compressBound(destlen)); + unsigned char *dmem = (unsigned char *)malloc(compressBound(destlen)); int rc = compress2(dmem, &destlen, tmem, tlen, 9); if((rc == Z_OK) && (((off_t)destlen) < tlen)) @@ -1737,7 +1741,7 @@ if(xc->dump_size_limit) xc2->size_limit_locked = 1; xc2->is_initial_time = 1; /* to trick emit value and emit time change */ #ifdef FST_DEBUG - fprintf(stderr, "<< dump file size limit reached, stopping dumping >>\n"); + fprintf(stderr, FST_APIMESS"<< dump file size limit reached, stopping dumping >>\n"); #endif } } @@ -1779,7 +1783,7 @@ struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc->parallel_enabled) { - struct fstWriterContext *xc2 = malloc(sizeof(struct fstWriterContext)); + struct fstWriterContext *xc2 = (struct fstWriterContext *)malloc(sizeof(struct fstWriterContext)); unsigned int i; pthread_mutex_lock(&xc->mutex); @@ -1788,16 +1792,16 @@ if(xc->parallel_enabled) xc->xc_parent = xc; memcpy(xc2, xc, sizeof(struct fstWriterContext)); - xc2->valpos_mem = malloc(xc->maxhandle * 4 * sizeof(uint32_t)); + xc2->valpos_mem = (uint32_t *)malloc(xc->maxhandle * 4 * sizeof(uint32_t)); memcpy(xc2->valpos_mem, xc->valpos_mem, xc->maxhandle * 4 * sizeof(uint32_t)); /* curval mem is updated in the thread */ #ifdef FST_REMOVE_DUPLICATE_VC - xc2->curval_mem = malloc(xc->maxvalpos); + xc2->curval_mem = (unsigned char *)malloc(xc->maxvalpos); memcpy(xc2->curval_mem, xc->curval_mem, xc->maxvalpos); #endif - xc->vchg_mem = malloc(xc->vchg_alloc_siz); + xc->vchg_mem = (unsigned char *)malloc(xc->vchg_alloc_siz); xc->vchg_mem[0] = '!'; xc->vchg_siz = 1; @@ -1906,11 +1910,11 @@ if(xc && !xc->already_in_close && !xc->already_in_flush) /* write out geom section */ fflush(xc->geom_handle); tlen = ftello(xc->geom_handle); - tmem = fstMmap(NULL, tlen, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->geom_handle), 0); + tmem = (unsigned char *)fstMmap(NULL, tlen, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->geom_handle), 0); if(tmem) { unsigned long destlen = tlen; - unsigned char *dmem = malloc(compressBound(destlen)); + unsigned char *dmem = (unsigned char *)malloc(compressBound(destlen)); int rc = compress2(dmem, &destlen, tmem, tlen, 9); if((rc != Z_OK) || (((off_t)destlen) > tlen)) @@ -1978,7 +1982,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush) int zfd; int fourpack_duo = 0; #ifndef __MINGW32__ - char *fnam = malloc(strlen(xc->filename) + 5 + 1); + char *fnam = (char *)malloc(strlen(xc->filename) + 5 + 1); #endif fixup_offs = ftello(xc->handle); @@ -1989,7 +1993,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush) if(!xc->fourpack) { - unsigned char *mem = malloc(FST_GZIO_LEN); + unsigned char *mem = (unsigned char *)malloc(FST_GZIO_LEN); zfd = dup(fileno(xc->handle)); fflush(xc->handle); zhandle = gzdopen(zfd, "wb4"); @@ -2020,8 +2024,8 @@ if(xc && !xc->already_in_close && !xc->already_in_flush) fflush(xc->handle); lz4_maxlen = LZ4_compressBound(xc->hier_file_len); - mem = malloc(lz4_maxlen); - hmem = fstMmap(NULL, xc->hier_file_len, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->hier_handle), 0); + mem = (unsigned char *)malloc(lz4_maxlen); + hmem = (unsigned char *)fstMmap(NULL, xc->hier_file_len, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->hier_handle), 0); packed_len = LZ4_compress((char *)hmem, (char *)mem, xc->hier_file_len); fstMunmap(hmem, xc->hier_file_len); @@ -2034,7 +2038,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush) int packed_len_duo; lz4_maxlen_duo = LZ4_compressBound(packed_len); - mem_duo = malloc(lz4_maxlen_duo); + mem_duo = (unsigned char *)malloc(lz4_maxlen_duo); packed_len_duo = LZ4_compress((char *)mem, (char *)mem_duo, packed_len); fstWriterVarint(xc->handle, packed_len); /* 1st round compressed length */ @@ -2094,7 +2098,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush) FILE *fp; off_t offpnt, uclen; int flen = strlen(xc->filename); - char *hf = calloc(1, flen + 5); + char *hf = (char *)calloc(1, flen + 5); strcpy(hf, xc->filename); strcpy(hf+flen, ".pak"); @@ -2102,7 +2106,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush) if(fp) { - void *dsth; + gzFile dsth; int zfd; char gz_membuf[FST_GZIO_LEN]; @@ -2158,7 +2162,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush) #ifdef __MINGW32__ { int flen = strlen(xc->filename); - char *hf = calloc(1, flen + 6); + char *hf = (char *)calloc(1, flen + 6); strcpy(hf, xc->filename); if(xc->compress_hier) @@ -2299,22 +2303,24 @@ if(xc && path && path[0]) #ifndef _WAVE_HAVE_JUDY const uint32_t hashmask = FST_PATH_HASHMASK; const unsigned char *path2 = (const unsigned char *)path; + PPvoid_t pv; #else - char *path2 = alloca(slen + 1); /* judy lacks const qualifier in its JudyHSIns definition */ + char *path2 = (char *)alloca(slen + 1); /* judy lacks const qualifier in its JudyHSIns definition */ + PPvoid_t pv; strcpy(path2, path); #endif - PPvoid_t pv = JudyHSIns(&(xc->path_array), path2, slen, NULL); + pv = JudyHSIns(&(xc->path_array), path2, slen, NULL); if(*pv) { - sidx = (long)(*pv); + sidx = (intptr_t)(*pv); } else { char *rp = NULL; sidx = ++xc->path_array_count; - *pv = (void *)(long)(xc->path_array_count); + *pv = (void *)(intptr_t)(xc->path_array_count); if(use_realpath) { @@ -2475,7 +2481,7 @@ if(xc) #ifndef FST_WRITER_PARALLEL if(xc->parallel_enabled) { - fprintf(stderr, "ERROR: fstWriterSetParallelMode(), FST_WRITER_PARALLEL not enabled during compile, exiting.\n"); + fprintf(stderr, FST_APIMESS"fstWriterSetParallelMode(), FST_WRITER_PARALLEL not enabled during compile, exiting.\n"); exit(255); } #endif @@ -2582,7 +2588,7 @@ if(xc && nam) xc->vchg_alloc_siz = xc->fst_break_size + xc->fst_break_add_size; if(xc->vchg_mem) { - xc->vchg_mem = realloc(xc->vchg_mem, xc->vchg_alloc_siz); + xc->vchg_mem = (unsigned char *)realloc(xc->vchg_mem, xc->vchg_alloc_siz); } } } @@ -2720,6 +2726,111 @@ if(xc) } +fstEnumHandle fstWriterCreateEnumTable(void *ctx, const char *name, uint32_t elem_count, unsigned int min_valbits, const char **literal_arr, const char **val_arr) +{ +fstEnumHandle handle = 0; +unsigned int *literal_lens = NULL; +unsigned int *val_lens = NULL; +int lit_len_tot = 0; +int val_len_tot = 0; +int name_len; +char elem_count_buf[16]; +int elem_count_len; +int total_len; +int pos = 0; +char *attr_str = NULL; + +if(ctx && name && literal_arr && val_arr && (elem_count != 0)) + { + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + + uint32_t i; + + name_len = strlen(name); + elem_count_len = sprintf(elem_count_buf, "%" PRIu32, elem_count); + + literal_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int)); + val_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int)); + + for(i=0;i 0) + { + if(val_lens[i] < min_valbits) + { + val_len_tot += (min_valbits - val_lens[i]); /* additional converted len is same for '0' character */ + } + } + } + + total_len = name_len + 1 + elem_count_len + 1 + lit_len_tot + elem_count + val_len_tot + elem_count; + + attr_str = (char*)malloc(total_len); + pos = 0; + + memcpy(attr_str+pos, name, name_len); + pos += name_len; + attr_str[pos++] = ' '; + + memcpy(attr_str+pos, elem_count_buf, elem_count_len); + pos += elem_count_len; + attr_str[pos++] = ' '; + + for(i=0;i 0) + { + if(val_lens[i] < min_valbits) + { + memset(attr_str+pos, '0', min_valbits - val_lens[i]); + pos += (min_valbits - val_lens[i]); + } + } + + pos += fstUtilityBinToEsc((unsigned char*)attr_str+pos, (unsigned char*)val_arr[i], val_lens[i]); + attr_str[pos++] = ' '; + } + + attr_str[pos-1] = 0; + +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS"fstWriterCreateEnumTable() total_len: %d, pos: %d\n", total_len, pos); + fprintf(stderr, FST_APIMESS"*%s*\n", attr_str); +#endif + + fstWriterSetAttrBegin(xc, FST_AT_MISC, FST_MT_ENUMTABLE, attr_str, handle = ++xc->max_enumhandle); + + free(attr_str); + free(val_lens); + free(literal_lens); + } + +return(handle); +} + + +void fstWriterEmitEnumTableRef(void *ctx, fstEnumHandle handle) +{ +struct fstWriterContext *xc = (struct fstWriterContext *)ctx; +if(xc && handle) + { + fstWriterSetAttrBegin(xc, FST_AT_MISC, FST_MT_ENUMTABLE, NULL, handle); + } +} + + /* * value and time change emission */ @@ -2754,10 +2865,10 @@ if((xc) && (handle <= xc->maxhandle)) if((fpos + len + 10) > xc->vchg_alloc_siz) { xc->vchg_alloc_siz += (xc->fst_break_add_size + len); /* +len added in the case of extremely long vectors and small break add sizes */ - xc->vchg_mem = realloc(xc->vchg_mem, xc->vchg_alloc_siz); + xc->vchg_mem = (unsigned char *)realloc(xc->vchg_mem, xc->vchg_alloc_siz); if(!xc->vchg_mem) { - fprintf(stderr, "FATAL ERROR, could not realloc() in fstWriterEmitValueChange, exiting.\n"); + fprintf(stderr, FST_APIMESS"Could not realloc() in fstWriterEmitValueChange, exiting.\n"); exit(255); } } @@ -2868,10 +2979,10 @@ if((xc) && (handle <= xc->maxhandle)) if((fpos + len + 10 + 5) > xc->vchg_alloc_siz) { xc->vchg_alloc_siz += (xc->fst_break_add_size + len + 5); /* +len added in the case of extremely long vectors and small break add sizes */ - xc->vchg_mem = realloc(xc->vchg_mem, xc->vchg_alloc_siz); + xc->vchg_mem = (unsigned char *)realloc(xc->vchg_mem, xc->vchg_alloc_siz); if(!xc->vchg_mem) { - fprintf(stderr, "FATAL ERROR, could not realloc() in fstWriterEmitVariableLengthValueChange, exiting.\n"); + fprintf(stderr, FST_APIMESS"Could not realloc() in fstWriterEmitVariableLengthValueChange, exiting.\n"); exit(255); } } @@ -2945,7 +3056,7 @@ struct fstWriterContext *xc = (struct fstWriterContext *)ctx; if(xc) { - struct fstBlackoutChain *b = calloc(1, sizeof(struct fstBlackoutChain)); + struct fstBlackoutChain *b = (struct fstBlackoutChain *)calloc(1, sizeof(struct fstBlackoutChain)); b->tim = xc->curtime; b->active = (enable != 0); @@ -3071,6 +3182,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; @@ -3115,7 +3227,7 @@ if(rc<0) { xc->fseek_failed = 1; #ifdef FST_DEBUG - fprintf(stderr, "Seek to #%"PRId64" (whence = %d) failed!\n", offset, whence); + fprintf(stderr, FST_APIMESS"Seek to #%" PRId64 " (whence = %d) failed!\n", offset, whence); perror("Why"); #endif } @@ -3243,12 +3355,12 @@ const char *fstReaderPushScope(void *ctx, const char *nam, void *user_info) struct fstReaderContext *xc = (struct fstReaderContext *)ctx; if(xc) { - struct fstCurrHier *ch = malloc(sizeof(struct fstCurrHier)); + struct fstCurrHier *ch = (struct fstCurrHier *)malloc(sizeof(struct fstCurrHier)); int chl = xc->curr_hier ? xc->curr_hier->len : 0; int len = chl + 1 + strlen(nam); if(len >= xc->flat_hier_alloc_len) { - xc->curr_flat_hier_nam = xc->curr_flat_hier_nam ? realloc(xc->curr_flat_hier_nam, len+1) : malloc(len+1); + xc->curr_flat_hier_nam = xc->curr_flat_hier_nam ? (char *)realloc(xc->curr_flat_hier_nam, len+1) : (char *)malloc(len+1); } if(chl) @@ -3468,7 +3580,7 @@ return(xc ? xc->date : NULL); int fstReaderGetFileType(void *ctx) { struct fstReaderContext *xc = (struct fstReaderContext *)ctx; -return(xc ? xc->filetype : FST_FT_VERILOG); +return(xc ? (int)xc->filetype : (int)FST_FT_VERILOG); } @@ -3601,8 +3713,8 @@ int pass_status = 1; if(!xc->fh) { off_t offs_cache = ftello(xc->f); - char *fnam = malloc(strlen(xc->filename) + 6 + 16 + 32 + 1); - unsigned char *mem = malloc(FST_GZIO_LEN); + char *fnam = (char *)malloc(strlen(xc->filename) + 6 + 16 + 32 + 1); + unsigned char *mem = (unsigned char *)malloc(FST_GZIO_LEN); off_t hl, uclen; off_t clen = 0; gzFile zhandle = NULL; @@ -3699,8 +3811,8 @@ if(!xc->fh) else if(htyp == FST_BL_HIER_LZ4DUO) { - unsigned char *lz4_cmem = malloc(clen); - unsigned char *lz4_ucmem = malloc(uclen); + unsigned char *lz4_cmem = (unsigned char *)malloc(clen); + unsigned char *lz4_ucmem = (unsigned char *)malloc(uclen); unsigned char *lz4_ucmem2; uint64_t uclen2; int skiplen2 = 0; @@ -3708,7 +3820,7 @@ if(!xc->fh) fstFread(lz4_cmem, clen, 1, xc->f); uclen2 = fstGetVarint64(lz4_cmem, &skiplen2); - lz4_ucmem2 = malloc(uclen2); + lz4_ucmem2 = (unsigned char *)malloc(uclen2); pass_status = (uclen2 == (uint64_t)LZ4_decompress_safe_partial ((char *)lz4_cmem + skiplen2, (char *)lz4_ucmem2, clen - skiplen2, uclen2, uclen2)); if(pass_status) { @@ -3727,8 +3839,8 @@ if(!xc->fh) else if(htyp == FST_BL_HIER_LZ4) { - unsigned char *lz4_cmem = malloc(clen); - unsigned char *lz4_ucmem = malloc(uclen); + unsigned char *lz4_cmem = (unsigned char *)malloc(clen); + unsigned char *lz4_ucmem = (unsigned char *)malloc(uclen); fstFread(lz4_cmem, clen, 1, xc->f); pass_status = (uclen == LZ4_decompress_safe_partial ((char *)lz4_cmem, (char *)lz4_ucmem, clen, uclen, uclen)); @@ -3744,6 +3856,10 @@ if(!xc->fh) else /* FST_BL_SKIP */ { pass_status = 0; + if(xc->fh) + { + fclose(xc->fh); xc->fh = NULL; /* needed in case .hier file is missing and there are no hier sections */ + } } free(mem); @@ -3961,7 +4077,7 @@ if(!xc->fh) } } -str = malloc(FST_ID_NAM_ATTR_SIZ+1); +str = (char *)malloc(FST_ID_NAM_ATTR_SIZ+1); if(fv) { @@ -3970,40 +4086,40 @@ if(fv) fprintf(fv, "$date\n\t%s\n$end\n", xc->date); fprintf(fv, "$version\n\t%s\n$end\n", xc->version); - if(xc->timezero) fprintf(fv, "$timezero\n\t%"PRId64"\n$end\n", xc->timezero); + if(xc->timezero) fprintf(fv, "$timezero\n\t%" PRId64 "\n$end\n", xc->timezero); switch(xc->timescale) { case 2: time_scale = 100; time_dimension[0] = 0; break; - case 1: time_scale = 10; + case 1: time_scale = 10; /* fallthrough */ case 0: time_dimension[0] = 0; break; case -1: time_scale = 100; time_dimension[0] = 'm'; break; - case -2: time_scale = 10; + case -2: time_scale = 10; /* fallthrough */ case -3: time_dimension[0] = 'm'; break; case -4: time_scale = 100; time_dimension[0] = 'u'; break; - case -5: time_scale = 10; + case -5: time_scale = 10; /* fallthrough */ case -6: time_dimension[0] = 'u'; break; case -10: time_scale = 100; time_dimension[0] = 'p'; break; - case -11: time_scale = 10; + case -11: time_scale = 10; /* fallthrough */ case -12: time_dimension[0] = 'p'; break; case -13: time_scale = 100; time_dimension[0] = 'f'; break; - case -14: time_scale = 10; + case -14: time_scale = 10; /* fallthrough */ case -15: time_dimension[0] = 'f'; break; case -16: time_scale = 100; time_dimension[0] = 'a'; break; - case -17: time_scale = 10; + case -17: time_scale = 10; /* fallthrough */ case -18: time_dimension[0] = 'a'; break; case -19: time_scale = 100; time_dimension[0] = 'z'; break; - case -20: time_scale = 10; + case -20: time_scale = 10; /* fallthrough */ case -21: time_dimension[0] = 'z'; break; case -7: time_scale = 100; time_dimension[0] = 'n'; break; - case -8: time_scale = 10; + case -8: time_scale = 10; /* fallthrough */ case -9: default: time_dimension[0] = 'n'; break; } @@ -4015,10 +4131,10 @@ xc->maxhandle = 0; xc->num_alias = 0; free(xc->signal_lens); -xc->signal_lens = malloc(num_signal_dyn*sizeof(uint32_t)); +xc->signal_lens = (uint32_t *)malloc(num_signal_dyn*sizeof(uint32_t)); free(xc->signal_typs); -xc->signal_typs = malloc(num_signal_dyn*sizeof(unsigned char)); +xc->signal_typs = (unsigned char *)malloc(num_signal_dyn*sizeof(unsigned char)); fstReaderFseeko(xc, xc->fh, 0, SEEK_SET); while(!feof(xc->fh)) @@ -4063,13 +4179,13 @@ while(!feof(xc->fh)) switch(attrtype) { case FST_AT_ARRAY: if((subtype < FST_AR_NONE) || (subtype > FST_AR_MAX)) subtype = FST_AR_NONE; - fprintf(fv, "$attrbegin %s %s %s %"PRId64" $end\n", attrtypes[attrtype], arraytypes[subtype], str, attrarg); + fprintf(fv, "$attrbegin %s %s %s %" PRId64 " $end\n", attrtypes[attrtype], arraytypes[subtype], str, attrarg); break; case FST_AT_ENUM: if((subtype < FST_EV_SV_INTEGER) || (subtype > FST_EV_MAX)) subtype = FST_EV_SV_INTEGER; - fprintf(fv, "$attrbegin %s %s %s %"PRId64" $end\n", attrtypes[attrtype], enumvaluetypes[subtype], str, attrarg); + fprintf(fv, "$attrbegin %s %s %s %" PRId64 " $end\n", attrtypes[attrtype], enumvaluetypes[subtype], str, attrarg); break; case FST_AT_PACK: if((subtype < FST_PT_NONE) || (subtype > FST_PT_MAX)) subtype = FST_PT_NONE; - fprintf(fv, "$attrbegin %s %s %s %"PRId64" $end\n", attrtypes[attrtype], packtypes[subtype], str, attrarg); + fprintf(fv, "$attrbegin %s %s %s %" PRId64 " $end\n", attrtypes[attrtype], packtypes[subtype], str, attrarg); break; case FST_AT_MISC: default: attrtype = FST_AT_MISC; @@ -4084,11 +4200,11 @@ while(!feof(xc->fh)) int sidx_skiplen_dummy = 0; uint64_t sidx = fstGetVarint64((unsigned char *)str, &sidx_skiplen_dummy); - fprintf(fv, "$attrbegin %s %02x %"PRId64" %"PRId64" $end\n", attrtypes[attrtype], subtype, sidx, attrarg); + fprintf(fv, "$attrbegin %s %02x %" PRId64 " %" PRId64 " $end\n", attrtypes[attrtype], subtype, sidx, attrarg); } else { - fprintf(fv, "$attrbegin %s %02x %s %"PRId64" $end\n", attrtypes[attrtype], subtype, str, attrarg); + fprintf(fv, "$attrbegin %s %02x %s %" PRId64 " $end\n", attrtypes[attrtype], subtype, str, attrarg); } } break; @@ -4146,8 +4262,8 @@ while(!feof(xc->fh)) if(xc->maxhandle == num_signal_dyn) { num_signal_dyn *= 2; - xc->signal_lens = realloc(xc->signal_lens, num_signal_dyn*sizeof(uint32_t)); - xc->signal_typs = realloc(xc->signal_typs, num_signal_dyn*sizeof(unsigned char)); + xc->signal_lens = (uint32_t *)realloc(xc->signal_lens, num_signal_dyn*sizeof(uint32_t)); + xc->signal_typs = (unsigned char *)realloc(xc->signal_typs, num_signal_dyn*sizeof(unsigned char)); } xc->signal_lens[xc->maxhandle] = len; xc->signal_typs[xc->maxhandle] = vartype; @@ -4168,7 +4284,7 @@ while(!feof(xc->fh)) char vcdid_buf[16]; uint32_t modlen = (vartype != FST_VT_VCD_PORT) ? len : ((len - 2) / 3); fstVcdID(vcdid_buf, xc->maxhandle+1); - fprintf(fv, "$var %s %"PRIu32" %s %s $end\n", vartypes[vartype], modlen, vcdid_buf, str); + fprintf(fv, "$var %s %" PRIu32 " %s %s $end\n", vartypes[vartype], modlen, vcdid_buf, str); } xc->maxhandle++; } @@ -4184,7 +4300,7 @@ while(!feof(xc->fh)) char vcdid_buf[16]; uint32_t modlen = (vartype != FST_VT_VCD_PORT) ? len : ((len - 2) / 3); fstVcdID(vcdid_buf, alias); - fprintf(fv, "$var %s %"PRIu32" %s %s $end\n", vartypes[vartype], modlen, vcdid_buf, str); + fprintf(fv, "$var %s %" PRIu32 " %s %s $end\n", vartypes[vartype], modlen, vcdid_buf, str); } xc->num_alias++; } @@ -4199,14 +4315,14 @@ if(fv) fprintf(fv, "$enddefinitions $end\n"); maxhandle_scanbuild = xc->maxhandle ? xc->maxhandle : 1; /*scan-build warning suppression, in reality we have at least one signal */ -xc->signal_lens = realloc(xc->signal_lens, maxhandle_scanbuild*sizeof(uint32_t)); -xc->signal_typs = realloc(xc->signal_typs, maxhandle_scanbuild*sizeof(unsigned char)); +xc->signal_lens = (uint32_t *)realloc(xc->signal_lens, maxhandle_scanbuild*sizeof(uint32_t)); +xc->signal_typs = (unsigned char *)realloc(xc->signal_typs, maxhandle_scanbuild*sizeof(unsigned char)); free(xc->process_mask); -xc->process_mask = calloc(1, (maxhandle_scanbuild+7)/8); +xc->process_mask = (unsigned char *)calloc(1, (maxhandle_scanbuild+7)/8); free(xc->temp_signal_value_buf); -xc->temp_signal_value_buf = malloc(xc->longest_signal_value_len + 1); +xc->temp_signal_value_buf = (unsigned char *)malloc(xc->longest_signal_value_len + 1); xc->var_count = xc->maxhandle + xc->num_alias; @@ -4235,7 +4351,7 @@ if(sectype == FST_BL_ZWRAPPER) FILE *fcomp; off_t offpnt, uclen; char gz_membuf[FST_GZIO_LEN]; - void *zhandle; + gzFile zhandle; int zfd; int flen = strlen(xc->filename); char *hf; @@ -4245,7 +4361,7 @@ if(sectype == FST_BL_ZWRAPPER) if(!seclen) return(0); /* not finished compressing, this is a failed read */ - hf = calloc(1, flen + 16 + 32 + 1); + hf = (char *)calloc(1, flen + 16 + 32 + 1); sprintf(hf, "%s.upk_%d_%p", xc->filename, getpid(), (void *)xc); fcomp = fopen(hf, "w+b"); @@ -4408,7 +4524,7 @@ if(gzread_pass_status) { uint64_t clen = seclen - 24; uint64_t uclen = fstReaderUint64(xc->f); - unsigned char *ucdata = malloc(uclen); + unsigned char *ucdata = (unsigned char *)malloc(uclen); unsigned char *pnt = ucdata; unsigned int i; @@ -4417,11 +4533,11 @@ if(gzread_pass_status) xc->longest_signal_value_len = 32; /* arbitrarily set at 32...this is much longer than an expanded double */ free(xc->process_mask); - xc->process_mask = calloc(1, (xc->maxhandle+7)/8); + xc->process_mask = (unsigned char *)calloc(1, (xc->maxhandle+7)/8); if(clen != uclen) { - unsigned char *cdata = malloc(clen); + unsigned char *cdata = (unsigned char *)malloc(clen); unsigned long destlen = uclen; unsigned long sourcelen = clen; int rc; @@ -4431,7 +4547,7 @@ if(gzread_pass_status) if(rc != Z_OK) { - printf("geom uncompress rc = %d\n", rc); + fprintf(stderr, FST_APIMESS"fstReaderInit(), geom uncompress rc = %d, exiting.\n", rc); exit(255); } @@ -4443,9 +4559,9 @@ if(gzread_pass_status) } free(xc->signal_lens); - xc->signal_lens = malloc(sizeof(uint32_t) * xc->maxhandle); + xc->signal_lens = (uint32_t *)malloc(sizeof(uint32_t) * xc->maxhandle); free(xc->signal_typs); - xc->signal_typs = malloc(sizeof(unsigned char) * xc->maxhandle); + xc->signal_typs = (unsigned char *)malloc(sizeof(unsigned char) * xc->maxhandle); for(i=0;imaxhandle;i++) { @@ -4472,7 +4588,7 @@ if(gzread_pass_status) } free(xc->temp_signal_value_buf); - xc->temp_signal_value_buf = malloc(xc->longest_signal_value_len + 1); + xc->temp_signal_value_buf = (unsigned char *)malloc(xc->longest_signal_value_len + 1); free(ucdata); } @@ -4501,9 +4617,9 @@ if(gzread_pass_status) xc->num_blackouts = fstReaderVarint32(xc->f); free(xc->blackout_times); - xc->blackout_times = calloc(xc->num_blackouts, sizeof(uint64_t)); + xc->blackout_times = (uint64_t *)calloc(xc->num_blackouts, sizeof(uint64_t)); free(xc->blackout_activity); - xc->blackout_activity = calloc(xc->num_blackouts, sizeof(unsigned char)); + xc->blackout_activity = (unsigned char *)calloc(xc->num_blackouts, sizeof(unsigned char)); for(i=0;inum_blackouts;i++) { @@ -4538,7 +4654,7 @@ return(hdr_seen); void *fstReaderOpenForUtilitiesOnly(void) { -struct fstReaderContext *xc = calloc(1, sizeof(struct fstReaderContext)); +struct fstReaderContext *xc = (struct fstReaderContext *)calloc(1, sizeof(struct fstReaderContext)); return(xc); } @@ -4546,7 +4662,7 @@ return(xc); void *fstReaderOpen(const char *nam) { -struct fstReaderContext *xc = calloc(1, sizeof(struct fstReaderContext)); +struct fstReaderContext *xc = (struct fstReaderContext *)calloc(1, sizeof(struct fstReaderContext)); if((!nam)||(!(xc->f=fopen(nam, "rb")))) { @@ -4556,7 +4672,7 @@ if((!nam)||(!(xc->f=fopen(nam, "rb")))) else { int flen = strlen(nam); - char *hf = calloc(1, flen + 6); + char *hf = (char *)calloc(1, flen + 6); int rc; #if defined(__MINGW32__) || defined(FST_MACOSX) @@ -4695,9 +4811,9 @@ uint32_t mc_mem_len; /* corresponds to largest value encountered in chain_table_ if(!xc) return(0); -scatterptr = calloc(xc->maxhandle, sizeof(uint32_t)); -headptr = calloc(xc->maxhandle, sizeof(uint32_t)); -length_remaining = calloc(xc->maxhandle, sizeof(uint32_t)); +scatterptr = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t)); +headptr = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t)); +length_remaining = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t)); if(fv) { @@ -4722,7 +4838,7 @@ for(;;) if((sectype == EOF) || (sectype == FST_BL_SKIP)) { #ifdef FST_DEBUG - fprintf(stderr, "<< EOF >>\n"); + fprintf(stderr, FST_APIMESS"<< EOF >>\n"); #endif break; } @@ -4759,11 +4875,11 @@ for(;;) mem_required_for_traversal = fstReaderUint64(xc->f); - mem_for_traversal = malloc(mem_required_for_traversal + 66); /* add in potential fastlz overhead */ + mem_for_traversal = (unsigned char *)malloc(mem_required_for_traversal + 66); /* add in potential fastlz overhead */ #ifdef FST_DEBUG - fprintf(stderr, "sec: %u seclen: %d begtim: %d endtim: %d\n", + fprintf(stderr, FST_APIMESS"sec: %u seclen: %d begtim: %d endtim: %d\n", secnum, (int)seclen, (int)beg_tim, (int)end_tim); - fprintf(stderr, "\tmem_required_for_traversal: %d\n", (int)mem_required_for_traversal); + fprintf(stderr, FST_APIMESS"mem_required_for_traversal: %d\n", (int)mem_required_for_traversal); #endif /* process time block */ { @@ -4781,11 +4897,11 @@ for(;;) tsec_clen = fstReaderUint64(xc->f); tsec_nitems = fstReaderUint64(xc->f); #ifdef FST_DEBUG - fprintf(stderr, "\ttime section unc: %d, com: %d (%d items)\n", + fprintf(stderr, FST_APIMESS"time section unc: %d, com: %d (%d items)\n", (int)tsec_uclen, (int)tsec_clen, (int)tsec_nitems); #endif if(tsec_clen > seclen) break; /* corrupted tsec_clen: by definition it can't be larger than size of section */ - ucdata = malloc(tsec_uclen); + ucdata = (unsigned char *)malloc(tsec_uclen); if(!ucdata) break; /* malloc fail as tsec_uclen out of range from corrupted file */ destlen = tsec_uclen; sourcelen = tsec_clen; @@ -4794,14 +4910,14 @@ for(;;) if(tsec_uclen != tsec_clen) { - cdata = malloc(tsec_clen); + cdata = (unsigned char *)malloc(tsec_clen); fstFread(cdata, tsec_clen, 1, xc->f); rc = uncompress(ucdata, &destlen, cdata, sourcelen); if(rc != Z_OK) { - printf("tsec uncompress rc = %d\n", rc); + fprintf(stderr, FST_APIMESS"fstReaderIterBlocks2(), tsec uncompress rc = %d, exiting.\n", rc); exit(255); } @@ -4813,7 +4929,7 @@ for(;;) } free(time_table); - time_table = calloc(tsec_nitems, sizeof(uint64_t)); + time_table = (uint64_t *)calloc(tsec_nitems, sizeof(uint64_t)); tpnt = ucdata; tpval = 0; for(ti=0;tinum_blackouts)&&(cur_blackout != xc->num_blackouts)) @@ -4867,7 +4983,7 @@ for(;;) } else { - unsigned char *mc = malloc(frame_clen); + unsigned char *mc = (unsigned char *)malloc(frame_clen); int rc; unsigned long destlen = frame_uclen; @@ -4877,7 +4993,7 @@ for(;;) rc = uncompress(mu, &destlen, mc, sourcelen); if(rc != Z_OK) { - printf("rc: %d\n", rc); + fprintf(stderr, FST_APIMESS"fstReaderIterBlocks2(), frame uncompress rc: %d, exiting.\n", rc); exit(255); } free(mc); @@ -5040,9 +5156,9 @@ for(;;) packtype = fgetc(xc->f); #ifdef FST_DEBUG - fprintf(stderr, "\tframe_uclen: %d, frame_clen: %d, frame_maxhandle: %d\n", + fprintf(stderr, FST_APIMESS"frame_uclen: %d, frame_clen: %d, frame_maxhandle: %d\n", (int)frame_uclen, (int)frame_clen, (int)frame_maxhandle); - fprintf(stderr, "\tvc_maxhandle: %d, packtype: %c\n", (int)vc_maxhandle, packtype); + fprintf(stderr, FST_APIMESS"vc_maxhandle: %d, packtype: %c\n", (int)vc_maxhandle, packtype); #endif indx_pntr = blkpos + seclen - 24 -tsec_clen -8; @@ -5050,9 +5166,9 @@ for(;;) chain_clen = fstReaderUint64(xc->f); indx_pos = indx_pntr - chain_clen; #ifdef FST_DEBUG - fprintf(stderr, "\tindx_pos: %d (%d bytes)\n", (int)indx_pos, (int)chain_clen); + fprintf(stderr, FST_APIMESS"indx_pos: %d (%d bytes)\n", (int)indx_pos, (int)chain_clen); #endif - chain_cmem = malloc(chain_clen); + chain_cmem = (unsigned char *)malloc(chain_clen); if(!chain_cmem) goto block_err; fstReaderFseeko(xc, xc->f, indx_pos, SEEK_SET); fstFread(chain_cmem, chain_clen, 1, xc->f); @@ -5063,8 +5179,8 @@ for(;;) free(chain_table_lengths); vc_maxhandle_largest = vc_maxhandle; - chain_table = calloc((vc_maxhandle+1), sizeof(off_t)); - chain_table_lengths = calloc((vc_maxhandle+1), sizeof(uint32_t)); + chain_table = (off_t *)calloc((vc_maxhandle+1), sizeof(off_t)); + chain_table_lengths = (uint32_t *)calloc((vc_maxhandle+1), sizeof(uint32_t)); } if(!chain_table || !chain_table_lengths) goto block_err; @@ -5169,11 +5285,11 @@ for(;;) } #ifdef FST_DEBUG - fprintf(stderr, "\tdecompressed chain idx len: %"PRIu32"\n", idx); + fprintf(stderr, FST_APIMESS"decompressed chain idx len: %" PRIu32 "\n", idx); #endif mc_mem_len = 16384; - mc_mem = malloc(mc_mem_len); /* buffer for compressed reads */ + mc_mem = (unsigned char *)malloc(mc_mem_len); /* buffer for compressed reads */ /* check compressed VC data */ if(idx > xc->maxhandle) idx = xc->maxhandle; @@ -5203,7 +5319,7 @@ for(;;) if(mc_mem_len < chain_table_lengths[i]) { free(mc_mem); - mc_mem = malloc(mc_mem_len = chain_table_lengths[i]); + mc_mem = (unsigned char *)malloc(mc_mem_len = chain_table_lengths[i]); } mc = mc_mem; @@ -5237,7 +5353,7 @@ for(;;) if(rc != Z_OK) { - printf("\tfac: %d clen: %d (rc=%d)\n", (int)i, (int)val, rc); + fprintf(stderr, FST_APIMESS"fstReaderIterBlocks2(), fac: %d clen: %d (rc=%d), exiting.\n", (int)i, (int)val, rc); exit(255); } @@ -5282,7 +5398,7 @@ for(;;) } } - wx_len = sprintf(wx_buf, "#%"PRIu64"\n", time_table[i]); + wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", time_table[i]); fstWritex(xc, wx_buf, wx_len); if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts)) @@ -5382,7 +5498,7 @@ for(;;) vcdid_len = fstVcdIDForFwrite(vcd_id+1, idx+1); { - unsigned char *vesc = malloc(len*4 + 1); + unsigned char *vesc = (unsigned char *)malloc(len*4 + 1); int vlen = fstUtilityBinToEsc(vesc, vdata, len); fstWritex(xc, vesc, vlen); free(vesc); @@ -5709,7 +5825,7 @@ if(!xc->rvat_sig_offs) { uint32_t cur_offs = 0; - xc->rvat_sig_offs = calloc(xc->maxhandle, sizeof(uint32_t)); + xc->rvat_sig_offs = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t)); for(i=0;imaxhandle;i++) { xc->rvat_sig_offs[i] = cur_offs; @@ -5789,9 +5905,9 @@ mem_required_for_traversal = fstReaderUint64(xc->f); #ifdef FST_DEBUG -fprintf(stderr, "rvat sec: %u seclen: %d begtim: %d endtim: %d\n", +fprintf(stderr, FST_APIMESS"rvat sec: %u seclen: %d begtim: %d endtim: %d\n", secnum, (int)seclen, (int)beg_tim, (int)end_tim); -fprintf(stderr, "\tmem_required_for_traversal: %d\n", (int)mem_required_for_traversal); +fprintf(stderr, FST_APIMESS"mem_required_for_traversal: %d\n", (int)mem_required_for_traversal); #endif /* process time block */ @@ -5810,24 +5926,24 @@ tsec_uclen = fstReaderUint64(xc->f); tsec_clen = fstReaderUint64(xc->f); tsec_nitems = fstReaderUint64(xc->f); #ifdef FST_DEBUG -fprintf(stderr, "\ttime section unc: %d, com: %d (%d items)\n", +fprintf(stderr, FST_APIMESS"time section unc: %d, com: %d (%d items)\n", (int)tsec_uclen, (int)tsec_clen, (int)tsec_nitems); #endif -ucdata = malloc(tsec_uclen); +ucdata = (unsigned char *)malloc(tsec_uclen); destlen = tsec_uclen; sourcelen = tsec_clen; fstReaderFseeko(xc, xc->f, -24 - ((off_t)tsec_clen), SEEK_CUR); if(tsec_uclen != tsec_clen) { - cdata = malloc(tsec_clen); + cdata = (unsigned char *)malloc(tsec_clen); fstFread(cdata, tsec_clen, 1, xc->f); rc = uncompress(ucdata, &destlen, cdata, sourcelen); if(rc != Z_OK) { - printf("tsec uncompress rc = %d\n", rc); + fprintf(stderr, FST_APIMESS"fstReaderGetValueFromHandleAtTime(), tsec uncompress rc = %d, exiting.\n", rc); exit(255); } @@ -5838,7 +5954,7 @@ if(tsec_uclen != tsec_clen) fstFread(ucdata, tsec_uclen, 1, xc->f); } -xc->rvat_time_table = calloc(tsec_nitems, sizeof(uint64_t)); +xc->rvat_time_table = (uint64_t *)calloc(tsec_nitems, sizeof(uint64_t)); tpnt = ucdata; tpval = 0; for(ti=0;tif, blkpos+32, SEEK_SET); frame_uclen = fstReaderVarint64(xc->f); frame_clen = fstReaderVarint64(xc->f); xc->rvat_frame_maxhandle = fstReaderVarint64(xc->f); -xc->rvat_frame_data = malloc(frame_uclen); +xc->rvat_frame_data = (unsigned char *)malloc(frame_uclen); if(frame_uclen == frame_clen) { @@ -5865,7 +5981,7 @@ if(frame_uclen == frame_clen) } else { - unsigned char *mc = malloc(frame_clen); + unsigned char *mc = (unsigned char *)malloc(frame_clen); int rc; unsigned long destlen = frame_uclen; @@ -5875,7 +5991,7 @@ if(frame_uclen == frame_clen) rc = uncompress(xc->rvat_frame_data, &destlen, mc, sourcelen); if(rc != Z_OK) { - printf("decompress rc: %d\n", rc); + fprintf(stderr, FST_APIMESS"fstReaderGetValueFromHandleAtTime(), frame decompress rc: %d, exiting.\n", rc); exit(255); } free(mc); @@ -5883,11 +5999,12 @@ 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", +fprintf(stderr, FST_APIMESS"frame_uclen: %d, frame_clen: %d, frame_maxhandle: %d\n", (int)frame_uclen, (int)frame_clen, (int)xc->rvat_frame_maxhandle); -fprintf(stderr, "\tvc_maxhandle: %d\n", (int)xc->rvat_vc_maxhandle); +fprintf(stderr, FST_APIMESS"vc_maxhandle: %d\n", (int)xc->rvat_vc_maxhandle); #endif indx_pntr = blkpos + seclen - 24 -tsec_clen -8; @@ -5895,49 +6012,96 @@ fstReaderFseeko(xc, xc->f, indx_pntr, SEEK_SET); chain_clen = fstReaderUint64(xc->f); indx_pos = indx_pntr - chain_clen; #ifdef FST_DEBUG -fprintf(stderr, "\tindx_pos: %d (%d bytes)\n", (int)indx_pos, (int)chain_clen); +fprintf(stderr, FST_APIMESS"indx_pos: %d (%d bytes)\n", (int)indx_pos, (int)chain_clen); #endif -chain_cmem = malloc(chain_clen); +chain_cmem = (unsigned char *)malloc(chain_clen); fstReaderFseeko(xc, xc->f, indx_pos, SEEK_SET); fstFread(chain_cmem, chain_clen, 1, xc->f); -xc->rvat_chain_table = calloc((xc->rvat_vc_maxhandle+1), sizeof(off_t)); -xc->rvat_chain_table_lengths = calloc((xc->rvat_vc_maxhandle+1), sizeof(uint32_t)); +xc->rvat_chain_table = (off_t *)calloc((xc->rvat_vc_maxhandle+1), sizeof(off_t)); +xc->rvat_chain_table_lengths = (uint32_t *)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); + uint32_t prev_alias = 0; - 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++; + } } - } + else + { + uint64_t val = fstGetVarint32(pnt, &skiplen); - pnt += skiplen; - } while (pnt != (chain_cmem + chain_clen)); + 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; @@ -5959,7 +6123,7 @@ for(i=0;irvat_data_valid = 1; @@ -5997,19 +6161,29 @@ if(!xc->rvat_chain_mem) xc->rvat_chain_len = fstReaderVarint32WithSkip(xc->f, &skiplen); if(xc->rvat_chain_len) { - unsigned char *mu = malloc(xc->rvat_chain_len); - unsigned char *mc = malloc(xc->rvat_chain_table_lengths[facidx]); + unsigned char *mu = (unsigned char *)malloc(xc->rvat_chain_len); + unsigned char *mc = (unsigned char *)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) { - printf("\tclen: %d (rc=%d)\n", (int)xc->rvat_chain_len, rc); + fprintf(stderr, FST_APIMESS"fstReaderGetValueFromHandleAtTime(), rvat decompress clen: %d (rc=%d), exiting.\n", (int)xc->rvat_chain_len, rc); exit(255); } @@ -6019,7 +6193,7 @@ if(!xc->rvat_chain_mem) else { int destlen = xc->rvat_chain_table_lengths[facidx] - skiplen; - unsigned char *mu = malloc(xc->rvat_chain_len = destlen); + unsigned char *mu = (unsigned char *)malloc(xc->rvat_chain_len = destlen); fstFread(mu, destlen, 1, xc->f); /* data to process is for(j=0;jrvat_chain_mem = mu; @@ -6320,17 +6494,17 @@ static uint32_t j_hash(const uint8_t *k, uint32_t length, uint32_t initval) c += length; switch(len) /* all the case statements fall through */ { - case 11: c+=((uint32_t)k[10]<<24); - case 10: c+=((uint32_t)k[9]<<16); - case 9 : c+=((uint32_t)k[8]<<8); + case 11: c+=((uint32_t)k[10]<<24); /* fallthrough */ + case 10: c+=((uint32_t)k[9]<<16); /* fallthrough */ + case 9 : c+=((uint32_t)k[8]<<8); /* fallthrough */ /* the first byte of c is reserved for the length */ - case 8 : b+=((uint32_t)k[7]<<24); - case 7 : b+=((uint32_t)k[6]<<16); - case 6 : b+=((uint32_t)k[5]<<8); - case 5 : b+=k[4]; - case 4 : a+=((uint32_t)k[3]<<24); - case 3 : a+=((uint32_t)k[2]<<16); - case 2 : a+=((uint32_t)k[1]<<8); + case 8 : b+=((uint32_t)k[7]<<24); /* fallthrough */ + case 7 : b+=((uint32_t)k[6]<<16); /* fallthrough */ + case 6 : b+=((uint32_t)k[5]<<8); /* fallthrough */ + case 5 : b+=k[4]; /* fallthrough */ + case 4 : a+=((uint32_t)k[3]<<24); /* fallthrough */ + case 3 : a+=((uint32_t)k[2]<<16); /* fallthrough */ + case 2 : a+=((uint32_t)k[1]<<8); /* fallthrough */ case 1 : a+=k[0]; /* case 0: nothing left to add */ } @@ -6365,7 +6539,7 @@ struct collchain_t *chain, *pchain; if(!*base) { - *base = calloc(1, (hashmask + 1) * sizeof(void *)); + *base = (struct collchain_t **)calloc(1, (hashmask + 1) * sizeof(void *)); } ar = *base; @@ -6388,7 +6562,7 @@ while(chain) chain = chain->next; } -chain = calloc(1, sizeof(struct collchain_t) + length - 1); +chain = (struct collchain_t *)calloc(1, sizeof(struct collchain_t) + length - 1); memcpy(chain->mem, mem, length); chain->fullhash = hf; chain->length = length; @@ -6434,9 +6608,46 @@ if(base && *base) /*** ***/ /************************/ -int fstUtilityBinToEsc(unsigned char *d, unsigned char *s, int len) +int fstUtilityBinToEscConvertedLen(const unsigned char *s, int len) { -unsigned char *src = s; +const unsigned char *src = s; +int dlen = 0; +int i; + +for(i=0;i ' ') && (src[i] <= '~')) /* no white spaces in output */ + { + dlen++; + } + else + { + dlen += 4; + } + break; + } + } + +return(dlen); +} + + +int fstUtilityBinToEsc(unsigned char *d, const unsigned char *s, int len) +{ +const unsigned char *src = s; unsigned char *dst = d; unsigned char val; int i; @@ -6535,3 +6746,76 @@ for(i=0;ielem_count = cnt; + et->name = strdup(s); + et->literal_arr = (char**)calloc(cnt, sizeof(char *)); + et->val_arr = (char**)calloc(cnt, sizeof(char *)); + + sp = strchr(et->name, ' '); + *sp = 0; + + sp = strchr(sp+1, ' '); + + for(i=0;iliteral_arr[i] = sp+1; + sp = sp2; + + newlen = fstUtilityEscToBin(NULL, (unsigned char*)et->literal_arr[i], strlen(et->literal_arr[i])); + et->literal_arr[i][newlen] = 0; + } + + for(i=0;ival_arr[i] = sp+1; + sp = sp2; + + newlen = fstUtilityEscToBin(NULL, (unsigned char*)et->val_arr[i], strlen(et->val_arr[i])); + et->val_arr[i][newlen] = 0; + } + } + } + +return(et); +} + + +void fstUtilityFreeEnumTable(struct fstETab *etab) +{ +if(etab) + { + free(etab->literal_arr); + free(etab->val_arr); + free(etab->name); + free(etab); + } +} diff --git a/vpi/fstapi.h b/vpi/fstapi.h index df86ef079..aef6de23b 100644 --- a/vpi/fstapi.h +++ b/vpi/fstapi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2015 Tony Bybell. + * Copyright (c) 2009-2018 Tony Bybell. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -39,6 +39,7 @@ extern "C" { #define FST_RDLOAD "FSTLOAD | " typedef uint32_t fstHandle; +typedef uint32_t fstEnumHandle; enum fstWriterPackType { FST_WR_PT_ZLIB = 0, @@ -168,7 +169,11 @@ enum fstHierType { FST_HT_ATTRBEGIN = 3, FST_HT_ATTREND = 4, - FST_HT_MAX = 4 + /* FST_HT_TREEBEGIN and FST_HT_TREEEND are not yet used by FST but are currently used when fstHier bridges other formats */ + FST_HT_TREEBEGIN = 5, + FST_HT_TREEEND = 6, + + FST_HT_MAX = 6 }; enum fstAttrType { @@ -192,9 +197,10 @@ enum fstMiscType { FST_MT_SOURCESTEM = 4, /* use fstWriterSetSourceStem() to emit */ FST_MT_SOURCEISTEM = 5, /* use fstWriterSetSourceInstantiationStem() to emit */ FST_MT_VALUELIST = 6, /* use fstWriterSetValueList() to emit, followed by fstWriterCreateVar*() */ - FST_MT_UNKNOWN = 7, + FST_MT_ENUMTABLE = 7, /* use fstWriterCreateEnumTable() and fstWriterEmitEnumTableRef() to emit */ + FST_MT_UNKNOWN = 8, - FST_MT_MAX = 7 + FST_MT_MAX = 8 }; enum fstArrayType { @@ -224,7 +230,10 @@ enum fstEnumValueType { FST_EV_SV_UNSIGNED_LONGINT = 12, FST_EV_SV_UNSIGNED_BYTE = 13, - FST_EV_MAX = 13 + FST_EV_REG = 14, + FST_EV_TIME = 15, + + FST_EV_MAX = 15 }; enum fstPackType { @@ -320,11 +329,21 @@ union { }; +struct fstETab +{ +char *name; +uint32_t elem_count; +char **literal_arr; +char **val_arr; +}; + + /* * writer functions */ void fstWriterClose(void *ctx); void * fstWriterCreate(const char *nam, int use_compressed_hier); +fstEnumHandle fstWriterCreateEnumTable(void *ctx, const char *name, uint32_t elem_count, unsigned int min_valbits, const char **literal_arr, const char **val_arr); /* used for Verilog/SV */ fstHandle fstWriterCreateVar(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam, fstHandle aliasHandle); @@ -333,9 +352,10 @@ fstHandle fstWriterCreateVar(void *ctx, enum fstVarType vt, enum fstVarDir fstHandle fstWriterCreateVar2(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam, fstHandle aliasHandle, const char *type, enum fstSupplementalVarType svt, enum fstSupplementalDataType sdt); +void fstWriterEmitDumpActive(void *ctx, int enable); +void fstWriterEmitEnumTableRef(void *ctx, fstEnumHandle handle); void fstWriterEmitValueChange(void *ctx, fstHandle handle, const void *val); void fstWriterEmitVariableLengthValueChange(void *ctx, fstHandle handle, const void *val, uint32_t len); -void fstWriterEmitDumpActive(void *ctx, int enable); void fstWriterEmitTimeChange(void *ctx, uint64_t tim); void fstWriterFlushContext(void *ctx); int fstWriterGetDumpSizeLimitReached(void *ctx); @@ -418,8 +438,12 @@ void fstReaderSetVcdExtensions(void *ctx, int enable); /* * utility functions */ -int fstUtilityBinToEsc(unsigned char *d, unsigned char *s, int len); +int fstUtilityBinToEscConvertedLen(const unsigned char *s, int len); /* used for mallocs for fstUtilityBinToEsc() */ +int fstUtilityBinToEsc(unsigned char *d, const unsigned char *s, int len); int fstUtilityEscToBin(unsigned char *d, unsigned char *s, int len); +struct fstETab *fstUtilityExtractEnumTableFromString(const char *s); +void fstUtilityFreeEnumTable(struct fstETab *etab); /* must use to free fstETab properly */ + #ifdef __cplusplus } diff --git a/vpi/lxt2_write.c b/vpi/lxt2_write.c index b50aa2359..d51ad73a2 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"), @@ -97,7 +97,7 @@ if (lonumfacs)) { struct lxt2_wr_symbol *s = lt->symchain; - struct lxt2_wr_symbol **aliascache = calloc(lt->numalias ? lt->numalias : 1, sizeof(struct lxt2_wr_symbol *)); + struct lxt2_wr_symbol **aliascache = (struct lxt2_wr_symbol**)calloc(lt->numalias ? lt->numalias : 1, sizeof(struct lxt2_wr_symbol *)); unsigned int aliases_encountered, facs_encountered; lt->sorted_facs = (struct lxt2_wr_symbol **)calloc(lt->numfacs, sizeof(struct lxt2_wr_symbol *)); @@ -920,7 +920,7 @@ if(flags&LXT2_WR_SYM_F_DOUBLE) s->len = 32; } - s->value = malloc(s->len + 1); + s->value = (char*)malloc(s->len + 1); memset(s->value, lt->initial_value, s->len); s->value[s->len]=0; @@ -1017,7 +1017,7 @@ static void lxt2_wr_emit_do_breakfile(struct lxt2_wr_trace *lt) { unsigned int len = strlen(lt->lxtname); int i; -char *tname = malloc(len + 30); +char *tname = (char*)malloc(len + 30); FILE *f2, *clone; off_t cnt, seg; char buf[32768]; @@ -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"); @@ -1810,13 +1810,13 @@ while(s->aliased_to) /* find root alias if exists */ valuelen = strlen(value); /* ensure string is proper length */ if(valuelen == s->len) { - vfix = wave_alloca(s->len+1); + vfix = (char*)wave_alloca(s->len+1); strcpy(vfix, value); value = vfix; } else { - vfix = wave_alloca(s->len+1); + vfix = (char*)wave_alloca(s->len+1); if(valuelen < s->len) { @@ -2088,7 +2088,7 @@ if((lt)&&(lt->blackout)) else { free(s->value); - s->value = calloc(1, 1*sizeof(char)); + s->value = (char*)calloc(1, 1*sizeof(char)); } } } @@ -2101,9 +2101,11 @@ if((lt)&&(lt->blackout)) { if((!(s->flags&LXT2_WR_SYM_F_ALIAS))&&(s->rows<2)) { + char tmp[16]; /* To get rid of the warning */ if(!(s->flags&(LXT2_WR_SYM_F_DOUBLE|LXT2_WR_SYM_F_STRING))) { - lxt2_wr_emit_value_bit_string(lt, s, 0, "x"); + strcpy(tmp, "x"); + lxt2_wr_emit_value_bit_string(lt, s, 0, tmp); } else if (s->flags&LXT2_WR_SYM_F_DOUBLE) { @@ -2113,7 +2115,8 @@ if((lt)&&(lt->blackout)) } else if (s->flags&LXT2_WR_SYM_F_STRING) { - lxt2_wr_emit_value_string(lt, s, 0, "UNDEF"); + strcpy(tmp, "UNDEF"); + lxt2_wr_emit_value_string(lt, s, 0, tmp); } } s=s->symchain; @@ -2202,4 +2205,3 @@ if(lt) lt->timezero = timeval; } } - diff --git a/vpi/lxt2_write.h b/vpi/lxt2_write.h index 7f2c6d134..1a8a1adbb 100644 --- a/vpi/lxt2_write.h +++ b/vpi/lxt2_write.h @@ -43,7 +43,7 @@ extern "C" { #define ftello ftell #endif -#include +#include "wavealloca.h" #define LXT2_WR_HDRID (0x1380) #define LXT2_WR_VERSION (0x0001) @@ -314,4 +314,3 @@ int lxt2_wr_emit_value_bit_string(struct lxt2_wr_trace *lt, struct lxt2_wr_sy #endif #endif - diff --git a/vpi/lz4.c b/vpi/lz4.c index ed928ced3..5ee034eea 100644 --- a/vpi/lz4.c +++ b/vpi/lz4.c @@ -1,6 +1,7 @@ /* LZ4 - Fast LZ compression algorithm Copyright (C) 2011-2015, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without @@ -27,14 +28,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - - LZ4 source repository : http://code.google.com/p/lz4 - - LZ4 source mirror : https://github.com/Cyan4973/lz4 + - LZ4 source repository : https://github.com/Cyan4973/lz4 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c */ /************************************** - Tuning parameters +* Tuning parameters **************************************/ /* * HEAPMODE : @@ -44,50 +44,15 @@ #define HEAPMODE 0 /* - * CPU_HAS_EFFICIENT_UNALIGNED_MEMORY_ACCESS : - * By default, the source code expects the compiler to correctly optimize - * 4-bytes and 8-bytes read on architectures able to handle it efficiently. - * This is not always the case. In some circumstances (ARM notably), - * the compiler will issue cautious code even when target is able to correctly handle unaligned memory accesses. - * - * You can force the compiler to use unaligned memory access by uncommenting the line below. - * One of the below scenarios will happen : - * 1 - Your target CPU correctly handle unaligned access, and was not well optimized by compiler (good case). - * You will witness large performance improvements (+50% and up). - * Keep the line uncommented and send a word to upstream (https://groups.google.com/forum/#!forum/lz4c) - * The goal is to automatically detect such situations by adding your target CPU within an exception list. - * 2 - Your target CPU correctly handle unaligned access, and was already already optimized by compiler - * No change will be experienced. - * 3 - Your target CPU inefficiently handle unaligned access. - * You will experience a performance loss. Comment back the line. - * 4 - Your target CPU does not handle unaligned access. - * Program will crash. - * If uncommenting results in better performance (case 1) - * please report your configuration to upstream (https://groups.google.com/forum/#!forum/lz4c) - * An automatic detection macro will be added to match your case within future versions of the library. + * ACCELERATION_DEFAULT : + * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 */ -/* #define CPU_HAS_EFFICIENT_UNALIGNED_MEMORY_ACCESS 1 */ +#define ACCELERATION_DEFAULT 1 /************************************** - CPU Feature Detection +* CPU Feature Detection **************************************/ -/* - * Automated efficient unaligned memory access detection - * Based on known hardware architectures - * This list will be updated thanks to feedbacks - */ -#if defined(CPU_HAS_EFFICIENT_UNALIGNED_MEMORY_ACCESS) \ - || defined(__ARM_FEATURE_UNALIGNED) \ - || defined(__i386__) || defined(__x86_64__) \ - || defined(_M_IX86) || defined(_M_X64) \ - || defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_8__) \ - || (defined(_M_ARM) && (_M_ARM >= 7)) -# define LZ4_UNALIGNED_ACCESS 1 -#else -# define LZ4_UNALIGNED_ACCESS 0 -#endif - /* * LZ4_FORCE_SW_BITCOUNT * Define this parameter if your target system or compiler does not support hardware bit count @@ -98,14 +63,14 @@ /************************************** - Compiler Options +* Includes **************************************/ -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ -/* "restrict" is a known keyword */ -#else -# define restrict /* Disable restrict */ -#endif +#include "lz4.h" + +/************************************** +* Compiler Options +**************************************/ #ifdef _MSC_VER /* Visual Studio */ # define FORCE_INLINE static __forceinline # include @@ -113,7 +78,7 @@ # pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */ #else # if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ -# ifdef __GNUC__ +# if defined(__GNUC__) || defined(__clang__) # define FORCE_INLINE static inline __attribute__((always_inline)) # else # define FORCE_INLINE static inline @@ -123,9 +88,8 @@ # endif /* __STDC_VERSION__ */ #endif /* _MSC_VER */ -#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) - -#if (GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__) +/* LZ4_GCC_VERSION is defined into lz4.h */ +#if (LZ4_GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__) # define expect(expr,value) (__builtin_expect ((expr),(value)) ) #else # define expect(expr,value) (expr) @@ -136,7 +100,7 @@ /************************************** - Memory routines +* Memory routines **************************************/ #include /* malloc, calloc, free */ #define ALLOCATOR(n,s) calloc(n,s) @@ -146,13 +110,7 @@ /************************************** - Includes -**************************************/ -#include "lz4.h" - - -/************************************** - Basic Types +* Basic Types **************************************/ #if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ # include @@ -171,7 +129,7 @@ /************************************** - Reading and writing into memory +* Reading and writing into memory **************************************/ #define STEPSIZE sizeof(size_t) @@ -184,67 +142,52 @@ static unsigned LZ4_isLittleEndian(void) } +static U16 LZ4_read16(const void* memPtr) +{ + U16 val16; + memcpy(&val16, memPtr, 2); + return val16; +} + static U16 LZ4_readLE16(const void* memPtr) { - if ((LZ4_UNALIGNED_ACCESS) && (LZ4_isLittleEndian())) - return *(U16*)memPtr; + if (LZ4_isLittleEndian()) + { + return LZ4_read16(memPtr); + } else { - const BYTE* p = memPtr; + const BYTE* p = (const BYTE*)memPtr; return (U16)((U16)p[0] + (p[1]<<8)); } } static void LZ4_writeLE16(void* memPtr, U16 value) { - if ((LZ4_UNALIGNED_ACCESS) && (LZ4_isLittleEndian())) + if (LZ4_isLittleEndian()) { - *(U16*)memPtr = value; - return; + memcpy(memPtr, &value, 2); } else { - BYTE* p = memPtr; + BYTE* p = (BYTE*)memPtr; p[0] = (BYTE) value; p[1] = (BYTE)(value>>8); } } - -static U16 LZ4_read16(const void* memPtr) -{ - if (LZ4_UNALIGNED_ACCESS) - return *(U16*)memPtr; - else - { - U16 val16; - memcpy(&val16, memPtr, 2); - return val16; - } -} - static U32 LZ4_read32(const void* memPtr) { - if (LZ4_UNALIGNED_ACCESS) - return *(U32*)memPtr; - else - { - U32 val32; - memcpy(&val32, memPtr, 4); - return val32; - } + U32 val32; + memcpy(&val32, memPtr, 4); + return val32; } static U64 LZ4_read64(const void* memPtr) { - if (LZ4_UNALIGNED_ACCESS) - return *(U64*)memPtr; - else - { - U64 val64; - memcpy(&val64, memPtr, 8); - return val64; - } + U64 val64; + memcpy(&val64, memPtr, 8); + return val64; } static size_t LZ4_read_ARCH(const void* p) @@ -256,44 +199,22 @@ static size_t LZ4_read_ARCH(const void* p) } -static void LZ4_copy4(void* dstPtr, const void* srcPtr) -{ - if (LZ4_UNALIGNED_ACCESS) - { - *(U32*)dstPtr = *(U32*)srcPtr; - return; - } - memcpy(dstPtr, srcPtr, 4); -} +static void LZ4_copy4(void* dstPtr, const void* srcPtr) { memcpy(dstPtr, srcPtr, 4); } -static void LZ4_copy8(void* dstPtr, const void* srcPtr) -{ -#if GCC_VERSION!=409 /* disabled on GCC 4.9, as it generates invalid opcode (crash) */ - if (LZ4_UNALIGNED_ACCESS) - { - if (LZ4_64bits()) - *(U64*)dstPtr = *(U64*)srcPtr; - else - ((U32*)dstPtr)[0] = ((U32*)srcPtr)[0], - ((U32*)dstPtr)[1] = ((U32*)srcPtr)[1]; - return; - } -#endif - memcpy(dstPtr, srcPtr, 8); -} +static void LZ4_copy8(void* dstPtr, const void* srcPtr) { memcpy(dstPtr, srcPtr, 8); } /* customized version of memcpy, which may overwrite up to 7 bytes beyond dstEnd */ static void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd) { - BYTE* d = dstPtr; - const BYTE* s = srcPtr; - BYTE* e = dstEnd; + BYTE* d = (BYTE*)dstPtr; + const BYTE* s = (const BYTE*)srcPtr; + BYTE* e = (BYTE*)dstEnd; do { LZ4_copy8(d,s); d+=8; s+=8; } while (d>3); -# elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) +# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_ctzll((U64)val) >> 3); # else static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; @@ -347,7 +271,7 @@ static unsigned LZ4_NbCommonBytes (register size_t val) unsigned long r; _BitScanForward( &r, (U32)val ); return (int)(r>>3); -# elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) +# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_ctz((U32)val) >> 3); # else static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; @@ -363,8 +287,8 @@ static unsigned LZ4_NbCommonBytes (register size_t val) unsigned long r = 0; _BitScanReverse64( &r, val ); return (unsigned)(r>>3); -# elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) - return (__builtin_clzll(val) >> 3); +# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_clzll((U64)val) >> 3); # else unsigned r; if (!(val>>32)) { r=4; } else { r=0; val>>=32; } @@ -379,8 +303,8 @@ static unsigned LZ4_NbCommonBytes (register size_t val) unsigned long r = 0; _BitScanReverse( &r, (unsigned long)val ); return (unsigned)(r>>3); -# elif defined(__GNUC__) && (GCC_VERSION >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT) - return (__builtin_clz(val) >> 3); +# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_clz((U32)val) >> 3); # else unsigned r; if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } @@ -412,7 +336,7 @@ static unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLi #ifndef LZ4_COMMONDEFS_ONLY /************************************** - Local Constants +* Local Constants **************************************/ #define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) #define HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) @@ -423,21 +347,14 @@ static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression ru /************************************** - Local Utils -**************************************/ -int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } -int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } - - -/************************************** - Local Structures and types +* Local Structures and types **************************************/ typedef struct { U32 hashTable[HASH_SIZE_U32]; U32 currentOffset; U32 initCheck; const BYTE* dictionary; - const BYTE* bufferStart; + BYTE* bufferStart; /* obsolete, used for slideInputBuffer */ U32 dictSize; } LZ4_stream_t_internal; @@ -451,12 +368,20 @@ typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; typedef enum { full = 0, partial = 1 } earlyEnd_directive; +/************************************** +* Local Utils +**************************************/ +int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } +int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } +int LZ4_sizeofState() { return LZ4_STREAMSIZE; } + + /******************************** - Compression functions +* Compression functions ********************************/ -static U32 LZ4_hashSequence(U32 sequence, tableType_t tableType) +static U32 LZ4_hashSequence(U32 sequence, tableType_t const tableType) { if (tableType == byU16) return (((sequence) * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); @@ -464,9 +389,24 @@ static U32 LZ4_hashSequence(U32 sequence, tableType_t tableType) return (((sequence) * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); } -static U32 LZ4_hashPosition(const BYTE* p, tableType_t tableType) { return LZ4_hashSequence(LZ4_read32(p), tableType); } +static const U64 prime5bytes = 889523592379ULL; +static U32 LZ4_hashSequence64(size_t sequence, tableType_t const tableType) +{ + const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; + const U32 hashMask = (1<> (40 - hashLog)) & hashMask; +} -static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase) +static U32 LZ4_hashSequenceT(size_t sequence, tableType_t const tableType) +{ + if (LZ4_64bits()) + return LZ4_hashSequence64(sequence, tableType); + return LZ4_hashSequence((U32)sequence, tableType); +} + +static U32 LZ4_hashPosition(const void* p, tableType_t tableType) { return LZ4_hashSequenceT(LZ4_read_ARCH(p), tableType); } + +static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase) { switch (tableType) { @@ -495,16 +435,17 @@ static const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableType_t t return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); } -static int LZ4_compress_generic( - void* ctx, - const char* source, - char* dest, - int inputSize, - int maxOutputSize, - limitedOutput_directive outputLimited, - tableType_t tableType, - dict_directive dict, - dictIssue_directive dictIssue) +FORCE_INLINE int LZ4_compress_generic( + void* const ctx, + const char* const source, + char* const dest, + const int inputSize, + const int maxOutputSize, + const limitedOutput_directive outputLimited, + const tableType_t tableType, + const dict_directive dict, + const dictIssue_directive dictIssue, + const U32 acceleration) { LZ4_stream_t_internal* const dictPtr = (LZ4_stream_t_internal*)ctx; @@ -527,7 +468,7 @@ static int LZ4_compress_generic( size_t refDelta=0; /* Init conditions */ - if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */ + if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */ switch(dict) { case noDict: @@ -558,15 +499,15 @@ static int LZ4_compress_generic( BYTE* token; { const BYTE* forwardIp = ip; - unsigned step=1; - unsigned searchMatchNb = (1U << LZ4_skipTrigger); + unsigned step = 1; + unsigned searchMatchNb = acceleration << LZ4_skipTrigger; /* Find a match */ do { U32 h = forwardH; ip = forwardIp; forwardIp += step; - step = searchMatchNb++ >> LZ4_skipTrigger; + step = (searchMatchNb++ >> LZ4_skipTrigger); if (unlikely(forwardIp > mflimit)) goto _last_literals; @@ -693,13 +634,22 @@ _next_match: _last_literals: /* Encode Last Literals */ { - int lastRun = (int)(iend - anchor); - if ((outputLimited) && (((char*)op - dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) + const size_t lastRun = (size_t)(iend - anchor); + if ((outputLimited) && ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) return 0; /* Check output limit */ - if (lastRun>=(int)RUN_MASK) { *op++=(RUN_MASK<= 255 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; } - else *op++ = (BYTE)(lastRun<= RUN_MASK) + { + size_t accumulator = lastRun - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; + *op++ = (BYTE) accumulator; + } + else + { + *op++ = (BYTE)(lastRun<= LZ4_compressBound(inputSize)) + { + if (inputSize < LZ4_64Klimit) + return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration); + else + return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); + } + else + { + if (inputSize < LZ4_64Klimit) + return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); + else + return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); + } +} + + +int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { #if (HEAPMODE) - void* ctx = ALLOCATOR(LZ4_STREAMSIZE_U64, 8); /* Aligned on 8-bytes boundaries */ + void* ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ #else - U64 ctx[LZ4_STREAMSIZE_U64] = {0}; /* Ensure data is aligned on 8-bytes boundaries */ + LZ4_stream_t ctx; + void* ctxPtr = &ctx; #endif - int result; - if (inputSize < LZ4_64Klimit) - result = LZ4_compress_generic((void*)ctx, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue); - else - result = LZ4_compress_generic((void*)ctx, source, dest, inputSize, 0, notLimited, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue); + int result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); #if (HEAPMODE) - FREEMEM(ctx); + FREEMEM(ctxPtr); #endif return result; } -int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) + +int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize) { -#if (HEAPMODE) - void* ctx = ALLOCATOR(LZ4_STREAMSIZE_U64, 8); /* Aligned on 8-bytes boundaries */ -#else - U64 ctx[LZ4_STREAMSIZE_U64] = {0}; /* Ensure data is aligned on 8-bytes boundaries */ -#endif - int result; + return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1); +} + + +/* hidden debug function */ +/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */ +int LZ4_compress_fast_force(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ + LZ4_stream_t ctx; + + LZ4_resetStream(&ctx); if (inputSize < LZ4_64Klimit) - result = LZ4_compress_generic((void*)ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue); + return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); else - result = LZ4_compress_generic((void*)ctx, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue); + return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); +} + + +/******************************** +* destSize variant +********************************/ + +static int LZ4_compress_destSize_generic( + void* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + const int targetDstSize, + const tableType_t tableType) +{ + const BYTE* ip = (const BYTE*) src; + const BYTE* base = (const BYTE*) src; + const BYTE* lowLimit = (const BYTE*) src; + const BYTE* anchor = ip; + const BYTE* const iend = ip + *srcSizePtr; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = iend - LASTLITERALS; + + BYTE* op = (BYTE*) dst; + BYTE* const oend = op + targetDstSize; + BYTE* const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */; + BYTE* const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */); + BYTE* const oMaxSeq = oMaxLit - 1 /* token */; + + U32 forwardH; + + + /* Init conditions */ + if (targetDstSize < 1) return 0; /* Impossible to store anything */ + if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */ + if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */ + if (*srcSizePtr> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimit)) + goto _last_literals; + + match = LZ4_getPositionOnHash(h, ctx, tableType, base); + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putPositionOnHash(ip, h, ctx, tableType, base); + + } while ( ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip)) + || (LZ4_read32(match) != LZ4_read32(ip)) ); + } + + /* Catch up */ + while ((ip>anchor) && (match > lowLimit) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } + + { + /* Encode Literal length */ + unsigned litLength = (unsigned)(ip - anchor); + token = op++; + if (op + ((litLength+240)/255) + litLength > oMaxLit) + { + /* Not enough space for a last match */ + op--; + goto _last_literals; + } + if (litLength>=RUN_MASK) + { + unsigned len = litLength - RUN_MASK; + *token=(RUN_MASK<= 255 ; len-=255) *op++ = 255; + *op++ = (BYTE)len; + } + else *token = (BYTE)(litLength< oMaxMatch) + { + /* Match description too long : reduce it */ + matchLength = (15-1) + (oMaxMatch-op) * 255; + } + /*printf("offset %5i, matchLength%5i \n", (int)(ip-match), matchLength + MINMATCH);*/ + ip += MINMATCH + matchLength; + + if (matchLength>=ML_MASK) + { + *token += ML_MASK; + matchLength -= ML_MASK; + while (matchLength >= 255) { matchLength-=255; *op++ = 255; } + *op++ = (BYTE)matchLength; + } + else *token += (BYTE)(matchLength); + } + + anchor = ip; + + /* Test end of block */ + if (ip > mflimit) break; + if (op > oMaxSeq) break; + + /* Fill table */ + LZ4_putPosition(ip-2, ctx, tableType, base); + + /* Test next position */ + match = LZ4_getPosition(ip, ctx, tableType, base); + LZ4_putPosition(ip, ctx, tableType, base); + if ( (match+MAX_DISTANCE>=ip) + && (LZ4_read32(match)==LZ4_read32(ip)) ) + { token=op++; *token=0; goto _next_match; } + + /* Prepare next loop */ + forwardH = LZ4_hashPosition(++ip, tableType); + } + +_last_literals: + /* Encode Last Literals */ + { + size_t lastRunSize = (size_t)(iend - anchor); + if (op + 1 /* token */ + ((lastRunSize+240)/255) /* litLength */ + lastRunSize /* literals */ > oend) + { + /* adapt lastRunSize to fill 'dst' */ + lastRunSize = (oend-op) - 1; + lastRunSize -= (lastRunSize+240)/255; + } + ip = anchor + lastRunSize; + + if (lastRunSize >= RUN_MASK) + { + size_t accumulator = lastRunSize - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; + *op++ = (BYTE) accumulator; + } + else + { + *op++ = (BYTE)(lastRunSize<= LZ4_compressBound(*srcSizePtr)) /* compression success is guaranteed */ + { + return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); + } + else + { + if (*srcSizePtr < LZ4_64Klimit) + return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, byU16); + else + return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, LZ4_64bits() ? byU32 : byPtr); + } +} + + +int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) +{ +#if (HEAPMODE) + void* ctx = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ +#else + LZ4_stream_t ctxBody; + void* ctx = &ctxBody; +#endif + + int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); #if (HEAPMODE) FREEMEM(ctx); @@ -748,19 +930,10 @@ int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, in } -/***************************************** - Experimental : Streaming functions -*****************************************/ -/* - * LZ4_initStream - * Use this function once, to init a newly allocated LZ4_stream_t structure - * Return : 1 if OK, 0 if error - */ -void LZ4_resetStream (LZ4_stream_t* LZ4_stream) -{ - MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t)); -} +/******************************** +* Streaming functions +********************************/ LZ4_stream_t* LZ4_createStream(void) { @@ -770,6 +943,11 @@ LZ4_stream_t* LZ4_createStream(void) return lz4s; } +void LZ4_resetStream (LZ4_stream_t* LZ4_stream) +{ + MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t)); +} + int LZ4_freeStream (LZ4_stream_t* LZ4_stream) { FREEMEM(LZ4_stream); @@ -777,6 +955,7 @@ int LZ4_freeStream (LZ4_stream_t* LZ4_stream) } +#define HASH_UNIT sizeof(size_t) int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) { LZ4_stream_t_internal* dict = (LZ4_stream_t_internal*) LZ4_dict; @@ -784,24 +963,26 @@ int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) const BYTE* const dictEnd = p + dictSize; const BYTE* base; - if (dict->initCheck) LZ4_resetStream(LZ4_dict); /* Uninitialized structure detected */ + if ((dict->initCheck) || (dict->currentOffset > 1 GB)) /* Uninitialized structure, or reuse overflow */ + LZ4_resetStream(LZ4_dict); - if (dictSize < MINMATCH) + if (dictSize < (int)HASH_UNIT) { dict->dictionary = NULL; dict->dictSize = 0; return 0; } - if (p <= dictEnd - 64 KB) p = dictEnd - 64 KB; + if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; + dict->currentOffset += 64 KB; base = p - dict->currentOffset; dict->dictionary = p; dict->dictSize = (U32)(dictEnd - p); dict->currentOffset += dict->dictSize; - while (p <= dictEnd-MINMATCH) + while (p <= dictEnd-HASH_UNIT) { - LZ4_putPosition(p, dict, byU32, base); + LZ4_putPosition(p, dict->hashTable, byU32, base); p+=3; } @@ -830,8 +1011,7 @@ static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, const BYTE* src) } -FORCE_INLINE int LZ4_compress_continue_generic (void* LZ4_stream, const char* source, char* dest, int inputSize, - int maxOutputSize, limitedOutput_directive limit) +int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { LZ4_stream_t_internal* streamPtr = (LZ4_stream_t_internal*)LZ4_stream; const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize; @@ -840,6 +1020,7 @@ FORCE_INLINE int LZ4_compress_continue_generic (void* LZ4_stream, const char* so if (streamPtr->initCheck) return 0; /* Uninitialized structure detected */ if ((streamPtr->dictSize>0) && (smallest>dictEnd)) smallest = dictEnd; LZ4_renormDictT(streamPtr, smallest); + if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; /* Check overlapping input/dictionary space */ { @@ -858,9 +1039,9 @@ FORCE_INLINE int LZ4_compress_continue_generic (void* LZ4_stream, const char* so { int result; if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) - result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limit, byU32, withPrefix64k, dictSmall); + result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, dictSmall, acceleration); else - result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limit, byU32, withPrefix64k, noDictIssue); + result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, noDictIssue, acceleration); streamPtr->dictSize += (U32)inputSize; streamPtr->currentOffset += (U32)inputSize; return result; @@ -870,9 +1051,9 @@ FORCE_INLINE int LZ4_compress_continue_generic (void* LZ4_stream, const char* so { int result; if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) - result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limit, byU32, usingExtDict, dictSmall); + result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, dictSmall, acceleration); else - result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limit, byU32, usingExtDict, noDictIssue); + result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, noDictIssue, acceleration); streamPtr->dictionary = (const BYTE*)source; streamPtr->dictSize = (U32)inputSize; streamPtr->currentOffset += (U32)inputSize; @@ -880,18 +1061,8 @@ FORCE_INLINE int LZ4_compress_continue_generic (void* LZ4_stream, const char* so } } -int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) -{ - return LZ4_compress_continue_generic(LZ4_stream, source, dest, inputSize, 0, notLimited); -} -int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize) -{ - return LZ4_compress_continue_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput); -} - - -/* Hidden debug function, to force separate dictionary mode */ +/* Hidden debug function, to force external dictionary mode */ int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int inputSize) { LZ4_stream_t_internal* streamPtr = (LZ4_stream_t_internal*)LZ4_dict; @@ -902,7 +1073,7 @@ int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* if (smallest > (const BYTE*) source) smallest = (const BYTE*) source; LZ4_renormDictT((LZ4_stream_t_internal*)LZ4_dict, smallest); - result = LZ4_compress_generic(LZ4_dict, source, dest, inputSize, 0, notLimited, byU32, usingExtDict, noDictIssue); + result = LZ4_compress_generic(LZ4_dict, source, dest, inputSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); streamPtr->dictionary = (const BYTE*)source; streamPtr->dictSize = (U32)inputSize; @@ -930,9 +1101,9 @@ int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) -/**************************** - Decompression functions -****************************/ +/******************************* +* Decompression functions +*******************************/ /* * This generic decompression function cover all use cases. * It shall be instantiated several times, using different sets of directives @@ -955,7 +1126,7 @@ FORCE_INLINE int LZ4_decompress_generic( ) { /* Local Variables */ - const BYTE* restrict ip = (const BYTE*) source; + const BYTE* ip = (const BYTE*) source; const BYTE* const iend = ip + inputSize; BYTE* op = (BYTE*) dest; @@ -1051,8 +1222,7 @@ FORCE_INLINE int LZ4_decompress_generic( { /* match can be copied as a single segment from external dictionary */ match = dictEnd - (lowPrefix-match); - memcpy(op, match, length); - op += length; + memmove(op, match, length); op += length; } else { @@ -1110,11 +1280,11 @@ FORCE_INLINE int LZ4_decompress_generic( if (endOnInput) return (int) (((char*)op)-dest); /* Nb of output bytes decoded */ else - return (int) (((char*)ip)-source); /* Nb of input bytes read */ + return (int) (((const char*)ip)-source); /* Nb of input bytes read */ /* Overflow error detected */ _output_error: - return (int) (-(((char*)ip)-source))-1; + return (int) (-(((const char*)ip)-source))-1; } @@ -1138,9 +1308,9 @@ int LZ4_decompress_fast(const char* source, char* dest, int originalSize) typedef struct { - BYTE* externalDict; + const BYTE* externalDict; size_t extDictSize; - BYTE* prefixEnd; + const BYTE* prefixEnd; size_t prefixSize; } LZ4_streamDecode_t_internal; @@ -1151,7 +1321,7 @@ typedef struct */ LZ4_streamDecode_t* LZ4_createStreamDecode(void) { - LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOCATOR(sizeof(U64), LZ4_STREAMDECODESIZE_U64); + LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOCATOR(1, sizeof(LZ4_streamDecode_t)); return lz4s; } @@ -1172,7 +1342,7 @@ int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dicti { LZ4_streamDecode_t_internal* lz4sd = (LZ4_streamDecode_t_internal*) LZ4_streamDecode; lz4sd->prefixSize = (size_t) dictSize; - lz4sd->prefixEnd = (BYTE*) dictionary + dictSize; + lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; lz4sd->externalDict = NULL; lz4sd->extDictSize = 0; return 1; @@ -1261,7 +1431,7 @@ FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, (BYTE*)dest-64 KB, NULL, 0); return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest-dictSize, NULL, 0); } - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (BYTE*)dictStart, dictSize); + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); } int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) @@ -1277,13 +1447,21 @@ int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSi /* debug function */ int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) { - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (BYTE*)dictStart, dictSize); + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); } /*************************************************** - Obsolete Functions +* Obsolete Functions ***************************************************/ +/* obsolete compression functions */ +int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); } +int LZ4_compress(const char* source, char* dest, int inputSize) { return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); } +int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); } +int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); } +int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); } +int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); } + /* These function names are deprecated and should no longer be used. They are only provided here for compatibility with older user programs. @@ -1298,59 +1476,33 @@ int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; } -static void LZ4_init(LZ4_stream_t_internal* lz4ds, const BYTE* base) +static void LZ4_init(LZ4_stream_t_internal* lz4ds, BYTE* base) { MEM_INIT(lz4ds, 0, LZ4_STREAMSIZE); lz4ds->bufferStart = base; } -int LZ4_resetStreamState(void* state, const char* inputBuffer) +int LZ4_resetStreamState(void* state, char* inputBuffer) { if ((((size_t)state) & 3) != 0) return 1; /* Error : pointer is not aligned on 4-bytes boundary */ - LZ4_init((LZ4_stream_t_internal*)state, (const BYTE*)inputBuffer); + LZ4_init((LZ4_stream_t_internal*)state, (BYTE*)inputBuffer); return 0; } -void* LZ4_create (const char* inputBuffer) +void* LZ4_create (char* inputBuffer) { void* lz4ds = ALLOCATOR(8, LZ4_STREAMSIZE_U64); - LZ4_init ((LZ4_stream_t_internal*)lz4ds, (const BYTE*)inputBuffer); + LZ4_init ((LZ4_stream_t_internal*)lz4ds, (BYTE*)inputBuffer); return lz4ds; } char* LZ4_slideInputBuffer (void* LZ4_Data) { LZ4_stream_t_internal* ctx = (LZ4_stream_t_internal*)LZ4_Data; - int dictSize = LZ4_saveDict((LZ4_stream_t*)ctx, (char*)ctx->bufferStart, 64 KB); + int dictSize = LZ4_saveDict((LZ4_stream_t*)LZ4_Data, (char*)ctx->bufferStart, 64 KB); return (char*)(ctx->bufferStart + dictSize); } -/* Obsolete compresson functions using User-allocated state */ - -int LZ4_sizeofState() { return LZ4_STREAMSIZE; } - -int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize) -{ - if (((size_t)(state)&3) != 0) return 0; /* Error : state is not aligned on 4-bytes boundary */ - MEM_INIT(state, 0, LZ4_STREAMSIZE); - - if (inputSize < LZ4_64Klimit) - return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue); - else - return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue); -} - -int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize) -{ - if (((size_t)(state)&3) != 0) return 0; /* Error : state is not aligned on 4-bytes boundary */ - MEM_INIT(state, 0, LZ4_STREAMSIZE); - - if (inputSize < LZ4_64Klimit) - return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue); - else - return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue); -} - /* Obsolete streaming decompression functions */ int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) diff --git a/vpi/lz4.h b/vpi/lz4.h index 7778caad3..3e7400225 100644 --- a/vpi/lz4.h +++ b/vpi/lz4.h @@ -1,7 +1,8 @@ /* LZ4 - Fast LZ compression algorithm Header File - Copyright (C) 2011-2014, Yann Collet. + Copyright (C) 2011-2015, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without @@ -28,7 +29,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - - LZ4 source repository : http://code.google.com/p/lz4/ + - LZ4 source repository : https://github.com/Cyan4973/lz4 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c */ #pragma once @@ -38,22 +39,22 @@ extern "C" { #endif /* - * lz4.h provides raw compression format functions, for optimal performance and integration into programs. - * If you need to generate data using an inter-operable format (respecting the framing specification), - * please use lz4frame.h instead. + * lz4.h provides block compression functions, and gives full buffer control to programmer. + * If you need to generate inter-operable compressed data (respecting LZ4 frame specification), + * and can let the library handle its own memory, please use lz4frame.h instead. */ /************************************** - Version +* Version **************************************/ #define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ -#define LZ4_VERSION_MINOR 5 /* for new (non-breaking) interface capabilities */ -#define LZ4_VERSION_RELEASE 0 /* for tweaks, bug-fixes, or development */ +#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */ +#define LZ4_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */ #define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) int LZ4_versionNumber (void); /************************************** - Tuning parameter +* Tuning parameter **************************************/ /* * LZ4_MEMORY_USAGE : @@ -66,77 +67,90 @@ int LZ4_versionNumber (void); /************************************** - Simple Functions +* Simple Functions **************************************/ -int LZ4_compress (const char* source, char* dest, int sourceSize); +int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize); int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize); /* -LZ4_compress() : - Compresses 'sourceSize' bytes from 'source' into 'dest'. - Destination buffer must be already allocated, - and must be sized to handle worst cases situations (input data not compressible) - Worst case size evaluation is provided by function LZ4_compressBound() - inputSize : Max supported value is LZ4_MAX_INPUT_SIZE - return : the number of bytes written in buffer dest - or 0 if the compression fails +LZ4_compress_default() : + Compresses 'sourceSize' bytes from buffer 'source' + into already allocated 'dest' buffer of size 'maxDestSize'. + Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize). + It also runs faster, so it's a recommended setting. + If the function cannot compress 'source' into a more limited 'dest' budget, + compression stops *immediately*, and the function result is zero. + As a consequence, 'dest' content is not valid. + This function never writes outside 'dest' buffer, nor read outside 'source' buffer. + sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE + maxDestSize : full or partial size of buffer 'dest' (which must be already allocated) + return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize) + or 0 if compression fails LZ4_decompress_safe() : - compressedSize : is obviously the source size - maxDecompressedSize : is the size of the destination buffer, which must be already allocated. - return : the number of bytes decompressed into the destination buffer (necessarily <= maxDecompressedSize) - If the destination buffer is not large enough, decoding will stop and output an error code (<0). + compressedSize : is the precise full size of the compressed block. + maxDecompressedSize : is the size of destination buffer, which must be already allocated. + return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize) + If destination buffer is not large enough, decoding will stop and output an error code (<0). If the source stream is detected malformed, the function will stop decoding and return a negative result. - This function is protected against buffer overflow exploits, - and never writes outside of output buffer, nor reads outside of input buffer. - It is also protected against malicious data packets. + This function is protected against buffer overflow exploits, including malicious data packets. + It never writes outside output buffer, nor reads outside input buffer. */ /************************************** - Advanced Functions +* Advanced Functions **************************************/ #define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ -#define LZ4_COMPRESSBOUND(isize) ((unsigned int)(isize) > (unsigned int)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) +#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) /* LZ4_compressBound() : Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) - This function is primarily useful for memory allocation purposes (output buffer size). + This function is primarily useful for memory allocation purposes (destination buffer size). Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). - - isize : is the input size. Max supported value is LZ4_MAX_INPUT_SIZE - return : maximum output size in a "worst case" scenario - or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE) + Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize) + inputSize : max supported value is LZ4_MAX_INPUT_SIZE + return : maximum output size in a "worst case" scenario + or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE) */ -int LZ4_compressBound(int isize); +int LZ4_compressBound(int inputSize); + +/* +LZ4_compress_fast() : + Same as LZ4_compress_default(), but allows to select an "acceleration" factor. + The larger the acceleration value, the faster the algorithm, but also the lesser the compression. + It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. + An acceleration value of "1" is the same as regular LZ4_compress_default() + Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1. +*/ +int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration); /* -LZ4_compress_limitedOutput() : - Compress 'sourceSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'. - If it cannot achieve it, compression will stop, and result of the function will be zero. - This saves time and memory on detecting non-compressible (or barely compressible) data. - This function never writes outside of provided output buffer. - - sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE - maxOutputSize : is the size of the destination buffer (which must be already allocated) - return : the number of bytes written in buffer 'dest' - or 0 if compression fails -*/ -int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize); - - -/* -LZ4_compress_withState() : - Same compression functions, but using an externally allocated memory space to store compression state. +LZ4_compress_fast_extState() : + Same compression function, just using an externally allocated memory space to store compression state. Use LZ4_sizeofState() to know how much memory must be allocated, - and then, provide it as 'void* state' to compression functions. + and allocate it on 8-bytes boundaries (using malloc() typically). + Then, provide it as 'void* state' to compression function. */ int LZ4_sizeofState(void); -int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); -int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); +int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration); + + +/* +LZ4_compress_destSize() : + Reverse the logic, by compressing as much data as possible from 'source' buffer + into already allocated buffer 'dest' of size 'targetDestSize'. + This function either compresses the entire 'source' content into 'dest' if it's large enough, + or fill 'dest' buffer completely with as much data as possible from 'source'. + *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'. + New value is necessarily <= old value. + return : Nb bytes written into 'dest' (necessarily <= targetDestSize) + or 0 if compression fails +*/ +int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize); /* @@ -152,7 +166,6 @@ LZ4_decompress_fast() : */ int LZ4_decompress_fast (const char* source, char* dest, int originalSize); - /* LZ4_decompress_safe_partial() : This function decompress a compressed block of size 'compressedSize' at position 'source' @@ -169,9 +182,8 @@ int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedS /*********************************************** - Streaming Compression Functions +* Streaming Compression Functions ***********************************************/ - #define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4) #define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long)) /* @@ -187,7 +199,7 @@ typedef struct { long long table[LZ4_STREAMSIZE_U64]; } LZ4_stream_t; * LZ4_resetStream * Use this function to init an allocated LZ4_stream_t structure */ -void LZ4_resetStream (LZ4_stream_t* LZ4_streamPtr); +void LZ4_resetStream (LZ4_stream_t* streamPtr); /* * LZ4_createStream will allocate and initialize an LZ4_stream_t structure @@ -196,7 +208,7 @@ void LZ4_resetStream (LZ4_stream_t* LZ4_streamPtr); * They are more future proof, in case of a change of LZ4_stream_t size. */ LZ4_stream_t* LZ4_createStream(void); -int LZ4_freeStream (LZ4_stream_t* LZ4_streamPtr); +int LZ4_freeStream (LZ4_stream_t* streamPtr); /* * LZ4_loadDict @@ -205,36 +217,31 @@ int LZ4_freeStream (LZ4_stream_t* LZ4_streamPtr); * Loading a size of 0 is allowed. * Return : dictionary size, in bytes (necessarily <= 64 KB) */ -int LZ4_loadDict (LZ4_stream_t* LZ4_streamPtr, const char* dictionary, int dictSize); +int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); /* - * LZ4_compress_continue - * Compress data block 'source', using blocks compressed before as dictionary to improve compression ratio - * Previous data blocks are assumed to still be present at their previous location. + * LZ4_compress_fast_continue + * Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio. + * Important : Previous data blocks are assumed to still be present and unmodified ! + * 'dst' buffer must be already allocated. + * If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. + * If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero. */ -int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); - -/* - * LZ4_compress_limitedOutput_continue - * Same as before, but also specify a maximum target compressed size (maxOutputSize) - * If objective cannot be met, compression exits, and returns a zero. - */ -int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); +int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration); /* * LZ4_saveDict * If previously compressed data block is not guaranteed to remain available at its memory location * save it into a safer place (char* safeBuffer) * Note : you don't need to call LZ4_loadDict() afterwards, - * dictionary is immediately usable, you can therefore call again LZ4_compress_continue() - * Return : dictionary size in bytes, or 0 if error - * Note : any dictSize > 64 KB will be interpreted as 64KB. + * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue() + * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error */ -int LZ4_saveDict (LZ4_stream_t* LZ4_streamPtr, char* safeBuffer, int dictSize); +int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize); /************************************************ - Streaming Decompression Functions +* Streaming Decompression Functions ************************************************/ #define LZ4_STREAMDECODESIZE_U64 4 @@ -265,8 +272,18 @@ int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dicti *_continue() : These decoding functions allow decompression of multiple blocks in "streaming" mode. Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB) - If this condition is not possible, save the relevant part of decoded data into a safe buffer, - and indicate where is its new address using LZ4_setStreamDecode() + In the case of a ring buffers, decoding buffer must be either : + - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions) + In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB). + - Larger than encoding buffer, by a minimum of maxBlockSize more bytes. + maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block. + In which case, encoding and decoding buffers do not need to be synchronized, + and encoding ring buffer can have any size, including small ones ( < 64 KB). + - _At least_ 64 KB + 8 bytes + maxBlockSize. + In which case, encoding and decoding buffers do not need to be synchronized, + and encoding ring buffer can have any size, including larger than decoding buffer. + Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer, + and indicate where it is saved using LZ4_setStreamDecode() */ int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize); int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize); @@ -276,8 +293,8 @@ int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const ch Advanced decoding functions : *_usingDict() : These decoding functions work the same as - a combination of LZ4_setDictDecode() followed by LZ4_decompress_x_continue() - They are stand-alone and don't use nor update an LZ4_streamDecode_t structure. + a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue() + They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure. */ int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize); int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize); @@ -285,29 +302,57 @@ int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalS /************************************** - Obsolete Functions +* Obsolete Functions **************************************/ -/* -Obsolete decompression functions -These function names are deprecated and should no longer be used. -They are only provided here for compatibility with older user programs. -- LZ4_uncompress is the same as LZ4_decompress_fast -- LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe -These function prototypes are now disabled; uncomment them if you really need them. -It is highly recommended to stop using these functions and migrate to newer ones */ +/* Deprecate Warnings */ +/* Should these warnings messages be a problem, + it is generally possible to disable them, + with -Wno-deprecated-declarations for gcc + or _CRT_SECURE_NO_WARNINGS in Visual for example. + You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */ +#ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK +# define LZ4_DEPRECATE_WARNING_DEFBLOCK +# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +# if (LZ4_GCC_VERSION >= 405) || defined(__clang__) +# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) +# elif (LZ4_GCC_VERSION >= 301) +# define LZ4_DEPRECATED(message) __attribute__((deprecated)) +# elif defined(_MSC_VER) +# define LZ4_DEPRECATED(message) __declspec(deprecated(message)) +# else +# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler") +# define LZ4_DEPRECATED(message) +# endif +#endif /* LZ4_DEPRECATE_WARNING_DEFBLOCK */ + +/* Obsolete compression functions */ +/* These functions are planned to start generate warnings by r131 approximately */ +int LZ4_compress (const char* source, char* dest, int sourceSize); +int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize); +int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); +int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); +int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); +int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); + +/* Obsolete decompression functions */ +/* These function names are completely deprecated and must no longer be used. + They are only provided here for compatibility with older programs. + - LZ4_uncompress is the same as LZ4_decompress_fast + - LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe + These function prototypes are now disabled; uncomment them only if you really need them. + It is highly recommended to stop using these prototypes and migrate to maintained ones */ /* int LZ4_uncompress (const char* source, char* dest, int outputSize); */ /* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */ - /* Obsolete streaming functions; use new streaming interface whenever possible */ -void* LZ4_create (const char* inputBuffer); -int LZ4_sizeofStreamState(void); -int LZ4_resetStreamState(void* state, const char* inputBuffer); -char* LZ4_slideInputBuffer (void* state); +LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer); +LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void); +LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer); +LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state); /* Obsolete streaming decoding functions */ -int LZ4_decompress_safe_withPrefix64k (const char* source, char* dest, int compressedSize, int maxOutputSize); -int LZ4_decompress_fast_withPrefix64k (const char* source, char* dest, int originalSize); +LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); +LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); #if defined (__cplusplus) diff --git a/vpi/sdf_lexor.lex b/vpi/sdf_lexor.lex index 0b8c7767e..9f05dd230 100644 --- a/vpi/sdf_lexor.lex +++ b/vpi/sdf_lexor.lex @@ -5,7 +5,7 @@ %{ /* - * Copyright (c) 2007-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2007-2017 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 @@ -217,7 +217,7 @@ static void destroy_sdf_lexor(void) { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 -# if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 +# if YY_FLEX_MINOR_VERSION > 5 || defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif diff --git a/vpi/sys_convert.c b/vpi/sys_convert.c index 389e3c764..8f78fec5a 100644 --- a/vpi/sys_convert.c +++ b/vpi/sys_convert.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2014 Michael Ruff (mruff at chiaro.com) + * Copyright (c) 2003-2018 Michael Ruff (mruff at chiaro.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 @@ -90,12 +90,12 @@ static void error_message(vpiHandle callh, const char* msg) vpi_control(vpiFinish, 1); } -static PLI_INT32 sizetf_32 (PLI_BYTE8*name) +static PLI_INT32 sizetf_32 (ICARUS_VPI_CONST PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ return 32; } -static PLI_INT32 sizetf_64 (PLI_BYTE8*name) +static PLI_INT32 sizetf_64 (ICARUS_VPI_CONST PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ return 64; @@ -288,4 +288,3 @@ void sys_convert_register(void) res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } - diff --git a/vpi/sys_countdrivers.c b/vpi/sys_countdrivers.c index 3eedc431e..d4fbb8ea4 100644 --- a/vpi/sys_countdrivers.c +++ b/vpi/sys_countdrivers.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Martin Whitaker. (icarus@martin-whitaker.me.uk) + * Copyright (C) 2012-2018 Martin Whitaker. (icarus@martin-whitaker.me.uk) * * 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 @@ -33,6 +33,7 @@ static void check_net_arg(vpiHandle arg, vpiHandle callh, const char *name) case vpiPartSelect: if (vpi_get(vpiType, vpi_handle(vpiParent, arg)) != vpiNet) break; + // fallthrough case vpiNet: if (vpi_get(vpiSize, arg) != 1) break; diff --git a/vpi/sys_darray.c b/vpi/sys_darray.c index 507bb974a..7fe90f4f4 100644 --- a/vpi/sys_darray.c +++ b/vpi/sys_darray.c @@ -263,7 +263,7 @@ static PLI_INT32 to_vec_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) offset)); vec_ptr->bval |= (bval >> (darr_word_size - offset)); - /* Start at the begining of the next vector part. */ + /* Start at the beginning of the next vector part. */ } else { offset = 0; } diff --git a/vpi/sys_display.c b/vpi/sys_display.c index ad36a1fbd..04cd66799 100644 --- a/vpi/sys_display.c +++ b/vpi/sys_display.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2018 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 @@ -65,6 +65,23 @@ static PLI_INT32 my_mcd_printf(PLI_UINT32 mcd, const char *fmt, ...) return r; } +static void my_mcd_rawwrite(PLI_UINT32 mcd, const char*buf, size_t count) +{ + if (IS_MCD(mcd)) { + vpip_mcd_rawwrite(mcd, buf, count); + } else { + FILE*fp = vpi_get_file(mcd); + if (fp) { + while (count > 0) { + size_t rc = fwrite(buf, 1, count, fp); + if (rc == 0) break; + count -= rc; + buf += rc; + } + } + } +} + struct timeformat_info_s timeformat_info = { 0, 0, 0, 20 }; struct strobe_cb_info { @@ -152,10 +169,11 @@ static int get_default_format(const char *name) int default_format; switch(name[ strlen(name)-1 ]){ - /* writE/strobE or monitoR or displaY/fdisplaY or sformaT */ + /* writE/strobE or monitoR or displaY/fdisplaY or sformaT/sformatF */ case 'e': case 'r': case 't': + case 'f': case 'y': default_format = vpiDecStrVal; break; case 'h': default_format = vpiHexStrVal; break; case 'o': default_format = vpiOctStrVal; break; @@ -389,31 +407,31 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus, vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { - value.format = vpiStringVal; + value.format = vpiIntVal; vpi_get_value(info->items[*idx], &value); if (value.format == vpiSuppressVal) { vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { - char ch = value.value.str[strlen(value.value.str)-1]; - - /* If the default buffer is too small, make it big enough. */ - size = width + 1; - if (size > ini_size) result = realloc(result, size*sizeof(char)); + char ch = value.value.integer; /* If the width is less than one then use a width of one. */ if (width < 1) width = 1; + size = width + 1; + assert(size <= ini_size); + if (ljust == 0) { if (width > 1) { - char *cp = malloc((width+1)*sizeof(char)); - memset(cp, (ld_zero == 1 ? '0': ' '), width-1); - cp[width-1] = ch; - cp[width] = '\0'; - sprintf(result, "%*s", width, cp); - free(cp); - } else sprintf(result, "%c", ch); - } else sprintf(result, "%-*c", width, ch); - size = strlen(result) + 1; + memset(result, (ld_zero == 1 ? '0': ' '), width-1); + } + result[width-1] = ch; + } else { + result[0] = ch; + if (width > 1) { + memset(result+1, ' ', width-1); + } + } + result[width] = '\0'; } } break; @@ -522,7 +540,14 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus, if (size < 320) size = 320; size += prec; if (size > ini_size) result = realloc(result, size*sizeof(char)); +#if !defined(__GNUC__) + if (isnan(value.value.real)) + sprintf(result, "%s", "nan"); + else + sprintf(result, fmtb+1, value.value.real); +#else sprintf(result, fmtb+1, value.value.real); +#endif size = strlen(result) + 1; } } @@ -596,10 +621,6 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus, case 't': case 'T': *idx += 1; - if (plus != 0) { - vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", - info->filename, info->lineno, info->name, fmtb); - } if (*idx >= info->nitems) { vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", info->filename, info->lineno, info->name, fmtb); @@ -621,8 +642,35 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus, vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { - char *tbuf; + char *tbuf, *prev_suff = 0; PLI_INT32 time_units = vpi_get(vpiTimeUnit, info->scope); + + if (plus != 0) { + /* Icarus-specific extension to print out time units. + * It is needed by vhdlpp to correctly implement time'image(). */ + PLI_INT32 time_prec = vpi_get(vpiTimePrecision, info->scope); + + /* We are about to override the suffix string set with $timeformat(), + * therefore we need to restore it after the call. */ + prev_suff = timeformat_info.suff; + + if(time_units < -12) + timeformat_info.suff = strdup(" fs"); + else if(time_units < -9) + timeformat_info.suff = strdup(" ps"); + else if(time_units < -6) + timeformat_info.suff = strdup(" ns"); + else if(time_units < -3) + timeformat_info.suff = strdup(" us"); + else if(time_units < 0) + timeformat_info.suff = strdup(" ms"); + else + timeformat_info.suff = strdup(" s"); + + /* Adjust shift for get_time(), so the number indeed matches the unit */ + time_units += (3 + (time_units % 3)) % 3 + (time_prec - time_units); + } + unsigned swidth, free_flag = 0; unsigned suff_len = strlen(timeformat_info.suff); char *cp; @@ -643,6 +691,13 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus, cp = tbuf; swidth = strlen(tbuf); + if(plus != 0) { + /* Restore the previous suffix string, overridden by + * a vhdlpp-specific $sformatf() call. */ + free(timeformat_info.suff); + timeformat_info.suff = prev_suff; + } + if (ld_zero == 1) { /* No leading zeros are created by this conversion so just make * the width 0 for this case. */ @@ -930,7 +985,18 @@ static char *get_display(unsigned int *rtnsz, const struct strobe_cb_info *info) } else if (vpi_get(vpiConstType, item) == vpiRealConst) { value.format = vpiRealVal; vpi_get_value(item, &value); +#if !defined(__GNUC__) + if(compatible_flag) + sprintf(buf, "%g", value.value.real); + else { + if(value.value.real == 0.0 || value.value.real == -0.0) + sprintf(buf, "%.05f", value.value.real); + else + sprintf(buf, "%#g", value.value.real); + } +#else sprintf(buf, compatible_flag ? "%g" : "%#g", value.value.real); +#endif result = strdup(buf); width = strlen(result); } else { @@ -975,7 +1041,18 @@ static char *get_display(unsigned int *rtnsz, const struct strobe_cb_info *info) case vpiRealVar: value.format = vpiRealVal; vpi_get_value(item, &value); +#if !defined(__GNUC__) + if (compatible_flag) + sprintf(buf, "%g", value.value.real); + else { + if (value.value.real == 0.0 || value.value.real == -0.0) + sprintf(buf, "%.05f", value.value.real); + else + sprintf(buf, "%#g", value.value.real); + } +#else sprintf(buf, compatible_flag ? "%g" : "%#g", value.value.real); +#endif width = strlen(buf); rtn = realloc(rtn, (size+width)*sizeof(char)); memcpy(rtn+size-1, buf, width); @@ -1092,7 +1169,7 @@ static int sys_check_args(vpiHandle callh, vpiHandle argv, const PLI_BYTE8*name, name, vpi_get_str(vpiType, arg)); ret = 1; } - + // fallthrough case vpiConstant: case vpiParameter: case vpiNet: @@ -1192,14 +1269,16 @@ static PLI_INT32 sys_display_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) return sys_common_compiletf(name, 0, 0); } -/* This implements the $display/$fdisplay and the $write/$fwrite based tasks. */ +/* This implements the $sformatf, $display/$fdisplay + * and the $write/$fwrite based tasks. */ static PLI_INT32 sys_display_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, scope; struct strobe_cb_info info; char* result; - unsigned int size, location=0; + unsigned int size; PLI_UINT32 fd_mcd; + s_vpi_value val; callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); @@ -1208,7 +1287,6 @@ static PLI_INT32 sys_display_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) if(name[1] == 'f') { errno = 0; vpiHandle arg = vpi_scan(argv); - s_vpi_value val; val.format = vpiIntVal; vpi_get_value(arg, &val); fd_mcd = val.value.integer; @@ -1229,7 +1307,11 @@ static PLI_INT32 sys_display_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) vpi_free_object(argv); return 0; } + } else if(strncmp(name,"$sformatf",9) == 0) { + /* return as a string */ + fd_mcd = 0; } else { + /* stdout */ fd_mcd = 1; } @@ -1247,21 +1329,21 @@ static PLI_INT32 sys_display_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) /* Because %u and %z may put embedded NULL characters into the * returned string strlen() may not match the real size! */ result = get_display(&size, &info); - while (location < size) { - if (result[location] == '\0') { - my_mcd_printf(fd_mcd, "%c", '\0'); - location += 1; - } else { - my_mcd_printf(fd_mcd, "%s", &result[location]); - location += strlen(&result[location]); - } - } - if ((strncmp(name,"$display",8) == 0) || - (strncmp(name,"$fdisplay",9) == 0)) my_mcd_printf(fd_mcd, "\n"); + if(fd_mcd > 0) { + my_mcd_rawwrite(fd_mcd, result, size); + if ((strncmp(name,"$display",8) == 0) || + (strncmp(name,"$fdisplay",9) == 0)) my_mcd_rawwrite(fd_mcd, "\n", 1); + } else { + /* Return as a string ($sformatf) */ + val.format = vpiStringVal; + val.value.str = result; + vpi_put_value(callh, &val, 0, vpiNoDelay); + } + + free(result); free(info.filename); free(info.items); - free(result); return 0; } @@ -1282,20 +1364,12 @@ static PLI_INT32 strobe_cb(p_cb_data cb) if ((! IS_MCD(info->fd_mcd) && vpi_get_file(info->fd_mcd) != NULL) || ( IS_MCD(info->fd_mcd) && my_mcd_printf(info->fd_mcd, "") != EOF)) { char* result; - unsigned int size, location=0; + unsigned int size; /* Because %u and %z may put embedded NULL characters into the * returned string strlen() may not match the real size! */ result = get_display(&size, info); - while (location < size) { - if (result[location] == '\0') { - my_mcd_printf(info->fd_mcd, "%c", '\0'); - location += 1; - } else { - my_mcd_printf(info->fd_mcd, "%s", &result[location]); - location += strlen(&result[location]); - } - } - my_mcd_printf(info->fd_mcd, "\n"); + my_mcd_rawwrite(info->fd_mcd, result, size); + my_mcd_rawwrite(info->fd_mcd, "\n", 1); free(result); } @@ -1397,23 +1471,15 @@ static int monitor_enabled = 1; static PLI_INT32 monitor_cb_2(p_cb_data cb) { char* result; - unsigned int size, location=0; + unsigned int size; (void)cb; /* Parameter is not used. */ /* Because %u and %z may put embedded NULL characters into the * returned string strlen() may not match the real size! */ result = get_display(&size, &monitor_info); - while (location < size) { - if (result[location] == '\0') { - my_mcd_printf(monitor_info.fd_mcd, "%c", '\0'); - location += 1; - } else { - my_mcd_printf(monitor_info.fd_mcd, "%s", &result[location]); - location += strlen(&result[location]); - } - } - my_mcd_printf(monitor_info.fd_mcd, "\n"); + my_mcd_rawwrite(monitor_info.fd_mcd, result, size); + my_mcd_rawwrite(monitor_info.fd_mcd, "\n", 1); monitor_scheduled = 0; free(result); return 0; @@ -1643,7 +1709,7 @@ static PLI_INT32 sys_sformat_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) if (argv == 0) { vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s requires at least two argument.\n", name); + vpi_printf("%s requires at least two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } @@ -1664,7 +1730,7 @@ static PLI_INT32 sys_sformat_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) if (arg == 0) { vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s requires at least two argument.\n", name); + vpi_printf("%s requires at least two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } @@ -1732,6 +1798,46 @@ static PLI_INT32 sys_sformat_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) return 0; } +static PLI_INT32 sys_sformatf_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + PLI_INT32 type; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires at least one argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* The first argument must be a string, a register or a SV string. */ + arg = vpi_scan(argv); + if (arg == 0) { + vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires at least one argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + type = vpi_get(vpiType, arg); + if (((type != vpiConstant && type != vpiParameter) || + vpi_get(vpiConstType, arg) != vpiStringConst) && + type != vpiReg && type != vpiStringVar) { + vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's first argument must be a string or a register.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + if (sys_check_args(callh, argv, name, 0, 0)) vpi_control(vpiFinish, 1); + return 0; +} + static PLI_INT32 sys_end_of_compile(p_cb_data cb_data) { (void)cb_data; /* Parameter is not used. */ @@ -1865,6 +1971,8 @@ static const char *pts_convert(int value) { const char *string; switch (value) { + case 2: string = "100s"; break; + case 1: string = "10s"; break; case 0: string = "1s"; break; case -1: string = "100ms"; break; case -2: string = "10ms"; break; @@ -1949,12 +2057,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))); @@ -2412,6 +2515,16 @@ void sys_display_register(void) res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiStringFunc; + tf_data.tfname = "$sformatf"; + tf_data.calltf = sys_display_calltf; + tf_data.compiletf = sys_sformatf_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$sformatf"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + /*============================ timeformat */ tf_data.type = vpiSysTask; tf_data.tfname = "$timeformat"; diff --git a/vpi/sys_fileio.c b/vpi/sys_fileio.c index ec99c9f00..dbdecf5dc 100644 --- a/vpi/sys_fileio.c +++ b/vpi/sys_fileio.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2018 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 @@ -132,7 +132,7 @@ static PLI_INT32 sys_fopen_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) } } if (! fail) break; - + // fallthrough default: vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), @@ -880,7 +880,16 @@ static PLI_INT32 sys_fseek_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) } val.format = vpiIntVal; +#if defined(__GNUC__) val.value.integer = fseek(fp, offset, oper); +#else + if(oper < 0) { + val.value.integer = EOF; + errno = EINVAL; + } + else + val.value.integer = fseek(fp, offset, oper); +#endif vpi_put_value(callh, &val, 0 , vpiNoDelay); return 0; diff --git a/vpi/sys_fst.c b/vpi/sys_fst.c index f8c26404f..5286c1443 100644 --- a/vpi/sys_fst.c +++ b/vpi/sys_fst.c @@ -1,5 +1,5 @@ /* - * 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 @@ -35,6 +35,8 @@ static char *dump_path = NULL; static struct fstContext *dump_file = NULL; +static struct t_vpi_time zero_delay = { vpiSimTime, 0, 0, 0.0 }; + struct vcd_info { vpiHandle item; vpiHandle cb; @@ -83,7 +85,7 @@ static void show_this_item(struct vcd_info*info) } else { value.format = vpiBinStrVal; vpi_get_value(info->item, &value); - fstWriterEmitValueChange(dump_file, info->handle, value.value.str); + fstWriterEmitValueChange(dump_file, info->handle, (type != vpiNamedEvent) ? value.value.str : "1"); } } @@ -182,6 +184,7 @@ static PLI_INT32 variable_cb_1(p_cb_data cause) if (!vcd_dmp_list) { cb = *cause; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = variable_cb_2; vpi_register_cb(&cb); @@ -248,7 +251,6 @@ static PLI_INT32 finish_cb(p_cb_data cause) __inline__ static int install_dumpvars_callback(void) { struct t_cb_data cb; - static struct t_vpi_time now; if (dumpvars_status == 1) return 0; @@ -259,8 +261,7 @@ __inline__ static int install_dumpvars_callback(void) return 1; } - now.type = vpiSimTime; - cb.time = &now; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = dumpvars_cb; cb.user_data = 0x0; @@ -623,18 +624,18 @@ static void scan_item(unsigned depth, vpiHandle item, int skip) new_ident = fstWriterCreateVar(dump_file, type, dir, size, buf, - (fstHandle)(long)ident); + (fstHandle)(intptr_t)ident); free(buf); } else { new_ident = fstWriterCreateVar(dump_file, type, dir, size, escname, - (fstHandle)(long)ident); + (fstHandle)(intptr_t)ident); } free(escname); if (!ident) { if (nexus_id) set_nexus_ident(nexus_id, - (const char *)(long)new_ident); + (const char *)(intptr_t)new_ident); /* Add a callback for the signal. */ info = malloc(sizeof(*info)); 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_lxt.c b/vpi/sys_lxt.c index a0f16fbfc..d0de6765d 100644 --- a/vpi/sys_lxt.c +++ b/vpi/sys_lxt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2018 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,6 +38,8 @@ static char *dump_path = NULL; static struct lt_trace *dump_file = NULL; +static struct t_vpi_time zero_delay = { vpiSimTime, 0, 0, 0.0 }; + struct vcd_info { vpiHandle item; vpiHandle cb; @@ -261,6 +263,7 @@ static PLI_INT32 variable_cb_1(p_cb_data cause) if (!vcd_dmp_list) { cb = *cause; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = variable_cb_2; vpi_register_cb(&cb); @@ -320,7 +323,6 @@ static PLI_INT32 finish_cb(p_cb_data cause) __inline__ static int install_dumpvars_callback(void) { struct t_cb_data cb; - static struct t_vpi_time now; if (dumpvars_status == 1) return 0; @@ -331,8 +333,7 @@ __inline__ static int install_dumpvars_callback(void) return 1; } - now.type = vpiSimTime; - cb.time = &now; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = dumpvars_cb; cb.user_data = 0x0; @@ -549,6 +550,7 @@ static void scan_item(unsigned depth, vpiHandle item, int skip) PLI_INT32 idx = vpi_get(vpiIndex, item); item = vpi_handle_by_index(array, idx); } + // fallthrough case vpiIntegerVar: case vpiBitVar: case vpiByteVar: diff --git a/vpi/sys_lxt2.c b/vpi/sys_lxt2.c index bf211019a..3327264d1 100644 --- a/vpi/sys_lxt2.c +++ b/vpi/sys_lxt2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2018 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,8 @@ static struct lxt2_wr_trace *dump_file = NULL; static void* lxt2_thread(void*arg); +static struct t_vpi_time zero_delay = { vpiSimTime, 0, 0, 0.0 }; + /* * Manage a table of all the dump-enabled vcd item. The cells of this * table are allocated incrementally, but are released all at once, so @@ -105,7 +107,6 @@ void delete_all_vcd_info(void) */ # define VCD_INFO_ENDP ((struct vcd_info*)1) static struct vcd_info *vcd_dmp_list = VCD_INFO_ENDP; -static struct t_vpi_time vcd_dmp_time; static PLI_UINT64 vcd_cur_time = 0; static int dump_is_off = 0; @@ -302,9 +303,8 @@ static PLI_INT32 variable_cb_1(p_cb_data cause) } if (vcd_dmp_list == VCD_INFO_ENDP) { - vcd_dmp_time.type = vpiSuppressTime; cb = *cause; - cb.time = &vcd_dmp_time; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = variable_cb_2; vpi_register_cb(&cb); @@ -358,7 +358,6 @@ static PLI_INT32 finish_cb(p_cb_data cause) __inline__ static int install_dumpvars_callback(void) { struct t_cb_data cb; - static struct t_vpi_time now; if (dumpvars_status == 1) return 0; @@ -369,8 +368,7 @@ __inline__ static int install_dumpvars_callback(void) return 1; } - now.type = vpiSimTime; - cb.time = &now; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = dumpvars_cb; cb.user_data = 0x0; @@ -612,6 +610,7 @@ static void scan_item(unsigned depth, vpiHandle item, int skip) PLI_INT32 idx = vpi_get(vpiIndex, item); item = vpi_handle_by_index(array, idx); } + // fallthrough case vpiIntegerVar: case vpiBitVar: case vpiByteVar: diff --git a/vpi/sys_priv.c b/vpi/sys_priv.c index 08529cced..dd9714325 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-2018 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,17 +37,17 @@ PLI_UINT64 timerec_to_time64(const struct t_vpi_time*timerec) char *as_escaped(char *arg) { - unsigned idx, cur, cnt, len = strlen(arg); - char *res = (char *) malloc(sizeof(char *) * len); + unsigned idx, cur, cnt, len = strlen(arg) + 1; + char *res = (char *) malloc(sizeof(char) * len); cur = 0; - cnt = len; + cnt = len - 1; for (idx = 0; idx < cnt; idx++) { if (isprint((int)arg[idx])) { res[cur] = arg[idx]; cur += 1; } else { len += 3; - res = (char *) realloc(res, sizeof(char *) * len); + res = (char *) realloc(res, sizeof(char) * len); sprintf(&(res[cur]), "\\%03o", arg[idx]); cur += 4; } @@ -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; @@ -396,3 +399,23 @@ PLI_INT32 sys_one_string_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) return 0; } + +/* Return an integer value to the caller. */ +void put_integer_value(vpiHandle callh, PLI_INT32 result) +{ + s_vpi_value val; + + val.format = vpiIntVal; + val.value.integer = result; + vpi_put_value(callh, &val, 0, vpiNoDelay); +} + +/* Return a scalar value to the caller. */ +void put_scalar_value(vpiHandle callh, PLI_INT32 result) +{ + s_vpi_value val; + + val.format = vpiScalarVal; + val.value.scalar = result; + vpi_put_value(callh, &val, 0, vpiNoDelay); +} diff --git a/vpi/sys_priv.h b/vpi/sys_priv.h index 36553b250..d9052d51d 100644 --- a/vpi/sys_priv.h +++ b/vpi/sys_priv.h @@ -1,7 +1,7 @@ #ifndef IVL_sys_priv_H #define IVL_sys_priv_H /* - * Copyright (c) 2002-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2018 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 @@ -65,4 +65,10 @@ extern PLI_INT32 sys_one_opt_numeric_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *n extern PLI_INT32 sys_two_numeric_args_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name); extern PLI_INT32 sys_one_string_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name); +/* + * The standard put/return a value to the caller routines. + */ +extern void put_integer_value(vpiHandle callh, PLI_INT32 result); +extern void put_scalar_value(vpiHandle callh, PLI_INT32 result); + #endif /* IVL_sys_priv_H */ diff --git a/vpi/sys_queue.c b/vpi/sys_queue.c index 3d34c7b4a..7eb7525c2 100644 --- a/vpi/sys_queue.c +++ b/vpi/sys_queue.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2018 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 @@ -781,6 +781,7 @@ static PLI_INT32 fill_variable_with_scaled_time(vpiHandle var, uint64_t c_time) switch (words) { default: val_ptr[1].aval = (c_time >> 32) & 0xffffffff; + // fallthrough case 1: val_ptr[0].aval = c_time & 0xffffffff; } diff --git a/vpi/sys_random.c b/vpi/sys_random.c index f774764a6..72bb1c66f 100644 --- a/vpi/sys_random.c +++ b/vpi/sys_random.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2018 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 @@ -924,7 +924,7 @@ static PLI_INT32 sys_dist_erlang_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) return 0; } -static PLI_INT32 sys_rand_func_sizetf(PLI_BYTE8 *name) +static PLI_INT32 sys_rand_func_sizetf(ICARUS_VPI_CONST PLI_BYTE8 *name) { (void)name; /* Parameter is not used. */ return 32; diff --git a/vpi/sys_readmem.c b/vpi/sys_readmem.c index a670a6317..0922214cf 100644 --- a/vpi/sys_readmem.c +++ b/vpi/sys_readmem.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2018 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 @@ -49,6 +49,7 @@ static void get_mem_params(vpiHandle argv, vpiHandle callh, const char *name, case vpiConstant: case vpiParameter: if (vpi_get(vpiConstType, *start_item) != vpiRealConst) break; + // fallthrough case vpiRealVar: vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); @@ -67,6 +68,7 @@ static void get_mem_params(vpiHandle argv, vpiHandle callh, const char *name, if (vpi_get(vpiConstType, *stop_item) != vpiRealConst) { break; } + // fallthrough case vpiRealVar: vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), diff --git a/vpi/sys_readmem_lex.lex b/vpi/sys_readmem_lex.lex index 22e5b0e80..c7f4c53ef 100644 --- a/vpi/sys_readmem_lex.lex +++ b/vpi/sys_readmem_lex.lex @@ -4,7 +4,7 @@ %{ /* - * Copyright (c) 1999-2009 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2017 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 @@ -199,7 +199,7 @@ void destroy_readmem_lexor(void) { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 -# if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 +# if YY_FLEX_MINOR_VERSION > 5 || defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif diff --git a/vpi/sys_scanf.c b/vpi/sys_scanf.c index f05b6245f..4768a72e8 100644 --- a/vpi/sys_scanf.c +++ b/vpi/sys_scanf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2006-2018 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 @@ -20,6 +20,7 @@ /* round() is ISO C99 from math.h. This define should enable it. */ # define _ISOC99_SOURCE 1 # define _SVID_SOURCE 1 +# define _DEFAULT_SOURCE 1 # include "sys_priv.h" # include @@ -218,7 +219,7 @@ static double get_float(struct byte_source *src, unsigned width, int *match) */ static int scan_format_float(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, - unsigned suppress_flag, PLI_BYTE8 *name, + unsigned suppress_flag, ICARUS_VPI_CONST PLI_BYTE8 *name, char code) { vpiHandle arg; @@ -265,7 +266,7 @@ static int scan_format_float(vpiHandle callh, vpiHandle argv, */ static int scan_format_float_time(vpiHandle callh, vpiHandle argv, struct byte_source*src, unsigned width, - unsigned suppress_flag, PLI_BYTE8 *name) + unsigned suppress_flag, ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle scope = vpi_handle(vpiScope, callh); int time_units = vpi_get(vpiTimeUnit, scope); @@ -329,7 +330,7 @@ static int scan_format_float_time(vpiHandle callh, vpiHandle argv, */ static int scan_format_base(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, - unsigned suppress_flag, PLI_BYTE8 *name, + unsigned suppress_flag, ICARUS_VPI_CONST PLI_BYTE8 *name, const char *match, char code, PLI_INT32 type) { @@ -405,7 +406,7 @@ static int scan_format_base(vpiHandle callh, vpiHandle argv, */ static int scan_format_binary(vpiHandle callh, vpiHandle argv, struct byte_source *src, int width, - unsigned suppress_flag, PLI_BYTE8 *name) + unsigned suppress_flag, ICARUS_VPI_CONST PLI_BYTE8 *name) { return scan_format_base(callh, argv, src, width, suppress_flag, name, "01xzXZ?_", 'b', vpiBinStrVal); @@ -419,7 +420,7 @@ static int scan_format_binary(vpiHandle callh, vpiHandle argv, */ static int scan_format_char(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, - unsigned suppress_flag, PLI_BYTE8 *name) + unsigned suppress_flag, ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle arg; s_vpi_value val; @@ -465,7 +466,7 @@ static int scan_format_char(vpiHandle callh, vpiHandle argv, */ static int scan_format_decimal(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, - unsigned suppress_flag, PLI_BYTE8 *name) + unsigned suppress_flag, ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle arg; char *strval = malloc(1); @@ -584,7 +585,7 @@ static int scan_format_decimal(vpiHandle callh, vpiHandle argv, */ static int scan_format_hex(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, - unsigned suppress_flag, PLI_BYTE8 *name) + unsigned suppress_flag, ICARUS_VPI_CONST PLI_BYTE8 *name) { return scan_format_base(callh, argv, src, width, suppress_flag, name, "0123456789abcdefxzABCDEFXZ?_", 'h', @@ -596,7 +597,7 @@ static int scan_format_hex(vpiHandle callh, vpiHandle argv, */ static int scan_format_octal(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, - unsigned suppress_flag, PLI_BYTE8 *name) + unsigned suppress_flag, ICARUS_VPI_CONST PLI_BYTE8 *name) { return scan_format_base(callh, argv, src, width, suppress_flag, name, "01234567xzXZ?_", 'o', vpiOctStrVal); @@ -607,7 +608,7 @@ static int scan_format_octal(vpiHandle callh, vpiHandle argv, * Routine to return the current hierarchical path (implements %m). */ static int scan_format_module_path(vpiHandle callh, vpiHandle argv, - unsigned suppress_flag, PLI_BYTE8 *name) + unsigned suppress_flag, ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle scope, arg; char *module_path; @@ -649,7 +650,7 @@ static int scan_format_module_path(vpiHandle callh, vpiHandle argv, */ static int scan_format_string(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, - unsigned suppress_flag, PLI_BYTE8 *name) + unsigned suppress_flag, ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle arg; char *strval = malloc(1); @@ -728,7 +729,7 @@ static int scan_format_string(vpiHandle callh, vpiHandle argv, */ static int scan_format_two_state(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, - unsigned suppress_flag, PLI_BYTE8 *name) + unsigned suppress_flag, ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle arg; p_vpi_vecval val_ptr; @@ -867,7 +868,7 @@ static int scan_format_two_state(vpiHandle callh, vpiHandle argv, */ static int scan_format_four_state(vpiHandle callh, vpiHandle argv, struct byte_source *src, unsigned width, - unsigned suppress_flag, PLI_BYTE8 *name) + unsigned suppress_flag, ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle arg; p_vpi_vecval val_ptr; @@ -1005,7 +1006,7 @@ static int scan_format_four_state(vpiHandle callh, vpiHandle argv, * passed to this function, which processes the rest of the function. */ static int scan_format(vpiHandle callh, struct byte_source*src, vpiHandle argv, - PLI_BYTE8 *name) + ICARUS_VPI_CONST PLI_BYTE8 *name) { s_vpi_value val; vpiHandle item; @@ -1257,6 +1258,7 @@ static int is_assignable_obj(vpiHandle obj) break; case vpiPartSelect: if (! is_assignable_obj(vpi_handle(vpiParent, obj))) break; + // fallthrough case vpiIntegerVar: case vpiBitVar: case vpiByteVar: @@ -1402,7 +1404,7 @@ static PLI_INT32 sys_sscanf_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) case vpiConstant: case vpiParameter: if (vpi_get(vpiConstType, reg) == vpiStringConst) break; - + // fallthrough default: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); diff --git a/vpi/sys_sdf.c b/vpi/sys_sdf.c index 5d69ff623..9d3cd2b7b 100644 --- a/vpi/sys_sdf.c +++ b/vpi/sys_sdf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2007-2017 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 @@ -289,7 +289,10 @@ static PLI_INT32 sys_sdf_annotate_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) FILE *sdf_fd; char *fname = get_filename(callh, name, vpi_scan(argv)); - if (fname == 0) return 0; + if (fname == 0) { + vpi_free_object(argv); + return 0; + } sdf_fd = fopen(fname, "r"); if (sdf_fd == 0) { @@ -297,6 +300,8 @@ static PLI_INT32 sys_sdf_annotate_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) (int)vpi_get(vpiLineNo, callh)); vpi_printf("Unable to open SDF file \"%s\"." " Skipping this annotation.\n", fname); + vpi_free_object(argv); + free(fname); return 0; } diff --git a/vpi/sys_table.c b/vpi/sys_table.c index bb5e3ad7e..23b2c9142 100644 --- a/vpi/sys_table.c +++ b/vpi/sys_table.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2010,2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2018 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 diff --git a/vpi/sys_vcd.c b/vpi/sys_vcd.c index 30d783f90..fb97a2fad 100644 --- a/vpi/sys_vcd.c +++ b/vpi/sys_vcd.c @@ -1,5 +1,5 @@ /* - * 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 @@ -34,6 +34,8 @@ static char *dump_path = NULL; static FILE *dump_file = NULL; +static struct t_vpi_time zero_delay = { vpiSimTime, 0, 0, 0.0 }; + struct vcd_info { vpiHandle item; vpiHandle cb; @@ -216,6 +218,7 @@ static PLI_INT32 variable_cb_1(p_cb_data cause) if (!vcd_dmp_list) { cb = *cause; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = variable_cb_2; vpi_register_cb(&cb); @@ -283,7 +286,6 @@ static PLI_INT32 finish_cb(p_cb_data cause) __inline__ static int install_dumpvars_callback(void) { struct t_cb_data cb; - static struct t_vpi_time now; if (dumpvars_status == 1) return 0; @@ -294,8 +296,7 @@ __inline__ static int install_dumpvars_callback(void) return 1; } - now.type = vpiSimTime; - cb.time = &now; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = dumpvars_cb; cb.user_data = 0x0; diff --git a/vpi/system.sft b/vpi/system.sft index f3048198f..be082c85f 100644 --- a/vpi/system.sft +++ b/vpi/system.sft @@ -24,3 +24,5 @@ $table_model vpiSysFuncReal $ivl_string_method$len vpiSysFuncInt $ivl_string_method$to_vec vpiSysFuncVoid + +$sformatf vpiSysFuncString diff --git a/vpi/table_mod.c b/vpi/table_mod.c index 43609989b..c8157fdda 100644 --- a/vpi/table_mod.c +++ b/vpi/table_mod.c @@ -629,7 +629,7 @@ static unsigned initialize_table_model(vpiHandle callh, const char *name, } /* - * Routine to evalute the table model using the current input values. + * Routine to evaluate the table model using the current input values. */ static double eval_table_model(vpiHandle callh, p_table_mod table) { diff --git a/vpi/table_mod_lexor.lex b/vpi/table_mod_lexor.lex index bd518ee7e..36031821b 100644 --- a/vpi/table_mod_lexor.lex +++ b/vpi/table_mod_lexor.lex @@ -6,7 +6,7 @@ %{ /* - * Copyright (C) 2011-2013 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2017 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 @@ -154,7 +154,7 @@ void destroy_tblmod_lexor(void) { # ifdef FLEX_SCANNER # if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 -# if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 +# if YY_FLEX_MINOR_VERSION > 5 || defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 yylex_destroy(); # endif # endif diff --git a/vpi/table_mod_parse.y b/vpi/table_mod_parse.y index 1ded2cae3..4ba043d10 100644 --- a/vpi/table_mod_parse.y +++ b/vpi/table_mod_parse.y @@ -1,7 +1,7 @@ %{ /* - * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2015 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 @@ -228,7 +228,7 @@ void yyerror(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - fprintf(stderr, "%s:%u: TABLE ERROR: ", in_file_name, + fprintf(stderr, "%s:%d: TABLE ERROR: ", in_file_name, yylloc.first_line-1); vfprintf(stderr, fmt, ap); va_end(ap); diff --git a/vpi/v2009.sft b/vpi/v2009.sft index e2a84620b..e60c8cfdd 100644 --- a/vpi/v2009.sft +++ b/vpi/v2009.sft @@ -12,5 +12,11 @@ $high vpiSysFuncInt $increment vpiSysFuncInt $size vpiSysFuncInt +$countbits vpiSysFuncInt +$countones vpiSysFuncInt +$onehot vpiSysFuncSized 1 unsigned +$onehot0 vpiSysFuncSized 1 unsigned +$isunknown vpiSysFuncSized 1 unsigned + $ivl_array_method$to_vec vpiSysFuncVoid $ivl_array_method$from_vec vpiSysFuncVoid diff --git a/vpi/v2009_array.c b/vpi/v2009_array.c index 0262f5f28..daf914167 100644 --- a/vpi/v2009_array.c +++ b/vpi/v2009_array.c @@ -55,7 +55,11 @@ static PLI_INT32 one_array_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) // Checks if a function is passed an array and optionally an integer. static PLI_INT32 array_int_opt_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) { +#if defined(__GNUC__) const int MAX_ARGC = 3; // one more is to verify there are at most 2 args +#else +#define MAX_ARGC 3 +#endif vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv, arg[MAX_ARGC]; PLI_INT32 arg_type[MAX_ARGC]; diff --git a/vpi/v2009_bitvec.c b/vpi/v2009_bitvec.c new file mode 100644 index 000000000..cc824b9bb --- /dev/null +++ b/vpi/v2009_bitvec.c @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2018 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 + * 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 +#include "vpi_user.h" +#include "sys_priv.h" + +/* + * Check that $couintbits() is called with the correct arguments. + */ +static PLI_INT32 countbits_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv, arg; + int cb_count = 1; + + assert(callh != 0); + argv = vpi_iterate(vpiArgument, callh); + (void)name; /* Parameter is not used. */ + + /* $countbits() must have arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("$countbits() requires at least two arguments.\n"); + vpi_control(vpiFinish, 1); + return 0; + } + + /* The 1st argument must be numeric. */ + arg = vpi_scan(argv); + if (! is_numeric_obj(arg)) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("The first argument to $countbits() must be numeric.\n"); + vpi_control(vpiFinish, 1); + } + + /* We need one or more numeric control bit arguments. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("$countbits() requires at least one control bit " + "argument.\n"); + vpi_control(vpiFinish, 1); + } + + do { + if (arg && ! is_numeric_obj(arg)) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Control bit argument %d to $countbits() must " + "be numeric.\n", cb_count); + vpi_control(vpiFinish, 1); + } + ++cb_count; + if (arg) arg = vpi_scan(argv); + } while (arg); + + return 0; +} + +/* Count the number of bits in the expression that match the search bits. */ +static PLI_INT32 count_bits_in_expr(vpiHandle expr_arg, char search[4]) +{ + s_vpi_value val; + PLI_INT32 result; + PLI_INT32 size = vpi_get(vpiSize, expr_arg); + assert(size > 0); + + val.format = vpiVectorVal; + vpi_get_value(expr_arg, &val); + + result = 0; + for (unsigned lp = 0; lp < (unsigned)size; ++lp) { + unsigned offset = lp / 32; + unsigned bit = lp % 32; + unsigned abit, bbit; + abit = (val.value.vector[offset].aval >> bit) & 0x1; + bbit = (val.value.vector[offset].bval >> bit) & 0x1; + if (search[(bbit<<1)|abit]) ++result; + } + + return result; +} + +static PLI_INT32 countbits_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle expr_arg = vpi_scan(argv); + vpiHandle arg; + char search[4]; + (void)name; /* Parameter is not used. */ + + /* Scan the control bit arguments and mark which control bits to + * include in the count. */ + for (unsigned lp = 0; lp < 4 ; ++lp) search[lp] = 0; + while ((arg = vpi_scan(argv))) { + s_vpi_value val; + val.format = vpiScalarVal; + vpi_get_value(arg, &val); + switch (val.value.scalar) { + case vpi0: + search[0] = 1; + break; + case vpi1: + search[1] = 1; + break; + case vpiZ: + search[2] = 1; + break; + case vpiX: + search[3] = 1; + break; + default: + vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Unknown scalar control bit argument %d passed " + "to $countbits() will be ignored.\n", + val.value.scalar); + break; + } + } + + put_integer_value(callh, count_bits_in_expr(expr_arg, search)); + + return 0; +} + +/* Count the number of ones in the expression. */ +static PLI_INT32 count_ones_in_expr(vpiHandle expr_arg) +{ + s_vpi_value val; + PLI_INT32 result; + PLI_INT32 size = vpi_get(vpiSize, expr_arg); + assert(size > 0); + + val.format = vpiVectorVal; + vpi_get_value(expr_arg, &val); + + result = 0; + size = (size + 31) / 32; + for (unsigned lp = 0; lp < (unsigned)size; ++lp) { + PLI_UINT32 ones = ~val.value.vector[lp].bval & + val.value.vector[lp].aval; + while (ones) { + if (ones & 0x1) ++result; + ones >>= 1; + } + } + + return result; +} + +static PLI_INT32 countones_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle expr_arg = vpi_scan(argv); + (void)name; /* Parameter is not used. */ + + vpi_free_object(argv); + + put_integer_value(callh, count_ones_in_expr(expr_arg)); + + return 0; +} + +/* Check to see if the expression is onehot. */ +static PLI_INT32 is_onehot(vpiHandle expr_arg, unsigned zero_is_okay) +{ + s_vpi_value val; + unsigned found_a_one; + PLI_INT32 size = vpi_get(vpiSize, expr_arg); + assert(size > 0); + + val.format = vpiVectorVal; + vpi_get_value(expr_arg, &val); + + found_a_one = 0; + size = (size + 31) / 32; + for (unsigned lp = 0; lp < (unsigned)size; ++lp) { + PLI_UINT32 ones = ~val.value.vector[lp].bval & + val.value.vector[lp].aval; + while (ones) { + if (ones & 0x1) { + if (found_a_one) return vpi0; + found_a_one = 1; + } + ones >>= 1; + } + } + + if (found_a_one) return vpi1; + else if (zero_is_okay) return vpi1; + return vpi0; +} + +static PLI_INT32 onehot_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle expr_arg = vpi_scan(argv); + (void)name; /* Parameter is not used. */ + + vpi_free_object(argv); + + put_scalar_value(callh, is_onehot(expr_arg, 0)); + + return 0; +} + +static PLI_INT32 onehot0_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle expr_arg = vpi_scan(argv); + (void)name; /* Parameter is not used. */ + + vpi_free_object(argv); + + put_scalar_value(callh, is_onehot(expr_arg, 1)); + + return 0; +} + +/* Check to see if the expression has an undefined value. */ +static PLI_INT32 is_unknown(vpiHandle expr_arg) +{ + s_vpi_value val; + PLI_INT32 size = vpi_get(vpiSize, expr_arg); + assert(size > 0); + + val.format = vpiVectorVal; + vpi_get_value(expr_arg, &val); + + size = (size + 31) / 32; + for (unsigned lp = 0; lp < (unsigned)size; ++lp) { + if (val.value.vector[lp].bval) return vpi1; + } + + return vpi0; +} + +static PLI_INT32 isunknown_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle expr_arg = vpi_scan(argv); + (void)name; /* Parameter is not used. */ + + vpi_free_object(argv); + + put_scalar_value(callh, is_unknown(expr_arg)); + + return 0; +} + +static PLI_INT32 bit_vec_sizetf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + (void)name; /* Parameter is not used. */ + + return 1; +} + +/* + * Register the functions with Verilog. + */ +void v2009_bitvec_register(void) +{ + s_vpi_systf_data tf_data; + vpiHandle res; + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiIntFunc; + tf_data.calltf = countbits_calltf; + tf_data.compiletf = countbits_compiletf; + tf_data.sizetf = 0; + tf_data.tfname = "$countbits"; + tf_data.user_data = 0; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiIntFunc; + tf_data.calltf = countones_calltf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; + tf_data.sizetf = 0; + tf_data.tfname = "$countones"; + tf_data.user_data = "$countones"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiSizedFunc; + tf_data.calltf = onehot_calltf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; + tf_data.sizetf = bit_vec_sizetf; + tf_data.tfname = "$onehot"; + tf_data.user_data = "$onehot"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiSizedFunc; + tf_data.calltf = onehot0_calltf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; + tf_data.sizetf = bit_vec_sizetf; + tf_data.tfname = "$onehot0"; + tf_data.user_data = "$onehot0"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiSizedFunc; + tf_data.calltf = isunknown_calltf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; + tf_data.sizetf = bit_vec_sizetf; + tf_data.tfname = "$isunknown"; + tf_data.user_data = "$isunknown"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); +} diff --git a/vpi/v2009_enum.c b/vpi/v2009_enum.c index be9aaa9fa..8c638ff22 100644 --- a/vpi/v2009_enum.c +++ b/vpi/v2009_enum.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2010-2018 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 @@ -108,6 +108,7 @@ static PLI_INT32 ivl_enum_method_next_prev_compiletf(ICARUS_VPI_CONST PLI_BYTE8* case vpiConstant: case vpiParameter: if (vpi_get(vpiConstType, arg_count) != vpiStringConst) break; + // fallthrough default: vpi_printf("%s:%d: compiler error: ", vpi_get_str(vpiFile, sys), @@ -226,7 +227,7 @@ static void fill_handle_with_init(vpiHandle arg, int is_two_state) /* * Implement the next()/prev() enumeration methods. */ -static PLI_INT32 ivl_enum_method_next_prev_calltf(PLI_BYTE8*name) +static PLI_INT32 ivl_enum_method_next_prev_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle sys = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, sys); @@ -431,7 +432,7 @@ static PLI_INT32 ivl_enum_method_name_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) /* * Implement the name() enumeration method. */ -static PLI_INT32 ivl_enum_method_name_calltf(PLI_BYTE8*name) +static PLI_INT32 ivl_enum_method_name_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) { vpiHandle sys = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, sys); diff --git a/vpi/v2009_table.c b/vpi/v2009_table.c index 4e3328779..1a3f8ee2b 100644 --- a/vpi/v2009_table.c +++ b/vpi/v2009_table.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2010-2018 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 @@ -18,11 +18,13 @@ */ extern void v2009_array_register(void); +extern void v2009_bitvec_register(void); extern void v2009_enum_register(void); extern void v2009_string_register(void); void (*vlog_startup_routines[])(void) = { v2009_array_register, + v2009_bitvec_register, v2009_enum_register, v2009_string_register, 0 diff --git a/vpi/vams_simparam.c b/vpi/vams_simparam.c index 6d5e8b30d..2de9818a9 100644 --- a/vpi/vams_simparam.c +++ b/vpi/vams_simparam.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2014 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2008-2018 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 @@ -247,7 +247,7 @@ static PLI_INT32 simparam_str_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name_ext) return 0; } -static PLI_INT32 simparam_str_sizetf(PLI_BYTE8 *name_ext) +static PLI_INT32 simparam_str_sizetf(ICARUS_VPI_CONST PLI_BYTE8 *name_ext) { (void) name_ext; /* Parameter is not used. */ return MAX_STRING_RESULT; /* 128 characters max! */ diff --git a/vpi/vhdl_sys.sft b/vpi/vhdl_sys.sft index d0d452212..86903476f 100644 --- a/vpi/vhdl_sys.sft +++ b/vpi/vhdl_sys.sft @@ -1,3 +1,6 @@ $ivlh_attribute_event vpiSysFuncSized 1 unsigned $ivlh_rising_edge vpiSysFuncSized 1 unsigned $ivlh_falling_edge vpiSysFuncSized 1 unsigned + +$ivlh_shift_left vpiSysFuncSized 32 unsigned +$ivlh_shift_right vpiSysFuncSized 32 unsigned diff --git a/vpi/vhdl_table.c b/vpi/vhdl_table.c index a6dcac300..64cfe0386 100644 --- a/vpi/vhdl_table.c +++ b/vpi/vhdl_table.c @@ -21,6 +21,7 @@ # include "vpi_user.h" # include # include "ivl_alloc.h" +# include "sys_priv.h" /* * The $ivlh_attribute_event implements the VHDL 'event @@ -40,11 +41,24 @@ struct monitor_data { static struct monitor_data **mdata = 0; static unsigned mdata_count = 0; -typedef enum { EVENT = 0, RISING_EDGE = 1, FALLING_EDGE = 2 } event_type_t; -static const char* func_names[] = { - "$ivlh_attribute_event", - "$ivlh_rising_edge", - "$ivlh_falling_edge" +typedef enum { + EVENT = 0, + RISING_EDGE = 1, + FALLING_EDGE = 2 +} event_type_t; +static const char* attr_func_names[] = { + "$ivlh_attribute_event", + "$ivlh_rising_edge", + "$ivlh_falling_edge" +}; + +typedef enum { + SHIFT_LEFT = 0, + SHIFT_RIGHT = 1, +} shift_type_t; +static const char* shift_func_names[] = { + "$ivlh_shift_left", + "$ivlh_shift_right", }; /* To keep valgrind happy free the allocated memory. */ @@ -93,7 +107,7 @@ static PLI_INT32 ivlh_attribute_event_compiletf(ICARUS_VPI_CONST PLI_BYTE8*data) vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, sys), (int)vpi_get(vpiLineNo, sys)); vpi_printf("(compiler error) %s requires a single argument.\n", - func_names[type]); + attr_func_names[type]); vpi_control(vpiFinish, 1); return 0; } @@ -127,7 +141,7 @@ static PLI_INT32 ivlh_attribute_event_compiletf(ICARUS_VPI_CONST PLI_BYTE8*data) vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, sys), (int)vpi_get(vpiLineNo, sys)); vpi_printf("(compiler error) %s only takes a single argument.\n", - func_names[type]); + attr_func_names[type]); vpi_free_object(argv); vpi_control(vpiFinish, 1); } @@ -156,16 +170,16 @@ static PLI_INT32 ivlh_attribute_event_calltf(ICARUS_VPI_CONST PLI_BYTE8*data) rval.value.scalar = vpi1; - // Detect if there was any change + // Detect if change occurred in this moment if (mon->last_event.high != tnow.high) rval.value.scalar = vpi0; if (mon->last_event.low != tnow.low) rval.value.scalar = vpi0; // Determine the edge, if required - if (type == RISING_EDGE && mon->last_value.value.scalar == vpi0) + if (type == RISING_EDGE && mon->last_value.value.scalar != vpi1) rval.value.scalar = vpi0; - else if (type == FALLING_EDGE && mon->last_value.value.scalar == vpi1) + else if (type == FALLING_EDGE && mon->last_value.value.scalar != vpi0) rval.value.scalar = vpi0; } @@ -180,39 +194,83 @@ static PLI_INT32 ivlh_attribute_event_sizetf(ICARUS_VPI_CONST PLI_BYTE8*type) return 1; } +static PLI_INT32 ivlh_shift_calltf(ICARUS_VPI_CONST PLI_BYTE8*data) +{ + shift_type_t shift_type = (shift_type_t) data; + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle argh = vpi_scan(argv); + vpiHandle counth = vpi_scan(argv); + s_vpi_value val; + PLI_INT32 count; + + vpi_free_object(argv); + + val.format = vpiIntVal; + vpi_get_value(counth, &val); + count = val.value.integer; + + val.format = vpiIntVal; + vpi_get_value(argh, &val); + + if(shift_type == SHIFT_LEFT) + val.value.integer <<= count; + else if(shift_type == SHIFT_RIGHT) + val.value.integer >>= count; + else + assert(0); + + vpi_put_value(callh, &val, 0, vpiNoDelay); + + return 0; +} + +static PLI_INT32 ivlh_shift_sizetf(ICARUS_VPI_CONST PLI_BYTE8*type) +{ + (void) type; /* Parameter is not used. */ + return 32; +} + static void vhdl_register(void) { s_vpi_systf_data tf_data; s_cb_data cb; vpiHandle res; + /* Event attribute functions */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSizedFunc; tf_data.calltf = ivlh_attribute_event_calltf; tf_data.compiletf = ivlh_attribute_event_compiletf; tf_data.sizetf = ivlh_attribute_event_sizetf; - tf_data.tfname = func_names[EVENT]; + tf_data.tfname = attr_func_names[EVENT]; tf_data.user_data = (PLI_BYTE8*) EVENT; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); - tf_data.type = vpiSysFunc; - tf_data.sysfunctype = vpiSizedFunc; - tf_data.calltf = ivlh_attribute_event_calltf; - tf_data.compiletf = ivlh_attribute_event_compiletf; - tf_data.sizetf = ivlh_attribute_event_sizetf; - tf_data.tfname = func_names[RISING_EDGE]; + tf_data.tfname = attr_func_names[RISING_EDGE]; tf_data.user_data = (PLI_BYTE8*) RISING_EDGE; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); + tf_data.tfname = attr_func_names[FALLING_EDGE]; + tf_data.user_data = (PLI_BYTE8*) FALLING_EDGE; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + /* Shift functions */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSizedFunc; - tf_data.calltf = ivlh_attribute_event_calltf; - tf_data.compiletf = ivlh_attribute_event_compiletf; - tf_data.sizetf = ivlh_attribute_event_sizetf; - tf_data.tfname = func_names[FALLING_EDGE]; - tf_data.user_data = (PLI_BYTE8*) FALLING_EDGE; + tf_data.calltf = ivlh_shift_calltf; + tf_data.compiletf = sys_two_numeric_args_compiletf; + tf_data.sizetf = ivlh_shift_sizetf; + tf_data.tfname = shift_func_names[SHIFT_LEFT]; + tf_data.user_data = (PLI_BYTE8*) SHIFT_LEFT; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.tfname = shift_func_names[SHIFT_RIGHT]; + tf_data.user_data = (PLI_BYTE8*) SHIFT_RIGHT; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); diff --git a/vpi/vhdl_textio.c b/vpi/vhdl_textio.c new file mode 100644 index 000000000..ac18aacf9 --- /dev/null +++ b/vpi/vhdl_textio.c @@ -0,0 +1,1018 @@ +/* + * Copyright (c) 2015 CERN + * @author Maciej Suminski + * + * 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. + */ + +/** + * The following VPI module implements some of the functions available + * in std.textio library. + * + * Type counterparts: + * VHDL SystemVerilog + * ------------------------------ + * LINE string (line of text, in VHDL it is a pointer to string) + * TEXT int (file handle) + * + * Some of functions offered by std.textio library are not implemented here, + * as they can be directly replaced with SystemVerilog system functions. + * + * VHDL SystemVerilog + * -------------------------------------- + * FILE_CLOSE(file F: TEXT) $fclose(fd) + * ENDFILE(file F: TEXT) $feof(fd) + * + * Procedures: + * HREAD (L: inout LINE; VALUE: out BIT_VECTOR) + * HWRITE (L: inout LINE; VALUE: out BIT_VECTOR) + * are handled with $ivlh_read/write() using FORMAT_HEX parameter (see format_t enum). + */ + +# include "sys_priv.h" +# include "vpi_config.h" +# include "vpi_user.h" +# include +# include +# include +# include +# include "ivl_alloc.h" + +/* additional parameter values to distinguish between integer, boolean and + * time types or to use hex format */ +enum format_t { FORMAT_STD, FORMAT_BOOL, FORMAT_TIME, FORMAT_HEX, FORMAT_STRING }; + +enum file_mode_t { FILE_MODE_READ, FILE_MODE_WRITE, FILE_MODE_APPEND, FILE_MODE_LAST }; +enum file_open_status_t { FS_OPEN_OK, FS_STATUS_ERROR, FS_NAME_ERROR, FS_MODE_ERROR }; + +/* bits per vector, in a single s_vpi_vecval struct */ +static const size_t BPW = 8 * sizeof(PLI_INT32); + +/* string buffer size */ +static const size_t STRING_BUF_SIZE = 1024; + +static int is_integer_var(vpiHandle obj) +{ + PLI_INT32 type = vpi_get(vpiType, obj); + + return (type == vpiIntegerVar || type == vpiShortIntVar || + type == vpiIntVar || type == vpiLongIntVar); +} + +static int is_const(vpiHandle obj) +{ + return vpi_get(vpiType, obj) == vpiConstant; +} + +static void show_error_line(vpiHandle callh) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); +} + +static void show_warning_line(vpiHandle callh) { + vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); +} + +/* sets a single bit value in a bit/logic vector */ +static int set_vec_val(s_vpi_vecval* vector, char value, int idx) { + s_vpi_vecval*v = &vector[idx / BPW]; + PLI_INT32 bit = idx % BPW; + + switch(value) { + case '0': + v->bval &= ~(1 << bit); + v->aval &= ~(1 << bit); + break; + + case '1': + v->bval &= ~(1 << bit); + v->aval |= (1 << bit); + break; + + case 'z': + case 'Z': + v->bval |= (1 << bit); + v->aval &= ~(1 << bit); + break; + + case 'x': + case 'X': + v->bval |= (1 << bit); + v->aval |= (1 << bit); + break; + + default: + return 1; + } + + return 0; +} + +/* Converts a string of characters to a vector in s_vpi_value struct. + * Returns number of processed characters, 0 in case of failure. + * string is the data to be converted. + * val is the target s_vpi_value struct. + * var is the variable that is converted (to obtain size & type [2/4-state]). + */ +static int read_vector(const char *string, s_vpi_value *val, vpiHandle var) +{ +#if 0 + /* It could be easier to simply use val.format = vpiBinStrVal + * but there is no way to check if processing went fine */ + val.format = vpiBinStrVal; + val.value.str = string; + processed_chars = size; +#endif + /* Vector size (==1 for scalars) */ + int size = vpi_get(vpiSize, var); + + /* Number of required s_vpi_vecval structs to store the result */ + int words = (size + BPW - 1) / BPW; /* == ceil(size / BPW) */ + int len = strlen(string); + + val->format = vpiVectorVal; /* it also covers scalars */ + val->value.vector = calloc(words, sizeof(s_vpi_vecval)); + + /* Skip spaces in the beginning */ + int skipped = 0; + while(*string && *string == ' ') { + --len; + ++string; + ++skipped; + } + + /* Process bits */ + int p; + for(p = 0; p < size && p < len; ++p) { + if(set_vec_val(val->value.vector, string[p], size - p - 1)) { + free(val->value.vector); /* error */ + return 0; + } + } + + /* 2-logic variables cannot hold X or Z values, so change them to 0 */ + if(vpi_get(vpiType, var) == vpiBitVar) { + for(int i = 0; i < words; ++i) { + val->value.vector[i].aval &= ~val->value.vector[i].bval; + val->value.vector[i].bval = 0; + } + } + + return p + skipped; +} + +/* Converts a string of characters to a time value, stored in vector filed of + * s_vpi_value struct. + * Returns number of processed characters, 0 in case of failure. + * string is the data to be converted. + * val is the target s_vpi_value struct. + * scope_unit is the time unit used in the scope (-3 for millisecond, + * -6 for microsecond, etc.) + */ +static int read_time(const char *string, s_vpi_value *val, PLI_INT32 scope_unit) { + PLI_UINT64 period; + char units[2]; + int time_unit, processed_chars; + + if(sscanf(string, "%" PLI_UINT64_FMT " %2s%n", &period, units, &processed_chars) != 2) + return 0; + + if(!strncasecmp(units, "fs", 2)) + time_unit = -15; + else if(!strncasecmp(units, "ps", 2)) + time_unit = -12; + else if(!strncasecmp(units, "ns", 2)) + time_unit = -9; + else if(!strncasecmp(units, "us", 2)) + time_unit = -6; + else if(!strncasecmp(units, "ms", 2)) + time_unit = -3; + else if(!strncasecmp(units, "s", 1)) + time_unit = 0; + else + return 0; + + /* Scale the time units to the one used in the scope */ + int scale_diff = time_unit - scope_unit; + + if(scale_diff > 0) { + for(int i = 0; i < scale_diff; ++i) + period *= 10; + } else { + for(int i = 0; i < -scale_diff; ++i) + period /= 10; + } + + /* vpiTimeVal format is not handled at the moment, + * so return the read value as a vector*/ + val->format = vpiVectorVal; + val->value.vector = calloc(2, sizeof(s_vpi_vecval)); + memset(val->value.vector, 0, 2 * sizeof(s_vpi_vecval)); + + val->value.vector[1].aval = (PLI_UINT32) (period >> 32); + val->value.vector[0].aval = (PLI_UINT32) period; + + return processed_chars; +} + +static int read_string(const char *string, s_vpi_value *val, int count) { + char buf[STRING_BUF_SIZE]; + int processed_chars; + char format_str[32]; + + /* No string length limit imposed */ + if(count <= 0 || count >= (int)STRING_BUF_SIZE) + count = STRING_BUF_SIZE - 1; + + snprintf(format_str, 32, "%%%ds%%n", count); + + if(sscanf(string, format_str, buf, &processed_chars) != 1) + return 0; + + val->format = vpiStringVal; + val->value.str = strdup(buf); + + return processed_chars; +} + +static int write_time(char *string, const s_vpi_value* val, + size_t width, PLI_INT32 scope_unit) { + char prefix = 0; + PLI_UINT64 period; + + switch(val->format) { + case vpiIntVal: + period = val->value.integer; + break; + + case vpiVectorVal: + period = val->value.vector[0].aval; + + if(width > BPW) + period |= (PLI_UINT64)(val->value.vector[1].aval) << 32; + break; + + default: + return 1; + } + + /* Handle the case when the time unit base is 10 or 100 */ + int remainder = scope_unit % -3; + if(remainder) { + remainder += 3; + scope_unit -= remainder; + + while(remainder--) + period *= 10; + } + + switch(scope_unit) { + case -15: prefix = 'f'; break; + case -12: prefix = 'p'; break; + case -9: prefix = 'n'; break; + case -6: prefix = 'u'; break; + case -3: prefix = 'm'; break; + } + + if(prefix) + sprintf(string, "%" PLI_UINT64_FMT " %cs", period, prefix); + else + sprintf(string, "%" PLI_UINT64_FMT " s", period); + + return 0; +} + +/* slightly modified sys_fopen_compiletf */ +static PLI_INT32 ivlh_file_open_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv, arg; + assert(callh != 0); + int ok = 1; + + argv = vpi_iterate(vpiArgument, callh); + + /* Check that there is a file name argument and that it is a string. */ + if (argv == 0) + ok = 0; + + arg = vpi_scan(argv); + if (!arg || !is_integer_var(arg)) + ok = 0; + + arg = vpi_scan(argv); + if (arg && is_integer_var(arg)) + arg = vpi_scan(argv); + + // no vpi_scan() here, if we had both 'status' and 'file' arguments, + // then the next arg is read in the above if, otherwise we are going + // to check the second argument once again + if (!arg || !is_string_obj(arg)) + ok = 0; + + arg = vpi_scan(argv); + if (arg && !is_const(arg)) + ok = 0; + + if (!ok) { + show_error_line(callh); + vpi_printf("%s() function is available in following variants:\n", name); + vpi_printf("* (file f: text; filename: in string, file_open_kind: in mode)\n"); + vpi_printf("* (status: out file_open_status, file f: text; filename: in string, file_open_kind: in mode)\n"); + vpi_control(vpiFinish, 1); + } + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "four arguments", 1); + + return 0; +} + +/* procedure FILE_MODE(file F: TEXT; External_Name; in STRING; + Open_Kind: in FILE_MODE_KIND := READ_MODE); */ +/* slightly modified sys_fopen_calltf */ +static PLI_INT32 ivlh_file_open_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + s_vpi_value val; + int mode; + char *fname; + + vpiHandle fstatush = vpi_scan(argv); + vpiHandle fhandleh = vpi_scan(argv); + vpiHandle fnameh = vpi_scan(argv); + vpiHandle modeh = vpi_scan(argv); + + if(!modeh) { + /* There are only three arguments, so rearrange handles */ + modeh = fnameh; + fnameh = fhandleh; + fhandleh = fstatush; + fstatush = 0; + } else { + vpi_free_object(argv); + } + + /* Get the mode handle */ + val.format = vpiIntVal; + vpi_get_value(modeh, &val); + mode = val.value.integer; + + if(mode < 0 || mode >= FILE_MODE_LAST) { + show_error_line(callh); + vpi_printf("%s's file open mode argument is invalid.\n", name); + return 0; + } + + fname = get_filename(callh, name, fnameh); + + if(fname == 0) { + show_error_line(callh); + vpi_printf("%s's could not obtain the file name.\n", name); + return 0; + } + + /* Open file and save the handle */ + PLI_INT32 result = -1; + switch(mode) { + case FILE_MODE_READ: + result = vpi_fopen(fname, "r"); + break; + + case FILE_MODE_WRITE: + result = vpi_fopen(fname, "w"); + break; + + case FILE_MODE_APPEND: + result = vpi_fopen(fname, "a"); + break; + } + + if(fstatush) { + val.format = vpiIntVal; + + if(!result) { + switch(errno) { + case ENOENT: + case ENAMETOOLONG: + val.value.integer = FS_NAME_ERROR; + break; + + case EINVAL: + case EACCES: + case EEXIST: + case EISDIR: + val.value.integer = FS_MODE_ERROR; + break; + + default: + val.value.integer = FS_STATUS_ERROR; + break; + } + } else { + val.value.integer = FS_OPEN_OK; + } + + vpi_put_value(fstatush, &val, 0, vpiNoDelay); + } + + val.format = vpiIntVal; + val.value.integer = result; + vpi_put_value(fhandleh, &val, 0, vpiNoDelay); + free(fname); + + return 0; +} + +static PLI_INT32 ivlh_readwriteline_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are two arguments and that the first is an + integer (file handle) and that the second is string. */ + if(argv == 0) { + show_error_line(callh); + vpi_printf("%s requires two arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + arg = vpi_scan(argv); + if(!arg || !is_integer_var(arg)) { + show_error_line(callh); + vpi_printf("%s's first argument must be an integer variable (file handle).\n", name); + vpi_control(vpiFinish, 1); + } + + arg = vpi_scan(argv); + if(!arg || !is_string_obj(arg)) { + show_error_line(callh); + vpi_printf("%s's second argument must be a string.\n", name); + vpi_control(vpiFinish, 1); + } + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "two arguments", 0); + + return 0; +} + +/* procedure READLINE (file F: TEXT; L: inout LINE); */ +/* slightly modified sys_fgets_calltf */ +static PLI_INT32 ivlh_readline_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle stringh, arg; + s_vpi_value val; + PLI_UINT32 fd; + FILE *fp; + char *text; + char buf[STRING_BUF_SIZE]; + + /* Get the file descriptor. */ + arg = vpi_scan(argv); + val.format = vpiIntVal; + vpi_get_value(arg, &val); + fd = val.value.integer; + + /* Get the string handle. */ + stringh = vpi_scan(argv); + vpi_free_object(argv); + + /* Return zero if this is not a valid fd. */ + fp = vpi_get_file(fd); + if(!fp) { + show_warning_line(callh); + vpi_printf("invalid file descriptor (0x%x) given to %s.\n", + (unsigned int)fd, name); + return 0; + } + + /* Read in the bytes. Return 0 if there was an error. */ + if(fgets(buf, STRING_BUF_SIZE, fp) == 0) { + show_error_line(callh); + vpi_printf("%s reading past the end of file.\n", name); + + return 0; + } + + int len = strlen(buf); + + if(len == 0) { + show_error_line(callh); + vpi_printf("%s read 0 bytes.\n", name); + return 0; + } else if(len == STRING_BUF_SIZE - 1) { + show_warning_line(callh); + vpi_printf("%s has reached the buffer limit, part of the " + "processed string might have been skipped.\n", name); + } + + /* Remove the newline character(s) */ + while (len > 0 && (buf[len-1] == '\n' || buf[len-1] == '\r')) { + buf[len-1] = 0; + len--; + } + /* Return the characters to the register. */ + text = strdup(buf); + val.format = vpiStringVal; + val.value.str = text; + vpi_put_value(stringh, &val, 0, vpiNoDelay); + free(text); + + /* Set end-of-file flag if we have just reached the end of the file. + * Otherwise the flag would be set only after the next read operation. */ + int c = fgetc(fp); + ungetc(c, fp); + + return 0; +} + +/* procedure WRITELINE (file F: TEXT; L: inout LINE); */ +/* slightly modified sys_fgets_calltf */ +static PLI_INT32 ivlh_writeline_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle stringh, arg; + s_vpi_value val; + PLI_UINT32 fd; + FILE *fp; + char *empty; + + /* Get the file descriptor. */ + arg = vpi_scan(argv); + val.format = vpiIntVal; + vpi_get_value(arg, &val); + fd = val.value.integer; + + /* Get the string contents. */ + stringh = vpi_scan(argv); + val.format = vpiStringVal; + vpi_get_value(stringh, &val); + + vpi_free_object(argv); + + /* Return zero if this is not a valid fd. */ + fp = vpi_get_file(fd); + if(!fp) { + show_warning_line(callh); + vpi_printf("invalid file descriptor (0x%x) given to %s.\n", + (unsigned int)fd, name); + return 0; + } + + fprintf(fp, "%s\n", val.value.str); + + /* Clear the written string */ + empty = strdup(""); + val.format = vpiStringVal; + val.value.str = empty; + vpi_put_value(stringh, &val, 0, vpiNoDelay); + free(empty); + + return 0; +} + +static PLI_INT32 ivlh_read_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + if(argv == 0) { + show_error_line(callh); + vpi_printf("%s requires three arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + arg = vpi_scan(argv); + if(!arg || !is_string_obj(arg)) { + show_error_line(callh); + vpi_printf("%s's first argument must be a string.\n", name); + vpi_control(vpiFinish, 1); + } + + arg = vpi_scan(argv); + if(!arg || is_constant_obj(arg)) { + show_error_line(callh); + vpi_printf("%s's second argument must be a variable.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + arg = vpi_scan(argv); + if(!arg) { + show_error_line(callh); + vpi_printf("%s's third argument must be an integer.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "three arguments", 0); + + return 0; +} + +/* procedure READ (L: inout LINE; + VALUE: out BIT/BIT_VECTOR/BOOLEAN/CHARACTER/INTEGER/REAL/STRING/TIME); */ +static PLI_INT32 ivlh_read_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle stringh, varh, formath; + s_vpi_value val; + PLI_INT32 type, format, dest_size; + char *string = 0; + unsigned int processed_chars = 0, fail = 0; + + /* Get the string */ + stringh = vpi_scan(argv); + val.format = vpiStringVal; + vpi_get_value(stringh, &val); + + if(strlen(val.value.str) == 0) { + show_error_line(callh); + vpi_printf("%s cannot read from an empty string.\n", name); + return 0; + } + + string = strdup(val.value.str); + + /* Get the destination variable */ + varh = vpi_scan(argv); + type = vpi_get(vpiType, varh); + dest_size = vpi_get(vpiSize, varh); + + /* Get the format (see enum format_t) */ + formath = vpi_scan(argv); + val.format = vpiIntVal; + vpi_get_value(formath, &val); + format = val.value.integer; + vpi_free_object(argv); + + switch(format) { + case FORMAT_STD: + switch(type) { + /* TODO longint is 64-bit, so it has to be handled by vector */ + /*case vpiLongIntVar:*/ + case vpiShortIntVar: + case vpiIntVar: + case vpiByteVar: + case vpiIntegerVar: + val.format = vpiIntVal; + if(sscanf(string, "%d%n", &val.value.integer, &processed_chars) != 1) + fail = 1; + break; + + case vpiBitVar: /* bit, bit vector */ + case vpiLogicVar: /* ==vpiReg time, logic, logic vector */ + processed_chars = read_vector(string, &val, varh); + break; + + case vpiRealVar: + val.format = vpiRealVal; + if(sscanf(string, "%lf%n", &val.value.real, &processed_chars) != 1) + fail = 1; + break; + + case vpiStringVar: + processed_chars = read_string(string, &val, dest_size / 8); + break; + + default: + fail = 1; + show_warning_line(callh); + vpi_printf("%s does not handle such type (%d).\n", name, type); + break; + } + break; + + case FORMAT_BOOL: + { + char buf[5]; + + val.format = vpiIntVal; + if(sscanf(string, "%5s%n", buf, &processed_chars) == 1) + { + if(!strncasecmp(buf, "true", 4)) + val.value.integer = 1; + else if(!strncasecmp(buf, "false", 5)) + val.value.integer = 0; + else + fail = 1; + } + } + break; + + case FORMAT_TIME: + val.format = vpiIntVal; + processed_chars = read_time(string, &val, vpi_get(vpiTimeUnit, callh)); + break; + + case FORMAT_HEX: + val.format = vpiIntVal; + if(sscanf(string, "%x%n", &val.value.integer, &processed_chars) != 1) + fail = 1; + break; + + case FORMAT_STRING: + processed_chars = read_string(string, &val, dest_size / 8); + break; + } + + if(processed_chars == 0) { + show_error_line(callh); + vpi_printf("%s could not read a valid value.\n", name); + fail = 1; + } else if(val.format == vpiStringVar && processed_chars == STRING_BUF_SIZE) { + show_warning_line(callh); + vpi_printf("%s has reached the buffer limit, part of the " + "processed string might have been skipped.\n", name); + } + + if(!fail) { + assert(processed_chars > 0); + + /* Store the read value */ + vpi_put_value(varh, &val, 0, vpiNoDelay); + + /* Clean up */ + if(val.format == vpiStringVal) + free(val.value.str); + else if(val.format == vpiVectorVal) + free(val.value.vector); + + /* Strip the read token from the string */ + char* tmp = strdup(&string[processed_chars]); + val.format = vpiStringVal; + val.value.str = tmp; + vpi_put_value(stringh, &val, 0, vpiNoDelay); + free(tmp); + } else { + show_error_line(callh); + vpi_printf("%s failed.\n", name); + /*vpi_control(vpiFinish, 1);*/ + } + + free(string); + + return 0; +} + +static PLI_INT32 ivlh_write_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + if(argv == 0) { + show_error_line(callh); + vpi_printf("%s requires three arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + arg = vpi_scan(argv); + if(!arg || !is_string_obj(arg)) { + show_error_line(callh); + vpi_printf("%s's first argument must be a string.\n", name); + vpi_control(vpiFinish, 1); + } + + arg = vpi_scan(argv); + if(!arg) { + show_error_line(callh); + vpi_printf("%s requires three arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + arg = vpi_scan(argv); + if(!arg) { + show_error_line(callh); + vpi_printf("%s's third argument must be an integer.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "three arguments", 0); + + return 0; +} + +/*procedure WRITE (L: inout LINE; + VALUE: in BIT/BIT_VECTOR/BOOLEAN/CHARACTER/INTEGER/REAL/STRING/TIME); + JUSTIFIED: in SIDE:= RIGHT; FIELD: in WIDTH := 0); */ +/* JUSTIFIED & FIELD are not handled at the moment */ +static PLI_INT32 ivlh_write_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle stringh, varh, formath; + s_vpi_value val; + PLI_INT32 type, format; + char *string = 0; + unsigned int fail = 0, res = 0; + char buf[STRING_BUF_SIZE]; + + /* Get the string */ + stringh = vpi_scan(argv); + val.format = vpiStringVal; + vpi_get_value(stringh, &val); + string = strdup(val.value.str); + + /* Get the destination variable */ + varh = vpi_scan(argv); + type = vpi_get(vpiType, varh); + + /* Get the format (see enum format_t) */ + formath = vpi_scan(argv); + val.format = vpiIntVal; + vpi_get_value(formath, &val); + format = val.value.integer; + vpi_free_object(argv); + + /* Convert constant types to variable types */ + if(type == vpiConstant) { + type = vpi_get(vpiConstType, varh); + + switch(type) { + case vpiRealConst: + type = vpiRealVar; + break; + + case vpiStringConst: + type = vpiStringVar; + break; + + case vpiDecConst: + case vpiOctConst: + case vpiHexConst: + type = vpiIntVar; + break; + + case vpiBinaryConst: + type = vpiBitVar; + break; + } + } + + switch(format) { + case FORMAT_STD: + switch(type) { + /* TODO longint is 64-bit, so it has to be handled by vector */ + /*case vpiLongIntVar:*/ + case vpiShortIntVar: + case vpiIntVar: + case vpiByteVar: + case vpiIntegerVar: + val.format = vpiIntVal; + vpi_get_value(varh, &val); + res = snprintf(buf, STRING_BUF_SIZE, "%s%d", string, val.value.integer); + break; + + case vpiBitVar: /* bit, bit vector */ + case vpiLogicVar: /* ==vpiReg time, logic, logic vector */ + val.format = vpiBinStrVal; + vpi_get_value(varh, &val); + + /* VHDL stores X/Z values uppercase, so follow the rule */ + for(size_t i = 0; i< strlen(val.value.str); ++i) + val.value.str[i] = toupper(val.value.str[i]); + + res = snprintf(buf, STRING_BUF_SIZE, "%s%s", string, val.value.str); + break; + + case vpiRealVar: + val.format = vpiRealVal; + vpi_get_value(varh, &val); + res = snprintf(buf, STRING_BUF_SIZE, "%s%lf", string, val.value.real); + break; + + case vpiStringVar: + val.format = vpiStringVal; + vpi_get_value(varh, &val); + res = snprintf(buf, STRING_BUF_SIZE, "%s%s", string, val.value.str); + break; + + default: + fail = 1; + show_warning_line(callh); + vpi_printf("%s does not handle such type (%d).\n", name, type); + break; + } + break; + + case FORMAT_BOOL: + val.format = vpiIntVal; + vpi_get_value(varh, &val); + res = snprintf(buf, STRING_BUF_SIZE, "%s%s", string, + val.value.integer ? "TRUE" : "FALSE"); + break; + + case FORMAT_TIME: + { + char tmp[64]; + + val.format = vpiIntVal; + vpi_get_value(varh, &val); + + if(write_time(tmp, &val, vpi_get(vpiSize, varh), vpi_get(vpiTimeUnit, callh))) { + fail = 1; + break; + } + + res = snprintf(buf, STRING_BUF_SIZE, "%s%s", string, tmp); + } + break; + + case FORMAT_HEX: + val.format = vpiIntVal; + vpi_get_value(varh, &val); + res = snprintf(buf, STRING_BUF_SIZE, "%s%X", string, val.value.integer); + break; + + case FORMAT_STRING: + val.format = vpiStringVal; + vpi_get_value(varh, &val); + res = snprintf(buf, STRING_BUF_SIZE, "%s%s", string, val.value.str); + break; + } + + if(res >= STRING_BUF_SIZE) + fail = 1; + + if(!fail) { + /* Strip the read token from the string */ + char* tmp = strdup(buf); + val.format = vpiStringVal; + val.value.str = tmp; + vpi_put_value(stringh, &val, 0, vpiNoDelay); + free(tmp); + } else { + show_error_line(callh); + vpi_printf("%s failed.\n", name); + /*vpi_control(vpiFinish, 1);*/ + } + + free(string); + + return 0; +} + +static void vhdl_register(void) +{ + vpiHandle res; + + s_vpi_systf_data tf_data[] = { + { vpiSysTask, 0, "$ivlh_file_open", + ivlh_file_open_calltf, ivlh_file_open_compiletf, 0, + "$ivlh_file_open" }, + + { vpiSysTask, 0, "$ivlh_readline", + ivlh_readline_calltf, ivlh_readwriteline_compiletf, 0, + "$ivlh_readline" }, + + { vpiSysTask, 0, "$ivlh_writeline", + ivlh_writeline_calltf, ivlh_readwriteline_compiletf, 0, + "$ivlh_writeline" }, + + { vpiSysTask, 0, "$ivlh_read", + ivlh_read_calltf, ivlh_read_compiletf, 0, + "$ivlh_read" }, + + { vpiSysTask, 0, "$ivlh_write", + ivlh_write_calltf, ivlh_write_compiletf, 0, + "$ivlh_write" }, + }; + + for(unsigned int i = 0; i < sizeof(tf_data) / sizeof(s_vpi_systf_data); ++i) { + res = vpi_register_systf(&tf_data[i]); + vpip_make_systf_system_defined(res); + } +} + +void (*vlog_startup_routines[])(void) = { + vhdl_register, + 0 +}; diff --git a/vpi/vhdl_textio.sft b/vpi/vhdl_textio.sft new file mode 100644 index 000000000..f2fe46cf1 --- /dev/null +++ b/vpi/vhdl_textio.sft @@ -0,0 +1,9 @@ +$ivlh_file_open vpiSysFuncVoid + +$ivlh_readline vpiSysFuncVoid +$ivlh_writeline vpiSysFuncVoid + +$ivlh_read vpiSysFuncVoid +$ivlh_write vpiSysFuncVoid +$ivlh_hread vpiSysFuncVoid +$ivlh_hwrite vpiSysFuncVoid diff --git a/vpi/wavealloca.h b/vpi/wavealloca.h index 4be2e4937..86276648f 100644 --- a/vpi/wavealloca.h +++ b/vpi/wavealloca.h @@ -1,10 +1,23 @@ /* - * Copyright (c) Tony Bybell 1999. + * Copyright (c) 1999 Tony Bybell. * - * 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 the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ #ifndef WAVE_ALLOCA_H diff --git a/vpi_user.h b/vpi_user.h index 7bef96669..db0a9bd5f 100644 --- a/vpi_user.h +++ b/vpi_user.h @@ -1,7 +1,7 @@ #ifndef VPI_USER_H #define VPI_USER_H /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2018 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 @@ -46,7 +46,9 @@ EXTERN_C_START # include # include "_pli_types.h" +#ifndef ICARUS_VPI_CONST #define ICARUS_VPI_CONST +#endif #ifdef __cplusplus typedef class __vpiHandle *vpiHandle; #else @@ -63,7 +65,7 @@ typedef struct t_vpi_systf_data { const char *tfname; PLI_INT32 (*calltf) (ICARUS_VPI_CONST PLI_BYTE8*); PLI_INT32 (*compiletf)(ICARUS_VPI_CONST PLI_BYTE8*); - PLI_INT32 (*sizetf) (PLI_BYTE8*); + PLI_INT32 (*sizetf) (ICARUS_VPI_CONST PLI_BYTE8*); ICARUS_VPI_CONST PLI_BYTE8 *user_data; } s_vpi_systf_data, *p_vpi_systf_data; @@ -421,10 +423,17 @@ extern PLI_UINT32 vpi_mcd_open(char *name); extern PLI_UINT32 vpi_mcd_close(PLI_UINT32 mcd); extern char *vpi_mcd_name(PLI_UINT32 mcd); extern PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, const char*fmt, ...) +#ifdef __MINGW32__ + __attribute__((format (gnu_printf,2,3))); +#else __attribute__((format (printf,2,3))); - +#endif extern PLI_INT32 vpi_printf(const char*fmt, ...) +#ifdef __MINGW32__ + __attribute__((format (gnu_printf,1,2))); +#else __attribute__((format (printf,1,2))); +#endif extern PLI_INT32 vpi_vprintf(const char*fmt, va_list ap); extern PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, const char*fmt, va_list ap); @@ -459,7 +468,7 @@ typedef struct t_cb_data { p_vpi_time time; p_vpi_value value; PLI_INT32 index; - char *user_data; + ICARUS_VPI_CONST PLI_BYTE8 *user_data; } s_cb_data, *p_cb_data; #define cbValueChange 1 @@ -625,6 +634,10 @@ extern void vpip_set_return_value(int value); extern s_vpi_vecval vpip_calc_clog2(vpiHandle arg); extern void vpip_make_systf_system_defined(vpiHandle ref); + /* Perform fwrite to mcd files. This is used to write raw data, + which may include nulls. */ +extern void vpip_mcd_rawwrite(PLI_UINT32 mcd, const char*buf, size_t count); + /* Return driver information for a net bit. The information is returned in the 'counts' array as follows: counts[0] - number of drivers driving '0' onto the net diff --git a/vvp/Makefile.in b/vvp/Makefile.in index 35750f69d..d5a1c7f92 100644 --- a/vvp/Makefile.in +++ b/vvp/Makefile.in @@ -55,7 +55,7 @@ else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif -CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ +CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ -DICARUS_VPI_CONST=const CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ LDFLAGS = @rdynamic@ @LDFLAGS@ @@ -72,7 +72,7 @@ V = vpi_modules.o vpi_callback.o vpi_cobject.o vpi_const.o vpi_darray.o \ vpip_to_dec.o vpip_format.o vvp_vpi.o O = main.o parse.o parse_misc.o lexor.o arith.o array_common.o array.o bufif.o compile.o \ - concat.o dff.o class_type.o enum_type.o extend.o file_line.o npmos.o part.o \ + concat.o dff.o class_type.o enum_type.o extend.o file_line.o latch.o npmos.o part.o \ permaheap.o reduce.o resolv.o \ sfunc.o stop.o \ substitute.o \ diff --git a/vvp/README.txt b/vvp/README.txt index 27827d9eb..dea23ae1a 100644 --- a/vvp/README.txt +++ b/vvp/README.txt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2017 Stephen Williams (steve@icarus.com) * */ @@ -97,24 +97,33 @@ SCOPE STATEMENTS: The syntax of a scope statement is: -