diff --git a/HName.h b/HName.h index 87903d087..6008a0d33 100644 --- a/HName.h +++ b/HName.h @@ -57,7 +57,7 @@ class hname_t { private: perm_string name_; - // If the number is anything other then INT_MIN, then this is + // 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. int number_; diff --git a/Makefile.in b/Makefile.in index 3df9bb21b..a5be31f9b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -186,11 +186,14 @@ iverilog-vpi.pdf: iverilog-vpi.ps # version.h file in the source tree (included in snapshots and releases), and # finally use nothing. .PHONY: version.h -version.h: +# "true" and "false" in the next few lines are Unix shell command names ifeq ($(GIT),none) - @echo '#define VERSION_TAG ""' > $@; +GIT_PRESENT = false else - @if test -d $(srcdir)/.git; then \ +GIT_PRESENT = true +endif +version.h: + @if $(GIT_PRESENT) && test -d $(srcdir)/.git; then \ echo "Using git-describe for VERSION_TAG"; \ tmp=`$(GIT) --git-dir $(srcdir)/.git describe \ | sed -e 's;\(.*\);#define VERSION_TAG "\1";'`; \ @@ -202,7 +205,6 @@ else echo "Using empty VERSION_TAG"; \ echo '#define VERSION_TAG ""' > $@; \ fi -endif ifeq (@MINGW32@,yes) ifeq ($(MAN),none) diff --git a/Module.cc b/Module.cc index 961940149..0b8a43a90 100644 --- a/Module.cc +++ b/Module.cc @@ -26,7 +26,7 @@ /* n is a permallocated string. */ Module::Module(perm_string n) -: PScope(n, 0) +: PScope(n) { library_flag = false; default_nettype = NetNet::NONE; @@ -41,16 +41,6 @@ void Module::add_gate(PGate*gate) gates_.push_back(gate); } -void Module::add_task(perm_string name, PTask*task) -{ - tasks_[name] = task; -} - -void Module::add_function(perm_string name, PFunction *func) -{ - funcs_[name] = func; -} - unsigned Module::port_count() const { return ports.count(); diff --git a/Module.h b/Module.h index daca15078..ef8816cf4 100644 --- a/Module.h +++ b/Module.h @@ -94,7 +94,7 @@ class Module : public PScope, public LineInfo { module is elaborated. During parsing, I put the parameters into this map. */ struct param_expr_t : public LineInfo { - param_expr_t() : range(0) { } + param_expr_t() : type(IVL_VT_NO_TYPE), msb(0), lsb(0), signed_flag(false), expr(0), range(0) { } // Type information ivl_variable_type_t type; PExpr*msb; @@ -102,7 +102,7 @@ class Module : public PScope, public LineInfo { bool signed_flag; // Value expression PExpr*expr; - // If there are range constrants, list them here + // If there are range constraints, list them here range_t*range; }; mapparameters; @@ -138,6 +138,10 @@ class Module : public PScope, public LineInfo { set by the `timescale directive. */ int time_unit, time_precision; + /* Task definitions within this module */ + map tasks; + map funcs; + /* The module has a list of genvars that may be used in various generate schemes. */ map genvars; @@ -152,8 +156,6 @@ class Module : public PScope, public LineInfo { perm_string mod_name() const { return pscope_name(); } void add_gate(PGate*gate); - void add_task(perm_string name, PTask*def); - void add_function(perm_string name, PFunction*def); unsigned port_count() const; const svector& get_port(unsigned idx) const; @@ -173,8 +175,6 @@ class Module : public PScope, public LineInfo { private: list gates_; - map tasks_; - map funcs_; static void elaborate_parm_item_(perm_string name, const param_expr_t&cur, Design*des, NetScope*scope); diff --git a/PExpr.h b/PExpr.h index 0d0b66f33..08dccdd29 100644 --- a/PExpr.h +++ b/PExpr.h @@ -306,10 +306,16 @@ class PEIdent : public PExpr { private: // Common functions to calculate parts of part/bit selects. bool calculate_parts_(Design*, NetScope*, long&msb, long&lsb) const; + NetExpr* calculate_up_do_base_(Design*, NetScope*) const; + bool calculate_param_range_(Design*, NetScope*, + const NetExpr*msb_ex, long&msb, + const NetExpr*lsb_ex, long&lsb) const; + bool calculate_up_do_width_(Design*, NetScope*, unsigned long&wid) const; private: NetAssign_*elaborate_lval_net_word_(Design*, NetScope*, NetNet*) const; + bool elaborate_lval_net_bit_(Design*, NetScope*, NetAssign_*) const; bool elaborate_lval_net_part_(Design*, NetScope*, NetAssign_*) const; bool elaborate_lval_net_idx_(Design*, NetScope*, NetAssign_*, index_component_t::ctype_t) const; @@ -321,6 +327,18 @@ class PEIdent : public PExpr { NetScope*found, const NetExpr*par_msb, const NetExpr*par_lsb) const; + NetExpr*elaborate_expr_param_part_(Design*des, + NetScope*scope, + const NetExpr*par, + NetScope*found, + const NetExpr*par_msb, + const NetExpr*par_lsb) const; + NetExpr*elaborate_expr_param_idx_up_(Design*des, + NetScope*scope, + const NetExpr*par, + NetScope*found, + const NetExpr*par_msb, + const NetExpr*par_lsb) const; NetExpr*elaborate_expr_net(Design*des, NetScope*scope, NetNet*net, diff --git a/PGenerate.cc b/PGenerate.cc index 03fcfb286..a769501e7 100644 --- a/PGenerate.cc +++ b/PGenerate.cc @@ -27,27 +27,14 @@ PGenerate::PGenerate(unsigned id) : id_number(id) { parent = 0; + lexical_scope = 0; } PGenerate::~PGenerate() { } -PWire* PGenerate::get_wire(perm_string name) const -{ - map::const_iterator obj = wires.find(name); - if (obj == wires.end()) - return 0; - else - return (*obj).second; -} - void PGenerate::add_gate(PGate*gate) { gates.push_back(gate); } - -void PGenerate::add_behavior(PProcess*proc) -{ - behaviors.push_back(proc); -} diff --git a/PGenerate.h b/PGenerate.h index 842e0c49a..d88115533 100644 --- a/PGenerate.h +++ b/PGenerate.h @@ -1,7 +1,7 @@ #ifndef __PGenerate_H #define __PGenerate_H /* - * Copyright (c) 2006 Stephen Williams (steve@icarus.com) + * Copyright (c) 2006-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -18,13 +18,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: PGenerate.h,v 1.4 2007/06/02 03:42:12 steve Exp $" -#endif # include "LineInfo.h" # include "StringHeap.h" # include "HName.h" +# include "PScope.h" # include # include # include "pform_types.h" @@ -32,7 +30,9 @@ class Design; class NetScope; class PExpr; +class PFunction; class PProcess; +class PTask; class PGate; class PWire; @@ -49,10 +49,10 @@ class PWire; * The parent points to the GS_CASE that contains this item. * the loop_test is compared with the parent->loop_test expression. */ -class PGenerate : public LineInfo { +class PGenerate : public LineInfo, public LexicalScope { public: - PGenerate(unsigned id_number); + explicit PGenerate(unsigned id_number); ~PGenerate(); // Generate schemes have an ID number, for when the scope is @@ -60,6 +60,10 @@ class PGenerate : public LineInfo { const unsigned id_number; perm_string scope_name; + // This is used during parsing to stack lexical scopes within + // this generate scheme. + PScope*lexical_scope; + enum scheme_t {GS_NONE, GS_LOOP, GS_CONDIT, GS_ELSE, GS_CASE, GS_CASE_ITEM}; scheme_t scheme_type; @@ -71,16 +75,17 @@ class PGenerate : public LineInfo { PExpr*loop_test; PExpr*loop_step; - mapwires; - PWire* get_wire(perm_string name) const; - list gates; void add_gate(PGate*); list behaviors; - void add_behavior(PProcess*behave); - list generates; + // Tasks instantiated within this scheme. + map tasks; + mapfuncs; + + // Generate schemes can contain further generate schemes. + list generate_schemes; PGenerate*parent; // This method is called by the elaboration of a module to diff --git a/PScope.cc b/PScope.cc index addd4ea4f..92022fa26 100644 --- a/PScope.cc +++ b/PScope.cc @@ -19,8 +19,13 @@ # include "PScope.h" -PScope::PScope(perm_string n, PScope*p) - : name_(n), parent_(p) +PScope::PScope(perm_string n, PScope*parent) +: name_(n), parent_(parent) +{ +} + +PScope::PScope(perm_string n) +: name_(n), parent_(0) { } @@ -28,7 +33,7 @@ PScope::~PScope() { } -PWire* PScope::wires_find(perm_string name) +PWire* LexicalScope::wires_find(perm_string name) { map::const_iterator cur = wires.find(name); if (cur == wires.end()) diff --git a/PScope.h b/PScope.h index b82079974..d65244a03 100644 --- a/PScope.h +++ b/PScope.h @@ -38,7 +38,22 @@ class NetScope; * NOTE: This is note the same concept as the "scope" of an elaborated * hierarchy. That is represented by NetScope objects after elaboration. */ -class PScope { + +class LexicalScope { + + public: + explicit LexicalScope() { } + // A virtual destructor is so that dynamic_cast can work. + virtual ~LexicalScope() { } + + // Nets an variables (wires) in the scope + mapwires; + PWire* wires_find(perm_string name); + + private: +}; + +class PScope : public LexicalScope { public: // When created, a scope has a name and a parent. The name is @@ -50,15 +65,12 @@ class PScope { // modules. Scopes for tasks and functions point to their // containing module. PScope(perm_string name, PScope*parent); + PScope(perm_string name); virtual ~PScope(); perm_string pscope_name() const { return name_; } PScope* pscope_parent() { return parent_; } - // Nets an variables (wires) in the scope - mapwires; - PWire* wires_find(perm_string name); - // Named events in the scope. mapevents; diff --git a/Statement.cc b/Statement.cc index 1ab66ec35..02680d322 100644 --- a/Statement.cc +++ b/Statement.cc @@ -92,7 +92,7 @@ PBlock::PBlock(perm_string n, PScope*parent, BL_TYPE t) } PBlock::PBlock(BL_TYPE t) -: PScope(perm_string(),0), bl_type_(t) +: PScope(perm_string()), bl_type_(t) { } diff --git a/design_dump.cc b/design_dump.cc index b191a25fe..ac6748521 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -109,6 +109,9 @@ ostream& operator << (ostream&o, ivl_switch_type_t val) case IVL_SW_RTRANIF1: o << "rtranif1"; break; + case IVL_SW_TRAN_VP: + o << "tran(VP)"; + break; } return o; } @@ -299,6 +302,22 @@ void NetArrayDq::dump_node(ostream&o, unsigned ind) const dump_obj_attr(o, ind+4); } +void NetCastInt::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "Cast to int. (NetCastInt): " << + name() << " width=" << width() << endl; + dump_node_pins(o, ind+4); + dump_obj_attr(o, ind+4); +} + +void NetCastReal::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "Cast to real (NetCastReal): " << + name() << endl; + dump_node_pins(o, ind+4); + dump_obj_attr(o, ind+4); +} + void NetCLShift::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "Combinatorial shift (NetCLShift): " << @@ -518,9 +537,6 @@ void NetPartSelect::dump_node(ostream&o, unsigned ind) const case PV: pt = "PV"; break; - case BI: - pt = "BI"; - break; } o << setw(ind) << "" << "NetPartSelect(" << pt << "): " << name(); @@ -638,7 +654,14 @@ void NetTaskDef::dump(ostream&o, unsigned ind) const void NetTran::dump_node(ostream&o, unsigned ind) const { - o << setw(ind) << "" << type_ << " " << name() << endl; + o << setw(ind) << "" << type_ << " " << name() + << " island " << island; + if (type_ == IVL_SW_TRAN_VP) { + o << " width=" << vector_width() + << " part=" << part_width() + << " offset=" << part_offset(); + } + o << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } diff --git a/driver/main.c b/driver/main.c index 55fac84b3..38c4d1bba 100644 --- a/driver/main.c +++ b/driver/main.c @@ -328,23 +328,24 @@ static int t_default(char*cmd, unsigned ncmd) remove(compiled_defines_path); } #ifdef __MINGW32__ /* MinGW just returns the exit status, so return it! */ + free(cmd); return rc; #else - + int rtn = 0; if (rc != 0) { if (rc == 127) { fprintf(stderr, "Failed to execute: %s\n", cmd); - return 1; - } - - if (WIFEXITED(rc)) - return WEXITSTATUS(rc); - - fprintf(stderr, "Command signaled: %s\n", cmd); - return -1; + rtn = 1; + } else if (WIFEXITED(rc)) { + rtn = WEXITSTATUS(rc); + } else { + fprintf(stderr, "Command signaled: %s\n", cmd); + rtn = -1; + } } - return 0; + free(cmd); + return rtn; #endif } @@ -505,7 +506,7 @@ int process_generation(const char*name) " 2005 -- IEEE1364-2005\n" "Other generation flags:\n" " specify | no-specify\n" - " verilog-ams | no-verinlog-ams\n" + " verilog-ams | no-verilog-ams\n" " std-include | no-std-include\n" " xtypes | no-xtypes\n" " icarus-misc | no-icarus-misc\n" @@ -871,6 +872,7 @@ int main(int argc, char **argv) } fprintf(stderr, "Command signaled: %s\n", cmd); + free(cmd); return -1; } @@ -889,6 +891,4 @@ int main(int argc, char **argv) fclose(iconfig_file); return t_default(cmd, ncmd); - - return 0; } diff --git a/elab_expr.cc b/elab_expr.cc index 90c1937fc..2939b91d9 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -716,6 +716,15 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, des->errors += 1; } + if (!rep->value().is_defined()) { + cerr << get_fileline() << ": error: Concatenation repeat " + << "may not be undefined (" << rep->value() + << ")." << endl; + des->errors += 1; + concat_depth -= 1; + return 0; + } + if (rep->value().is_negative()) { cerr << get_fileline() << ": error: Concatenation repeat " << "may not be negative (" << rep->value().as_long() @@ -873,6 +882,51 @@ bool PEIdent::calculate_up_do_width_(Design*des, NetScope*scope, return flag; } +/* + * When we know that this is an indexed part select (up or down) this + * method calculates the up/down base, as far at it can be calculated. + */ +NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope) const +{ + const name_component_t&name_tail = path_.back(); + ivl_assert(*this, !name_tail.index.empty()); + + const index_component_t&index_tail = name_tail.index.back(); + ivl_assert(*this, index_tail.lsb != 0); + ivl_assert(*this, index_tail.msb != 0); + + NetExpr*tmp = elab_and_eval(des, scope, index_tail.msb, -1); + return tmp; +} + +bool PEIdent::calculate_param_range_(Design*des, NetScope*scope, + const NetExpr*par_msb, long&par_msv, + const NetExpr*par_lsb, long&par_lsv) const +{ + if (par_msb == 0) { + // If the parameter doesn't have an explicit range, then + // just return range values of 0:0. The par_msv==0 is + // correct. The par_msv is not necessarily correct, but + // clients of this function don't need a correct value. + ivl_assert(*this, par_lsb == 0); + par_msv = 0; + par_lsv = 0; + return true; + } + + const NetEConst*tmp = dynamic_cast (par_msb); + ivl_assert(*this, tmp); + + par_msv = tmp->value().as_long(); + + tmp = dynamic_cast (par_lsb); + ivl_assert(*this, tmp); + + par_lsv = tmp->value().as_long(); + + return true; +} + unsigned PEIdent::test_width(Design*des, NetScope*scope, unsigned min, unsigned lval, bool&unsized_flag) const @@ -1085,6 +1139,131 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, return 0; } +static verinum param_part_select_bits(const verinum&par_val, long wid, + long lsv, long par_lsv) +{ + verinum result (verinum::Vx, wid, true); + + for (long idx = 0 ; idx < wid ; idx += 1) { + long off = idx + lsv - par_lsv; + if (off < 0) + result.set(idx, verinum::Vx); + else if (off < (long)par_val.len()) + result.set(idx, par_val.get(off)); + else if (par_val.is_string()) // Pad strings with nulls. + result.set(idx, verinum::V0); + else if (par_val.has_len()) // Pad sized parameters with X + result.set(idx, verinum::Vx); + else // Unsized parameters are "infinite" width. + result.set(idx, sign_bit(par_val)); + } + + // 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-par_lsv)%8 == 0) && (wid%8 == 0)) + return result.as_string(); + + return result; +} + +NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope, + const NetExpr*par, + NetScope*found_in, + const NetExpr*par_msb, + const NetExpr*par_lsb) const +{ + long msv, lsv; + bool flag = calculate_parts_(des, scope, msv, lsv); + if (!flag) + return 0; + + long par_msv, par_lsv; + flag = calculate_param_range_(des, scope, par_msb, par_msv, par_lsb, par_lsv); + if (!flag) + return 0; + + // Notice that the par_msv is not used in this function other + // than for this test. It is used to tell the direction that + // the bits are numbers, so that we can make sure the + // direction matches the part select direction. After that, + // we only need the par_lsv. + if ((msv>lsv && par_msv=par_lsv)) { + cerr << get_fileline() << ": error: Part select " + << "[" << msv << ":" << lsv << "] is out of order." << endl; + des->errors += 1; + return 0; + } + + long wid = 1 + labs(msv-lsv); + + if (debug_elaborate) + cerr << get_fileline() << ": debug: Calculate part select " + << "[" << msv << ":" << lsv << "] from range " + << "[" << par_msv << ":" << par_lsv << "]." << endl; + + const NetEConst*par_ex = dynamic_cast (par); + ivl_assert(*this, par_ex); + + verinum result = param_part_select_bits(par_ex->value(), wid, lsv, par_lsv); + NetEConst*result_ex = new NetEConst(result); + result_ex->set_line(*this); + + return result_ex; +} + +NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, + const NetExpr*par, + NetScope*found_in, + const NetExpr*par_msb, + const NetExpr*par_lsb) const +{ + + long par_msv, par_lsv; + bool flag = calculate_param_range_(des, scope, par_msb, par_msv, par_lsb, par_lsv); + if (!flag) + return 0; + + NetExpr*base = calculate_up_do_base_(des, scope); + if (base == 0) + return 0; + + unsigned long wid = 0; + calculate_up_do_width_(des, scope, wid); + + const NetEConst*par_ex = dynamic_cast (par); + ivl_assert(*this, par_ex); + + if (debug_elaborate) + cerr << get_fileline() << ": debug: Calculate part select " + << "[" << *base << "+:" << wid << "] from range " + << "[" << par_msv << ":" << par_lsv << "]." << endl; + + // Handle the special case that the base is constant. In this + // case, just precalculate the entire constant result. + if (NetEConst*base_c = dynamic_cast (base)) { + long lsv = base_c->value().as_long(); + + // Watch out for reversed bit numbering. We're making + // the part select from LSB to MSB. + if (par_msv < par_lsv) + lsv = lsv - wid + 1; + + verinum result = param_part_select_bits(par_ex->value(), wid, + lsv, par_lsv); + NetEConst*result_ex = new NetEConst(result); + result_ex->set_line(*this); + return result_ex; + } + + if ((par_msb < par_lsb) && (wid>1)) + base = make_add_expr(base, 1-(long)wid); + + NetExpr*tmp = par->dup_expr(); + tmp = new NetESelect(tmp, base, wid); + tmp->set_line(*this); + return tmp; +} + /* * Handle the case that the identifier is a parameter reference. The * parameter expression has already been located for us (as the par @@ -1097,84 +1276,29 @@ NetExpr* PEIdent::elaborate_expr_param(Design*des, const NetExpr*par_msb, const NetExpr*par_lsb) const { - NetExpr*tmp = par->dup_expr(); - const name_component_t&name_tail = path_.back(); index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; if (!name_tail.index.empty()) use_sel = name_tail.index.back().sel; - if (use_sel == index_component_t::SEL_PART) { - ivl_assert(*this, !name_tail.index.empty()); - const index_component_t&index_tail = name_tail.index.back(); - ivl_assert(*this, index_tail.msb); - ivl_assert(*this, index_tail.lsb); + // NOTE TO SELF: This is the way I want to see this code + // structured. This closely follows the structure of the + // elaborate_expr_net_ code, which splits all the various + // selects to different methods. + if (use_sel == index_component_t::SEL_PART) + return elaborate_expr_param_part_(des, scope, par, found_in, + par_msb, par_lsb); - /* If the identifier has a part select, we support - it by pulling the right bits out and making a - sized unsigned constant. This code assumes the - lsb of a parameter is 0 and the msb is the - width of the parameter. */ + if (use_sel == index_component_t::SEL_IDX_UP) + return elaborate_expr_param_idx_up_(des, scope, par, found_in, + par_msb, par_lsb); - verinum*lsn = index_tail.lsb->eval_const(des, scope); - verinum*msn = index_tail.msb->eval_const(des, scope); - if ((lsn == 0) || (msn == 0)) { - cerr << get_fileline() << ": error: " - "Part select expressions must be " - "constant expressions." << endl; - des->errors += 1; - return 0; - } + // NOTE TO SELF (continued): The code below should be + // rewritten in the above format, as I get to it. - long lsb = lsn->as_long(); - long msb = msn->as_long(); - if ((lsb < 0) || (msb < lsb)) { - cerr << get_fileline() << ": error: invalid part " - << "select: " << path_ - << "["<errors += 1; - return 0; - } - unsigned long ulsb=lsb; - unsigned long umsb=msb; + NetExpr*tmp = par->dup_expr(); - NetEConst*le = dynamic_cast(tmp); - assert(le); - - verinum result (verinum::V0, msb-lsb+1, true); - verinum exl = le->value(); - - /* Pull the bits from the parameter, one at a - time. If the bit is within the range, simply - copy it to the result. If the bit is outside - the range, we sign extend signed unsized - numbers, zero extend unsigned unsigned numbers, - and X extend sized numbers. */ - for (unsigned long idx = ulsb ; idx <= umsb ; idx += 1) { - if (idx < exl.len()) - result.set(idx-lsb, exl.get(idx)); - else if (exl.is_string()) - result.set(idx-lsb, verinum::V0); - else if (exl.has_len()) - result.set(idx-lsb, verinum::Vx); - else if (exl.has_sign()) - result.set(idx-lsb, exl.get(exl.len()-1)); - else - result.set(idx-lsb, verinum::V0); - } - - /* If the input is a string, and the part select - is working on byte boundaries, then the result - can be made into a string. */ - if (exl.is_string() - && (lsb%8 == 0) - && (result.len()%8 == 0)) - result = verinum(result.as_string()); - - delete tmp; - tmp = new NetEConst(result); - - } else if (use_sel == index_component_t::SEL_IDX_UP || use_sel == index_component_t::SEL_IDX_DO) { + if (use_sel == index_component_t::SEL_IDX_DO) { ivl_assert(*this, !name_tail.index.empty()); const index_component_t&index_tail = name_tail.index.back(); @@ -1249,7 +1373,7 @@ NetExpr* PEIdent::elaborate_expr_param(Design*des, par_mv = par_me->value().as_long(); par_lv = par_le->value().as_long(); } - /* Convert the index to cannonical bit address. */ + /* Convert the index to canonical bit address. */ long ridx = rv.as_long(); if (par_mv >= par_lv) { ridx -= par_lv; @@ -1361,8 +1485,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, // Special case: The index is out of range, so the value // of this expression is a 'bx vector the width of a word. if (!net->array_index_is_valid(addr)) { - verinum xxx (verinum::Vx, net->vector_width()); - NetEConst*resx = new NetEConst(xxx); + NetEConst*resx = make_const_x(net->vector_width()); resx->set_line(*this); delete word_index; return resx; @@ -1428,16 +1551,7 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, negative values. However, the width that they represent is unsigned. Remember that any order is possible, i.e., [1:0], [-4:6], etc. */ - unsigned long wid = 1 + ((msv>lsv)? (msv-lsv) : (lsv-msv)); - if (wid > net->vector_width()) { - cerr << get_fileline() << ": error: part select [" - << msv << ":" << lsv << "] out of range." << endl; - des->errors += 1; - //delete lsn; - //delete msn; - return net; - } - ivl_assert(*this, wid <= net->vector_width()); + unsigned long wid = 1 + labs(msv-lsv); if (net->sig()->sb_to_idx(msv) < net->sig()->sb_to_idx(lsv)) { cerr << get_fileline() << ": error: part select [" @@ -1448,26 +1562,29 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, return net; } - - if (net->sig()->sb_to_idx(msv) >= (signed) net->vector_width()) { - cerr << get_fileline() << ": error: part select [" - << msv << ":" << lsv << "] out of range." << endl; - des->errors += 1; - //delete lsn; - //delete msn; - return net; - } + long sb_lsb = net->sig()->sb_to_idx(lsv); + long sb_msb = net->sig()->sb_to_idx(msv); // If the part select covers exactly the entire // vector, then do not bother with it. Return the - // signal itself. - if (net->sig()->sb_to_idx(lsv) == 0 && wid == net->vector_width()) + // signal itself, casting to unsigned if necessary. + if (sb_lsb == 0 && wid == net->vector_width()) { + net->cast_signed(false); return net; + } - NetExpr*ex = new NetEConst(verinum(net->sig()->sb_to_idx(lsv))); + // If the part select covers NONE of the vector, then return a + // constant X. + + if ((sb_lsb >= (signed) net->vector_width()) || (sb_msb < 0)) { + NetEConst*tmp = make_const_x(wid); + tmp->set_line(*this); + return tmp; + } + + NetExpr*ex = new NetEConst(verinum(sb_lsb)); NetESelect*ss = new NetESelect(net, ex, wid); ss->set_line(*this); - return ss; } @@ -1477,14 +1594,7 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, NetESignal*net, NetScope*found_in) const { - const name_component_t&name_tail = path_.back(); - ivl_assert(*this, !name_tail.index.empty()); - - const index_component_t&index_tail = name_tail.index.back(); - ivl_assert(*this, index_tail.lsb != 0); - ivl_assert(*this, index_tail.msb != 0); - - NetExpr*base = elab_and_eval(des, scope, index_tail.msb, -1); + NetExpr*base = calculate_up_do_base_(des, scope); unsigned long wid = 0; calculate_up_do_width_(des, scope, wid); @@ -1609,8 +1719,7 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, /* The bit select is out of range of the vector. This is legal, but returns a constant 1'bx value. */ - verinum x (verinum::Vx); - NetEConst*tmp = new NetEConst(x); + NetEConst*tmp = make_const_x(1); tmp->set_line(*this); cerr << get_fileline() << ": warning: Bit select [" diff --git a/elab_lval.cc b/elab_lval.cc index 14402801b..174af3260 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -208,101 +208,18 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, return 0; } - long msb, lsb; - NetExpr*mux; if (use_sel == index_component_t::SEL_BIT) { - - const index_component_t&index_tail = name_tail.index.back(); - ivl_assert(*this, index_tail.msb != 0); - ivl_assert(*this, index_tail.lsb == 0); - - /* If there is only a single select expression, it is a - bit select. Evaluate the constant value and treat it - as a part select with a bit width of 1. If the - expression it not constant, then return the - expression as a mux. */ - - NetExpr*index_expr = elab_and_eval(des, scope, index_tail.msb, -1); - - if (NetEConst*index_con = dynamic_cast (index_expr)) { - msb = index_con->value().as_long(); - lsb = index_con->value().as_long(); - mux = 0; - - } else { - msb = 0; - lsb = 0; - mux = index_expr; - } - - } else { - - /* No select expressions, so presume a part select the - width of the register. */ - - msb = reg->msb(); - lsb = reg->lsb(); - mux = 0; + NetAssign_*lv = new NetAssign_(reg); + elaborate_lval_net_bit_(des, scope, lv); + return lv; } + ivl_assert(*this, use_sel == index_component_t::SEL_NONE); - NetAssign_*lv; - if (mux) { - - /* If there is a non-constant bit select, make a - NetAssign_ to the target reg and attach a - bmux to select the target bit. */ - lv = new NetAssign_(reg); - - /* Correct the mux for the range of the vector. */ - if (reg->msb() < reg->lsb()) - mux = make_sub_expr(reg->lsb(), mux); - else if (reg->lsb() != 0) - mux = make_add_expr(mux, - reg->lsb()); - - lv->set_part(mux, 1); - - } else if (msb == reg->msb() && lsb == reg->lsb()) { - - /* No bit select, and part select covers the entire - vector. Simplest case. */ - lv = new NetAssign_(reg); - - } else { - - /* If the bit/part select is constant, then make the - NetAssign_ only as wide as it needs to be and connect - only to the selected bits of the reg. */ - unsigned loff = reg->sb_to_idx(lsb); - unsigned moff = reg->sb_to_idx(msb); - unsigned wid = moff - loff + 1; - - if (moff < loff) { - cerr << get_fileline() << ": error: part select " - << reg->name() << "[" << msb<<":"<errors += 1; - return 0; - } - - /* If the part select extends beyond the extreme of the - variable, then report an error. Note that loff is - converted to normalized form so is relative the - variable pins. */ - - if ((wid + loff) > reg->vector_width()) { - cerr << get_fileline() << ": error: bit/part select " - << reg->name() << "[" << msb<<":"<errors += 1; - return 0; - } - - lv = new NetAssign_(reg); - lv->set_part(new NetEConst(verinum(loff)), wid); - } + /* No select expressions. */ + NetAssign_*lv = new NetAssign_(reg); return lv; } @@ -374,10 +291,67 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, return lv; } +bool PEIdent::elaborate_lval_net_bit_(Design*des, + NetScope*scope, + NetAssign_*lv) const +{ + const name_component_t&name_tail = path_.back(); + const index_component_t&index_tail = name_tail.index.back(); + ivl_assert(*this, index_tail.msb != 0); + ivl_assert(*this, index_tail.lsb == 0); + + NetNet*reg = lv->sig(); + + // Bit selects have a single select expression. Evaluate the + // constant value and treat it as a part select with a bit + // width of 1. + NetExpr*mux = elab_and_eval(des, scope, index_tail.msb, -1); + long lsb = 0; + + if (NetEConst*index_con = dynamic_cast (mux)) { + lsb = index_con->value().as_long(); + mux = 0; + } + + if (mux) { + // Non-constant bit mux. Correct the mux for the range + // of the vector, then set the l-value part select expression. + if (reg->msb() < reg->lsb()) + mux = make_sub_expr(reg->lsb(), mux); + else if (reg->lsb() != 0) + mux = make_add_expr(mux, - reg->lsb()); + + lv->set_part(mux, 1); + + } else if (lsb == reg->msb() && lsb == reg->lsb()) { + // Constant bit mux that happens to select the only bit + // of the l-value. Don't bother with any select at all. + + } else { + // Constant bit select that does something useful. + long loff = reg->sb_to_idx(lsb); + + if (loff < 0 || loff >= (long)reg->vector_width()) { + cerr << get_fileline() << ": error: bit select " + << reg->name() << "[" <errors += 1; + return 0; + } + + lv->set_part(new NetEConst(verinum(loff)), 1); + } + + return true; +} + bool PEIdent::elaborate_lval_net_part_(Design*des, NetScope*scope, NetAssign_*lv) const { + // The range expressions of a part select must be + // constant. The calculate_parts_ function calculates the + // values into msb and lsb. long msb, lsb; bool flag = calculate_parts_(des, scope, msb, lsb); if (!flag) @@ -388,17 +362,14 @@ bool PEIdent::elaborate_lval_net_part_(Design*des, if (msb == reg->msb() && lsb == reg->lsb()) { - /* No bit select, and part select covers the entire - vector. Simplest case. */ + /* Part select covers the entire vector. Simplest case. */ } else { - /* If the bit/part select is constant, then make the - NetAssign_ only as wide as it needs to be and connect - only to the selected bits of the reg. */ - unsigned loff = reg->sb_to_idx(lsb); - unsigned moff = reg->sb_to_idx(msb); - unsigned wid = moff - loff + 1; + /* Get the canonical offsets into the vector. */ + long loff = reg->sb_to_idx(lsb); + long moff = reg->sb_to_idx(msb); + long wid = moff - loff + 1; if (moff < loff) { cerr << get_fileline() << ": error: part select " @@ -408,17 +379,15 @@ bool PEIdent::elaborate_lval_net_part_(Design*des, return false; } - /* If the part select extends beyond the extreme of the + /* If the part select extends beyond the extremes of the variable, then report an error. Note that loff is converted to normalized form so is relative the variable pins. */ - if ((wid + loff) > reg->vector_width()) { - cerr << get_fileline() << ": error: bit/part select " + if (loff < 0 || moff >= (signed)reg->vector_width()) { + cerr << get_fileline() << ": warning: Part select " << reg->name() << "[" << msb<<":"<errors += 1; - return false; } lv->set_part(new NetEConst(verinum(loff)), wid); diff --git a/elab_net.cc b/elab_net.cc index 97fcb76cd..f00936882 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -137,7 +137,7 @@ NetNet* PEBinary::elaborate_net_add_(Design*des, NetScope*scope, const NetExpr* decay) const { NetNet*lsig = left_->elaborate_net(des, scope, lwidth, 0, 0, 0), - *rsig = right_->elaborate_net(des, scope, lwidth, 0, 0, 0); + *rsig = right_->elaborate_net(des, scope, lwidth, 0, 0, 0); if (lsig == 0 || rsig == 0) return 0; @@ -449,26 +449,21 @@ NetNet* PEBinary::elaborate_net_cmp_(Design*des, NetScope*scope, const NetExpr* fall, const NetExpr* decay) const { - /* Elaborate the operands of the compare first as expressions (so that the eval_tree method can reduce constant expressions, including parameters) then turn those results into synthesized nets. */ - NetExpr*lexp = elab_and_eval(des, scope, left_, lwidth), - *rexp = elab_and_eval(des, scope, right_, lwidth); + NetExpr*lexp = elab_and_eval(des, scope, left_, -1), + *rexp = elab_and_eval(des, scope, right_, -1); if (lexp == 0 || rexp == 0) return 0; - unsigned operand_width; - bool real_arg = false; - if (lexp->expr_type() == IVL_VT_REAL || - rexp->expr_type() == IVL_VT_REAL) { - operand_width = 1; - real_arg = true; - } else { + bool real_arg = true; + if (lexp->expr_type() != IVL_VT_REAL && + rexp->expr_type() != IVL_VT_REAL) { /* Choose the operand width to be the width of the widest self-determined operand. */ - operand_width = lexp->expr_width(); + unsigned operand_width = lexp->expr_width(); if (rexp->expr_width() > operand_width) operand_width = rexp->expr_width(); @@ -476,6 +471,8 @@ NetNet* PEBinary::elaborate_net_cmp_(Design*des, NetScope*scope, lexp = pad_to_width(lexp, operand_width); rexp->set_width(operand_width); rexp = pad_to_width(rexp, operand_width); + + real_arg = false; } NetNet*lsig = 0; @@ -719,6 +716,16 @@ NetNet* PEBinary::elaborate_net_div_(Design*des, NetScope*scope, unsigned rwidth = lwidth; + // If either operand is IVL_VT_REAL, then cast the other to + // IVL_VT_REAL so that the division can become IVL_VT_REAL. + + if (lsig->data_type()==IVL_VT_REAL || rsig->data_type()==IVL_VT_REAL) { + if (lsig->data_type() != IVL_VT_REAL) + lsig = cast_to_real(des, scope, lsig); + if (rsig->data_type() != IVL_VT_REAL) + rsig = cast_to_real(des, scope, rsig); + } + if (rwidth == 0) { rwidth = lsig->vector_width(); if (rsig->vector_width() > rwidth) @@ -790,8 +797,8 @@ NetNet* PEBinary::elaborate_net_mod_(Design*des, NetScope*scope, const NetExpr* fall, const NetExpr* decay) const { - NetNet*lsig = left_->elaborate_net(des, scope, 0, 0, 0, 0), - *rsig = right_->elaborate_net(des, scope, 0, 0, 0, 0); + NetNet*lsig = left_->elaborate_net(des, scope, lwidth, 0, 0, 0), + *rsig = right_->elaborate_net(des, scope, lwidth, 0, 0, 0); if (lsig == 0 || rsig == 0) return 0; @@ -1578,6 +1585,14 @@ NetNet* PEConcat::elaborate_net(Design*des, NetScope*scope, return 0; } + if (!erep->value().is_defined()) { + cerr << get_fileline() << ": error: Concatenation repeat " + << "may not be undefined (" << erep->value() + << ")." << endl; + des->errors += 1; + return 0; + } + if (erep->value().is_negative()) { cerr << get_fileline() << ": error: Concatenation repeat " << "may not be negative (" << erep->value().as_long() @@ -1779,8 +1794,32 @@ NetNet* PEIdent::elaborate_net(Design*des, NetScope*scope, that connects to a signal with the correct name. */ if (par != 0) { + // Detect and handle the special case that we have a + // real valued parameter. Return a NetLiteral and a + // properly typed net. + if (const NetECReal*pc = dynamic_cast(par)) { + NetLiteral*tmp = new NetLiteral(scope, scope->local_symbol(), + pc->value()); + des->add_node(tmp); + tmp->set_line(*par); + sig = new NetNet(scope, scope->local_symbol(), + NetNet::IMPLICIT); + sig->set_line(*tmp); + sig->data_type(tmp->data_type()); + sig->local_flag(true); + + connect(tmp->pin(0), sig->pin(0)); + return sig; + } + const NetEConst*pc = dynamic_cast(par); - assert(pc); + if (pc == 0) { + cerr << get_fileline() << ": internal error: " + << "Non-consant parameter value?: " << *par << endl; + cerr << get_fileline() << ": : " + << "Expression type is " << par->expr_type() << endl; + } + ivl_assert(*this, pc); verinum pvalue = pc->value(); /* If the parameter has declared dimensions, then apply @@ -2367,30 +2406,53 @@ NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope, osig->local_flag(true); osig->set_line(*this); - if (debug_elaborate) { - cerr << get_fileline() << ": debug: Generating part selects " - << "to connect input l-value to subexpressions." - << endl; + if (bidirectional_flag) { + if (debug_elaborate) { + cerr << get_fileline() << ": debug: Generating tran(VP) " + << "to connect input l-value to subexpressions." + << endl; + } + + for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) { + unsigned wid = nets[idx]->vector_width(); + unsigned off = width - wid; + NetTran*ps = new NetTran(scope, scope->local_symbol(), + osig->vector_width(), wid, off); + des->add_node(ps); + ps->set_line(*this); + + connect(ps->pin(0), osig->pin(0)); + connect(ps->pin(1), nets[idx]->pin(0)); + + ivl_assert(*this, wid <= width); + width -= wid; + } + + } else { + if (debug_elaborate) { + cerr << get_fileline() << ": debug: Generating part selects " + << "to connect input l-value to subexpressions." + << endl; + } + + NetPartSelect::dir_t part_dir = NetPartSelect::VP; + + for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) { + unsigned wid = nets[idx]->vector_width(); + unsigned off = width - wid; + NetPartSelect*ps = new NetPartSelect(osig, off, wid, part_dir); + des->add_node(ps); + ps->set_line(*this); + + connect(ps->pin(1), osig->pin(0)); + connect(ps->pin(0), nets[idx]->pin(0)); + + assert(wid <= width); + width -= wid; + } + assert(width == 0); } - NetPartSelect::dir_t part_dir = bidirectional_flag - ? NetPartSelect::BI - : NetPartSelect::VP; - - for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) { - unsigned wid = nets[idx]->vector_width(); - unsigned off = width - wid; - NetPartSelect*ps = new NetPartSelect(osig, off, wid, part_dir); - des->add_node(ps); - - connect(ps->pin(1), osig->pin(0)); - connect(ps->pin(0), nets[idx]->pin(0)); - - assert(wid <= width); - width -= wid; - } - assert(width == 0); - osig->data_type(nets[0]->data_type()); osig->local_flag(true); return osig; @@ -2743,11 +2805,6 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, /* If we are processing a tran or inout, then the partselect is bi-directional. Otherwise, it is a Part-to-Vector select. */ - NetPartSelect::dir_t part_dir; - if (bidirectional_flag) - part_dir = NetPartSelect::BI; - else - part_dir = NetPartSelect::PV; if (debug_elaborate) cerr << get_fileline() << ": debug: " @@ -2764,11 +2821,23 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, subsig->local_flag(true); subsig->set_line(*this); - NetPartSelect*sub = new NetPartSelect(sig, lidx, subnet_wid, - part_dir); - des->add_node(sub); - connect(sub->pin(0), subsig->pin(0)); + if (bidirectional_flag) { + // Make a tran(VP) + NetTran*sub = new NetTran(scope, scope->local_symbol(), + sig->vector_width(), + subnet_wid, lidx); + sub->set_line(*this); + des->add_node(sub); + connect(sub->pin(0), sig->pin(0)); + connect(sub->pin(1), subsig->pin(0)); + } else { + NetPartSelect*sub = new NetPartSelect(sig, lidx, subnet_wid, + NetPartSelect::PV); + des->add_node(sub); + sub->set_line(*this); + connect(sub->pin(0), subsig->pin(0)); + } sig = subsig; } diff --git a/elab_scope.cc b/elab_scope.cc index 04cce6626..6a4c55288 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -98,7 +98,16 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur, tmp->low_expr = 0; } - if (range->high_expr) { + if (range->high_expr && range->high_expr==range->low_expr) { + // Detect the special case of a "point" + // range. These are called out by setting the high + // and low expression ranges to the same + // expression. The exclude_flags should be false + // in this case + ivl_assert(*range->high_expr, tmp->low_open_flag==false && tmp->high_open_flag==false); + tmp->high_expr = tmp->low_expr; + + } else if (range->high_expr) { tmp->high_expr = elab_and_eval(des, scope, range->high_expr, -1); ivl_assert(*range->high_expr, tmp->high_expr); } else { @@ -114,6 +123,64 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur, delete val; } +static void elaborate_scope_tasks(Design*des, NetScope*scope, + const LineInfo&loc, + const map&tasks) +{ + typedef map::const_iterator tasks_it_t; + + for (tasks_it_t cur = tasks.begin() + ; cur != tasks.end() ; cur ++ ) { + + hname_t use_name( (*cur).first ); + if (scope->child(use_name)) { + cerr << loc.get_fileline() << ": error: task/scope name " + << use_name << " already used in this context." + << endl; + des->errors += 1; + continue; + } + NetScope*task_scope = new NetScope(scope, use_name, + NetScope::TASK); + task_scope->set_line((*cur).second); + + if (debug_scopes) + cerr << cur->second->get_fileline() << ": debug: " + << "Elaborate task scope " << scope_path(task_scope) << endl; + (*cur).second->elaborate_scope(des, task_scope); + } + +} + +static void elaborate_scope_funcs(Design*des, NetScope*scope, + const LineInfo&loc, + const map&funcs) +{ + typedef map::const_iterator funcs_it_t; + + for (funcs_it_t cur = funcs.begin() + ; cur != funcs.end() ; cur ++ ) { + + hname_t use_name( (*cur).first ); + if (scope->child(use_name)) { + cerr << loc.get_fileline() << ": error: function/scope name " + << use_name << " already used in this context." + << endl; + des->errors += 1; + continue; + } + NetScope*func_scope = new NetScope(scope, use_name, + NetScope::FUNC); + func_scope->set_line((*cur).second); + + if (debug_scopes) + cerr << cur->second->get_fileline() << ": debug: " + << "Elaborate function scope " << scope_path(func_scope) << endl; + (*cur).second->elaborate_scope(des, func_scope); + } + +} + bool Module::elaborate_scope(Design*des, NetScope*scope, const replace_t&replacements) const { @@ -257,49 +324,14 @@ bool Module::elaborate_scope(Design*des, NetScope*scope, // elaborate_scope method of the PTask for detailed // processing. - typedef map::const_iterator tasks_it_t; - - for (tasks_it_t cur = tasks_.begin() - ; cur != tasks_.end() ; cur ++ ) { - - hname_t use_name( (*cur).first ); - if (scope->child(use_name)) { - cerr << get_fileline() << ": error: task/scope name " - << use_name << " already used in this context." - << endl; - des->errors += 1; - continue; - } - NetScope*task_scope = new NetScope(scope, use_name, - NetScope::TASK); - task_scope->set_line((*cur).second); - (*cur).second->elaborate_scope(des, task_scope); - } + elaborate_scope_tasks(des, scope, *this, tasks); // Functions are very similar to tasks, at least from the // perspective of scopes. So handle them exactly the same // way. - typedef map::const_iterator funcs_it_t; - - for (funcs_it_t cur = funcs_.begin() - ; cur != funcs_.end() ; cur ++ ) { - - hname_t use_name( (*cur).first ); - if (scope->child(use_name)) { - cerr << get_fileline() << ": error: function/scope name " - << use_name << " already used in this context." - << endl; - des->errors += 1; - continue; - } - NetScope*func_scope = new NetScope(scope, use_name, - NetScope::FUNC); - func_scope->set_line((*cur).second); - (*cur).second->elaborate_scope(des, func_scope); - } - + elaborate_scope_funcs(des, scope, *this, funcs); // Gates include modules, which might introduce new scopes, so // scan all of them to create those scopes. @@ -539,8 +571,8 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container) PGenerate*default_item = 0; typedef list::const_iterator generator_it_t; - generator_it_t cur = generates.begin(); - while (cur != generates.end()) { + generator_it_t cur = generate_schemes.begin(); + while (cur != generate_schemes.end()) { PGenerate*item = *cur; assert( item->scheme_type == PGenerate::GS_CASE_ITEM ); @@ -569,7 +601,7 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container) delete case_value_co; case_value_co = 0; - PGenerate*item = (cur == generates.end())? default_item : *cur; + PGenerate*item = (cur == generate_schemes.end())? default_item : *cur; if (item == 0) { cerr << get_fileline() << ": debug: " << "No generate items found" << endl; @@ -596,11 +628,14 @@ void PGenerate::elaborate_subscope_(Design*des, NetScope*scope) // from simple elaboration. typedef list::const_iterator generate_it_t; - for (generate_it_t cur = generates.begin() - ; cur != generates.end() ; cur ++ ) { + for (generate_it_t cur = generate_schemes.begin() + ; cur != generate_schemes.end() ; cur ++ ) { (*cur) -> generate_scope(des, scope); } + elaborate_scope_tasks(des, scope, *this, tasks); + elaborate_scope_funcs(des, scope, *this, funcs); + // Scan the generated scope for gates that may create // their own scopes. typedef list::const_iterator pgate_list_it_t; @@ -609,6 +644,12 @@ void PGenerate::elaborate_subscope_(Design*des, NetScope*scope) (*cur) ->elaborate_scope(des, scope); } + typedef list::const_iterator proc_it_t; + for (proc_it_t cur = behaviors.begin() + ; cur != behaviors.end() ; cur ++ ) { + (*cur) -> statement() -> elaborate_scope(des, scope); + } + // Save the scope that we created, for future use. scope_list_.push_back(scope); } diff --git a/elab_sig.cc b/elab_sig.cc index 371f1759a..8faa7f2c9 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -108,6 +108,42 @@ bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const return flag; } +static void elaborate_sig_funcs(Design*des, NetScope*scope, + const map&funcs) +{ + typedef map::const_iterator mfunc_it_t; + + for (mfunc_it_t cur = funcs.begin() + ; cur != funcs.end() ; cur ++) { + + hname_t use_name ( (*cur).first ); + NetScope*fscope = scope->child(use_name); + if (scope == 0) { + cerr << (*cur).second->get_fileline() << ": internal error: " + << "Child scope for function " << (*cur).first + << " missing in " << scope_path(scope) << "." << endl; + des->errors += 1; + continue; + } + + (*cur).second->elaborate_sig(des, fscope); + } +} + +static void elaborate_sig_tasks(Design*des, NetScope*scope, + const map&tasks) +{ + typedef map::const_iterator mtask_it_t; + + for (mtask_it_t cur = tasks.begin() + ; cur != tasks.end() ; cur ++) { + NetScope*tscope = scope->child( hname_t((*cur).first) ); + assert(tscope); + (*cur).second->elaborate_sig(des, tscope); + } +} + + bool Module::elaborate_sig(Design*des, NetScope*scope) const { bool flag = true; @@ -182,38 +218,12 @@ bool Module::elaborate_sig(Design*des, NetScope*scope) const flag &= (*gt)->elaborate_sig(des, scope); } - - typedef map::const_iterator mfunc_it_t; - - for (mfunc_it_t cur = funcs_.begin() - ; cur != funcs_.end() ; cur ++) { - - hname_t use_name ( (*cur).first ); - NetScope*fscope = scope->child(use_name); - if (scope == 0) { - cerr << (*cur).second->get_fileline() << ": internal error: " - << "Child scope for function " << (*cur).first - << " missing in " << scope_path(scope) << "." << endl; - des->errors += 1; - continue; - } - - (*cur).second->elaborate_sig(des, fscope); - } - - // After all the wires are elaborated, we are free to // elaborate the ports of the tasks defined within this // module. Run through them now. - typedef map::const_iterator mtask_it_t; - - for (mtask_it_t cur = tasks_.begin() - ; cur != tasks_.end() ; cur ++) { - NetScope*tscope = scope->child( hname_t((*cur).first) ); - assert(tscope); - (*cur).second->elaborate_sig(des, tscope); - } + elaborate_sig_funcs(des, scope, funcs); + elaborate_sig_tasks(des, scope, tasks); // initial and always blocks may contain begin-end and // fork-join blocks that can introduce scopes. Therefore, I @@ -415,8 +425,8 @@ bool PGenerate::elaborate_sig(Design*des, NetScope*container) const << scope_path(container) << "." << endl; typedef list::const_iterator generate_it_t; - for (generate_it_t cur = generates.begin() - ; cur != generates.end() ; cur ++) { + for (generate_it_t cur = generate_schemes.begin() + ; cur != generate_schemes.end() ; cur ++) { PGenerate*item = *cur; if (! item->scope_list_.empty()) { flag &= item->elaborate_sig(des, container); @@ -461,9 +471,12 @@ bool PGenerate::elaborate_sig_(Design*des, NetScope*scope) const cur->elaborate_sig(des, scope); } + elaborate_sig_funcs(des, scope, funcs); + elaborate_sig_tasks(des, scope, tasks); + typedef list::const_iterator generate_it_t; - for (generate_it_t cur = generates.begin() - ; cur != generates.end() ; cur ++ ) { + for (generate_it_t cur = generate_schemes.begin() + ; cur != generate_schemes.end() ; cur ++ ) { (*cur) -> elaborate_sig(des, scope); } diff --git a/elaborate.cc b/elaborate.cc index d7180f4b5..a0d8d73a0 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -94,7 +94,7 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const assert(lval->pin_count() == 1); if (debug_elaborate) { - cerr << get_fileline() << ": debug: PGassign: elaborated l-value" + cerr << get_fileline() << ": debug: PGAssign: elaborated l-value" << " width=" << lval->vector_width() << ", type=" << lval->data_type() << endl; } @@ -113,14 +113,13 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const return; } - /* If either lval or rid are real then both must be real. */ - if ((lval->data_type() == IVL_VT_REAL || - rid->data_type() == IVL_VT_REAL) && - lval->data_type() != rid->data_type()) { - cerr << get_fileline() << ": sorry: Both the r-value and " - "the l-value must be real in this context." << endl; - des->errors += 1; - return; + /* Cast the right side when needed. */ + if ((lval->data_type() == IVL_VT_REAL && + rid->data_type() != IVL_VT_REAL)) { + rid = cast_to_real(des, scope, rid); + } else if ((lval->data_type() != IVL_VT_REAL && + rid->data_type() == IVL_VT_REAL)) { + rid = cast_to_int(des, scope, rid, lval->vector_width()); } ivl_assert(*this, rid); @@ -287,14 +286,13 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const assert(lval && rval); assert(rval->pin_count() == 1); - /* If either lval or rval are real then both must be real. */ - if ((lval->data_type() == IVL_VT_REAL || - rval->data_type() == IVL_VT_REAL) && - lval->data_type() != rval->data_type()) { - cerr << get_fileline() << ": sorry: Both the r-value and " - "the l-value must be real in this context." << endl; - des->errors += 1; - return; + /* Cast the right side when needed. */ + if ((lval->data_type() == IVL_VT_REAL && + rval->data_type() != IVL_VT_REAL)) { + rval = cast_to_real(des, scope, rval); + } else if ((lval->data_type() != IVL_VT_REAL && + rval->data_type() == IVL_VT_REAL)) { + rval = cast_to_int(des, scope, rval, lval->vector_width()); } /* If the r-value insists on being smaller then the l-value @@ -386,7 +384,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const high = msb.as_long(); if (debug_elaborate) { - cerr << get_fileline() << ": debug: PGBuiltin: Make arrray " + cerr << get_fileline() << ": debug: PGBuiltin: Make array " << "[" << high << ":" << low << "]" << " of " << count << " gates for " << name << endl; } @@ -864,6 +862,9 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const } } + + // "cur" is an array of pointers, and we don't need it any more. + delete[]cur; } NetNet*PGModule::resize_net_to_port_(Design*des, NetScope*scope, @@ -878,6 +879,31 @@ NetNet*PGModule::resize_net_to_port_(Design*des, NetScope*scope, tmp->local_flag(true); tmp->set_line(*sig); + // Handle the special case of a bi-directional part + // select. Create a NetTran(VP) instead of a uni-directional + // NetPartSelect node. + if (dir == NetNet::PINOUT) { + unsigned wida = sig->vector_width(); + unsigned widb = tmp->vector_width(); + bool part_b = widb < wida; + NetTran*node = new NetTran(scope, scope->local_symbol(), + part_b? wida : widb, + part_b? widb : wida, + 0); + if (part_b) { + connect(node->pin(0), sig->pin(0)); + connect(node->pin(1), tmp->pin(0)); + } else { + connect(node->pin(0), tmp->pin(0)); + connect(node->pin(1), sig->pin(0)); + } + + node->set_line(*this); + des->add_node(node); + + return tmp; + } + NetPartSelect*node = 0; switch (dir) { @@ -906,15 +932,7 @@ NetNet*PGModule::resize_net_to_port_(Design*des, NetScope*scope, break; case NetNet::PINOUT: - if (sig->vector_width() > tmp->vector_width()) { - node = new NetPartSelect(sig, 0, tmp->vector_width(), - NetPartSelect::BI); - connect(node->pin(0), tmp->pin(0)); - } else { - node = new NetPartSelect(tmp, 0, sig->vector_width(), - NetPartSelect::BI); - connect(node->pin(0), sig->pin(0)); - } + ivl_assert(*this, 0); break; default: @@ -1169,8 +1187,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const identifier elaborates to the same NetNet in both cases so the extra elaboration has no effect. But if the expression passed to the - inout port is a part select, aspecial part - select must be created that can paqss data in + inout port is a part select, a special part + select must be created that can pass data in both directions. Use the elaborate_bi_net method to handle all @@ -1932,7 +1950,7 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const if (nscope == 0) { cerr << get_fileline() << ": internal error: " "unable to find block scope " << scope_path(scope) - << "<" << pscope_name() << ">" << endl; + << "." << pscope_name() << endl; des->errors += 1; return 0; } @@ -3035,7 +3053,7 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const sig = des->find_signal(scope, id2->path()); if (sig == 0) { cerr << get_fileline() << ": error: Unable to find variable " - << id2->path() << " in for-loop increment expressin." << endl; + << id2->path() << " in for-loop increment expression." << endl; des->errors += 1; return body; } @@ -3536,6 +3554,34 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const } +static void elaborate_functions(Design*des, NetScope*scope, + const map&funcs) +{ + typedef map::const_iterator mfunc_it_t; + for (mfunc_it_t cur = funcs.begin() + ; cur != funcs.end() ; cur ++) { + + hname_t use_name ( (*cur).first ); + NetScope*fscope = scope->child(use_name); + assert(fscope); + (*cur).second->elaborate(des, fscope); + } +} + +static void elaborate_tasks(Design*des, NetScope*scope, + const map&tasks) +{ + typedef map::const_iterator mtask_it_t; + for (mtask_it_t cur = tasks.begin() + ; cur != tasks.end() ; cur ++) { + + hname_t use_name ( (*cur).first ); + NetScope*tscope = scope->child(use_name); + assert(tscope); + (*cur).second->elaborate(des, tscope); + } +} + /* * When a module is instantiated, it creates the scope then uses this * method to elaborate the contents of the module. @@ -3595,28 +3641,12 @@ bool Module::elaborate(Design*des, NetScope*scope) const } // Elaborate functions. - typedef map::const_iterator mfunc_it_t; - for (mfunc_it_t cur = funcs_.begin() - ; cur != funcs_.end() ; cur ++) { - - hname_t use_name ( (*cur).first ); - NetScope*fscope = scope->child(use_name); - assert(fscope); - (*cur).second->elaborate(des, fscope); - } + elaborate_functions(des, scope, funcs); // Elaborate the task definitions. This is done before the // behaviors so that task calls may reference these, and after // the signals so that the tasks can reference them. - typedef map::const_iterator mtask_it_t; - for (mtask_it_t cur = tasks_.begin() - ; cur != tasks_.end() ; cur ++) { - - hname_t use_name ( (*cur).first ); - NetScope*tscope = scope->child(use_name); - assert(tscope); - (*cur).second->elaborate(des, tscope); - } + elaborate_tasks(des, scope, tasks); // Get all the gates of the module and elaborate them by // connecting them to the signals. The gate may be simple or @@ -3661,8 +3691,8 @@ bool PGenerate::elaborate(Design*des, NetScope*container) const << scope_path(container) << "." << endl; typedef list::const_iterator generate_it_t; - for (generate_it_t cur = generates.begin() - ; cur != generates.end() ; cur ++) { + for (generate_it_t cur = generate_schemes.begin() + ; cur != generate_schemes.end() ; cur ++) { PGenerate*item = *cur; if (! item->scope_list_.empty()) { flag &= item->elaborate(des, container); @@ -3704,6 +3734,9 @@ bool PGenerate::elaborate(Design*des, NetScope*container) const bool PGenerate::elaborate_(Design*des, NetScope*scope) const { + elaborate_functions(des, scope, funcs); + elaborate_tasks(des, scope, tasks); + typedef list::const_iterator gates_it_t; for (gates_it_t cur = gates.begin() ; cur != gates.end() ; cur ++ ) (*cur)->elaborate(des, scope); @@ -3713,8 +3746,8 @@ bool PGenerate::elaborate_(Design*des, NetScope*scope) const (*cur)->elaborate(des, scope); typedef list::const_iterator generate_it_t; - for (generate_it_t cur = generates.begin() - ; cur != generates.end() ; cur ++ ) { + for (generate_it_t cur = generate_schemes.begin() + ; cur != generate_schemes.end() ; cur ++ ) { (*cur)->elaborate(des, scope); } diff --git a/emit.cc b/emit.cc index 079a41c84..4e2b652b6 100644 --- a/emit.cc +++ b/emit.cc @@ -72,6 +72,16 @@ bool NetCaseCmp::emit_node(struct target_t*tgt) const return true; } +bool NetCastInt::emit_node(struct target_t*tgt) const +{ + return tgt->lpm_cast_int(this); +} + +bool NetCastReal::emit_node(struct target_t*tgt) const +{ + return tgt->lpm_cast_real(this); +} + bool NetCLShift::emit_node(struct target_t*tgt) const { tgt->lpm_clshift(this); @@ -362,7 +372,7 @@ void NetScope::emit_scope(struct target_t*tgt) const /* Run the signals again, but this time to connect the delay paths. This is done as a second pass because the paths reference other signals that may be later - in the list. We can do it here becase delay paths are + in the list. We can do it here because delay paths are always connected within the scope. */ cur = signals_->sig_next_; do { diff --git a/examples/sqrt-virtex.v b/examples/sqrt-virtex.v index 410e6e744..bc95d4939 100644 --- a/examples/sqrt-virtex.v +++ b/examples/sqrt-virtex.v @@ -154,7 +154,7 @@ * * The POST_MAP compiler directive causes the GSR manipulations * included in the test bench to be compiled in, to simulate the chip - * startup. Other then that, the test bench runs the post-map design + * startup. Other than that, the test bench runs the post-map design * the same way the pre-synthesis design works. * * Run this design with the command: diff --git a/expr_synth.cc b/expr_synth.cc index 3f079fc5c..df291f6d1 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -459,7 +459,7 @@ NetNet* NetEBDiv::synthesize(Design*des) default: { cerr << get_fileline() << ": internal error: " - << "NetEBDiv has unexpeced op() code: " + << "NetEBDiv has unexpected op() code: " << op() << endl; des->errors += 1; diff --git a/ivl.def b/ivl.def index 48b798910..6a74bf7cb 100644 --- a/ivl.def +++ b/ivl.def @@ -53,6 +53,9 @@ ivl_file_table_index ivl_file_table_item ivl_file_table_size +ivl_island_flag_set +ivl_island_flag_test + ivl_logic_attr ivl_logic_attr_cnt ivl_logic_attr_val @@ -115,6 +118,7 @@ ivl_nexus_ptr_pin ivl_nexus_ptr_lpm ivl_nexus_ptr_log ivl_nexus_ptr_sig +ivl_nexus_ptr_switch ivl_parameter_basename ivl_parameter_expr @@ -222,10 +226,14 @@ ivl_switch_a ivl_switch_b ivl_switch_basename ivl_switch_enable +ivl_switch_file +ivl_switch_island +ivl_switch_lineno +ivl_switch_offset +ivl_switch_part ivl_switch_scope ivl_switch_type -ivl_switch_attr_cnt; -ivl_switch_attr_val; +ivl_switch_width ivl_udp_init ivl_udp_name diff --git a/ivl_target.h b/ivl_target.h index a39366a18..13d4b68db 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -80,6 +80,13 @@ _BEGIN_DECL * processes. Structural expressions are instead treated as logic * gates. * + * ivl_island_t + * Certain types of objects may belong to islands. The island that + * they belong to is represented by the ivl_island_t cookie. To + * know if object belong to the same island, it is sufficient to + * compare island cookies. If a==b, then island a is the same as + * island b. + * * ivl_lpm_t * This object is the base class for all the various LPM type * device nodes. This object carries a few base properties @@ -146,6 +153,7 @@ typedef struct ivl_delaypath_s*ivl_delaypath_t; typedef struct ivl_design_s *ivl_design_t; typedef struct ivl_event_s *ivl_event_t; typedef struct ivl_expr_s *ivl_expr_t; +typedef struct ivl_island_s *ivl_island_t; typedef struct ivl_lpm_s *ivl_lpm_t; typedef struct ivl_lval_s *ivl_lval_t; typedef struct ivl_net_const_s*ivl_net_const_t; @@ -237,7 +245,8 @@ typedef enum ivl_switch_type_e { IVL_SW_TRANIF1 = 2, IVL_SW_RTRAN = 3, IVL_SW_RTRANIF0 = 4, - IVL_SW_RTRANIF1 = 5 + IVL_SW_RTRANIF1 = 5, + IVL_SW_TRAN_VP = 6 } ivl_switch_type_t; /* This is the type of an LPM object. */ @@ -245,6 +254,8 @@ typedef enum ivl_lpm_type_e { IVL_LPM_ABS = 32, IVL_LPM_ADD = 0, IVL_LPM_ARRAY = 30, + IVL_LPM_CAST_INT = 34, + IVL_LPM_CAST_REAL = 33, IVL_LPM_CONCAT = 16, IVL_LPM_CMP_EEQ= 18, /* Case EQ (===) */ IVL_LPM_CMP_EQ = 10, @@ -257,7 +268,7 @@ typedef enum ivl_lpm_type_e { IVL_LPM_MOD = 13, IVL_LPM_MULT = 4, IVL_LPM_MUX = 5, - IVL_LPM_PART_BI= 28, /* part select: bi-directional (part on 0) */ + /* IVL_LPM_PART_BI= 28, / obsolete */ IVL_LPM_PART_VP= 15, /* part select: vector to part */ IVL_LPM_PART_PV= 17, /* part select: part written to vector */ IVL_LPM_POW = 31, @@ -713,6 +724,16 @@ extern unsigned ivl_file_table_index(const char *); extern unsigned ivl_file_table_size(void); +/* ISLAND + * + * ivl_island_flag_set + * ivl_island_flag_test + * Allow the user to test or set a boolean flag associated with the + * island. + */ +extern int ivl_island_flag_set(ivl_island_t net, unsigned flag, int value); +extern int ivl_island_flag_test(ivl_island_t net, unsigned flag); + /* LOGIC * These types and functions support manipulation of logic gates. The * ivl_logic_t enumeration identifies the various kinds of gates that @@ -1327,6 +1348,7 @@ extern unsigned ivl_nexus_ptr_pin(ivl_nexus_ptr_t net); extern ivl_net_const_t ivl_nexus_ptr_con(ivl_nexus_ptr_t net); extern ivl_net_logic_t ivl_nexus_ptr_log(ivl_nexus_ptr_t net); extern ivl_lpm_t ivl_nexus_ptr_lpm(ivl_nexus_ptr_t net); +extern ivl_switch_t ivl_nexus_ptr_switch(ivl_nexus_ptr_t net); extern ivl_signal_t ivl_nexus_ptr_sig(ivl_nexus_ptr_t net); /* PARAMETER @@ -1854,9 +1876,6 @@ extern ivl_statement_t ivl_stmt_sub_stmt(ivl_statement_t net); * ivl_switch_basename * This is the name given to the device in the source code. * - * ivl_switch_scope - * The scope where the switch device appears. - * * ivl_switch_a * ivl_switch_b * The a and b ports are the two ports of the switch. @@ -1867,17 +1886,31 @@ extern ivl_statement_t ivl_stmt_sub_stmt(ivl_statement_t net); * * SEMANTIC NOTES * The a/b ports can be any type, but the types must exactly - * match. The enable must be a scalar. + * match, including vector widths. The enable must be a scalar. + * + * The IVL_SW_TRAN_VP is an exception to the above. In this case, + * the B side may be a different size, and the a side will have a + * a fixed width. The unused bits are padded to Z on the A side. */ extern ivl_switch_type_t ivl_switch_type(ivl_switch_t net); -extern const char*ivl_switch_basename(ivl_switch_t net); extern ivl_scope_t ivl_switch_scope(ivl_switch_t net); +extern const char*ivl_switch_basename(ivl_switch_t net); extern ivl_nexus_t ivl_switch_a(ivl_switch_t net); extern ivl_nexus_t ivl_switch_b(ivl_switch_t net); extern ivl_nexus_t ivl_switch_enable(ivl_switch_t net); +extern ivl_island_t ivl_switch_island(ivl_switch_t net); + /* These are only support for IVL_SW_TRAN_VP switches. */ +extern unsigned ivl_switch_width(ivl_switch_t net); +extern unsigned ivl_switch_part(ivl_switch_t net); +extern unsigned ivl_switch_offset(ivl_switch_t net); + +/* Not implemented yet extern unsigned ivl_switch_attr_cnt(ivl_switch_t net); extern ivl_attribute_t ivl_switch_attr_val(ivl_switch_t net, unsigned idx); +*** */ +extern const char* ivl_switch_file(ivl_switch_t net); +extern unsigned ivl_switch_lineno(ivl_switch_t net); #if defined(__MINGW32__) || defined (__CYGWIN32__) # define DLLEXPORT __declspec(dllexport) diff --git a/ivl_target_priv.h b/ivl_target_priv.h new file mode 100644 index 000000000..042e0e415 --- /dev/null +++ b/ivl_target_priv.h @@ -0,0 +1,44 @@ +#ifndef __ivl_target_priv_H +#define __ivl_target_H +/* + * Copyright (c) 2008 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +# include + +/* +* This deader has declarations related to the ivl_target.h API that +* are not to be exported outside of the core via the ivl_target.h +* interface. +*/ + +/* +* Information about islands. Connected branches within a net are +* collected into islands. Branches that are purely ddiscrete do not +* have disciplines and do not belong to islands. +*/ + +class discipline_t; + +struct ivl_island_s { + discipline_t*discipline; + // user accessible flags. They are initially false, always. + vector flags; +}; + +#endif diff --git a/ivlpp/lexor.lex b/ivlpp/lexor.lex index 098ef38f7..c4ac34fc3 100644 --- a/ivlpp/lexor.lex +++ b/ivlpp/lexor.lex @@ -941,7 +941,7 @@ static void do_define() } /* Detect the continuation sequence. If I find it, remove it - * and the white space that preceeds it, then replace all that + * and the white space that precedes it, then replace all that * with a single newline. */ if ((cp > yytext) && (cp[-1] == '\\')) diff --git a/lexor.lex b/lexor.lex index d0dfa7980..936f5ce0f 100644 --- a/lexor.lex +++ b/lexor.lex @@ -843,7 +843,7 @@ static verinum*make_unsized_dec(const char*ptr) if (ptr[0] == '\'') { /* The number has decorations of the form 'sd, possibly with space between the d and the . - Also, the 's' is optional, and markes the number as + Also, the 's' is optional, and marks the number as signed. */ ptr += 1; diff --git a/main.cc b/main.cc index 640fcf4d3..26b237e24 100644 --- a/main.cc +++ b/main.cc @@ -825,6 +825,11 @@ int main(int argc, char*argv[]) func(des); } + if (verbose_flag) { + cout << "CALCULATING ISLANDS" << endl; + } + des->join_islands(); + if (net_path) { if (verbose_flag) cerr<<" dumping netlist to " <dup_expr(); bool flag = targ_scope->replace_parameter(perm_name, val); if (! flag) { cerr << val->get_fileline() << ": warning: parameter " @@ -335,6 +339,14 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) unsigned long wid = (msb >= lsb)? msb - lsb : lsb - msb; wid += 1; + /* If we have a real value convert it to an integer. */ + if(NetECReal*tmp = dynamic_cast(expr)) { + verinum nval(tmp->value().as_long64()); + expr = new NetEConst(nval); + expr->set_line(*((*cur).second.expr)); + (*cur).second.expr = expr; + } + NetEConst*val = dynamic_cast(expr); assert(val); @@ -348,7 +360,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) tmp.has_sign ( (*cur).second.signed_flag ); delete val; val = new NetEConst(tmp); - expr = val; + (*cur).second.expr = expr = val; } } @@ -529,6 +541,10 @@ void NetScope::evaluate_parameters(Design*des) cerr << (*cur).second.get_fileline() << ": internal error: " << "Unexpected expression type " << (*cur).second.type << "." << endl; + cerr << (*cur).second.get_fileline() << ": : " + << "Parameter name: " << (*cur).first << endl; + cerr << (*cur).second.get_fileline() << ": : " + << "Expression is: " << *(*cur).second.expr << endl; ivl_assert((*cur).second, 0); break; } @@ -673,3 +689,14 @@ void Design::delete_process(NetProcTop*top) delete top; } +void Design::join_islands(void) +{ + if (nodes_ == 0) + return; + + NetNode*cur = nodes_->node_next_; + do { + join_island(cur); + cur = cur->node_next_; + } while (cur != nodes_->node_next_); +} diff --git a/net_scope.cc b/net_scope.cc index 4425a2a49..2b8baf0cb 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -121,6 +121,9 @@ NetExpr* NetScope::set_parameter(perm_string key, NetExpr*expr, ivl_assert(file_line, ref.range == 0); ref.range = range_list; ref.set_line(file_line); + + ivl_assert(file_line, type != IVL_VT_NO_TYPE); + return res; } @@ -163,7 +166,7 @@ bool NetScope::replace_parameter(perm_string key, NetExpr*expr) /* * This is not really complete (msb, lsb, sign). It is currently only - * used to add a genver to the local parameter list. + * used to add a genvar to the local parameter list. */ NetExpr* NetScope::set_localparam(perm_string key, NetExpr*expr, const LineInfo&file_line) @@ -272,7 +275,7 @@ const NetFuncDef* NetScope::func_def() const void NetScope::set_module_name(perm_string n) { assert(type_ == MODULE); - module_name_ = n; /* NOTE: n mus have been permallocated. */ + module_name_ = n; /* NOTE: n must have been permallocated. */ } perm_string NetScope::module_name() const diff --git a/net_tran.cc b/net_tran.cc index 88067769b..b28a28753 100644 --- a/net_tran.cc +++ b/net_tran.cc @@ -26,6 +26,7 @@ # include "compiler.h" # include "netlist.h" # include "netmisc.h" +# include "ivl_target_priv.h" # include "ivl_assert.h" static bool has_enable(ivl_switch_type_t tt) @@ -42,7 +43,7 @@ static bool has_enable(ivl_switch_type_t tt) } NetTran::NetTran(NetScope*scope, perm_string n, ivl_switch_type_t tt) -: NetNode(scope, n, has_enable(tt)? 3 : 2) +: NetNode(scope, n, has_enable(tt)? 3 : 2), type_(tt) { pin(0).set_dir(Link::PASSIVE); pin(0).set_name(perm_string::literal("A"), 0); pin(1).set_dir(Link::PASSIVE); pin(1).set_name(perm_string::literal("B"), 0); @@ -52,6 +53,108 @@ NetTran::NetTran(NetScope*scope, perm_string n, ivl_switch_type_t tt) } } +NetTran::NetTran(NetScope*scope, perm_string n, unsigned wid, unsigned part, unsigned off) +: NetNode(scope, n, 2), type_(IVL_SW_TRAN_VP), wid_(wid), part_(part), off_(off) +{ + pin(0).set_dir(Link::PASSIVE); pin(0).set_name(perm_string::literal("A"), 0); + pin(1).set_dir(Link::PASSIVE); pin(1).set_name(perm_string::literal("B"), 0); +} + NetTran::~NetTran() { } + +unsigned NetTran::vector_width() const +{ + return wid_; +} + +unsigned NetTran::part_width() const +{ + return part_; +} + +unsigned NetTran::part_offset() const +{ + return off_; +} + +void join_island(NetObj*obj) +{ + IslandBranch*branch = dynamic_cast (obj); + + // If this is not even a branch, then stop now. + if (branch == 0) + return; + + // If this is a branch, but already given to an island, then + // stop. + if (branch->island) + return; + + list uncommitted_neighbors; + + // Look for neighboring objects that might already be in + // islands. If we find something, then join that island. + for (unsigned idx = 0 ; idx < obj->pin_count() ; idx += 1) { + Nexus*nex = obj->pin(idx).nexus(); + for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) { + unsigned pin; + NetObj*tmp; + cur->cur_link(tmp, pin); + + // Skip self. + if (tmp == obj) + continue; + + // If tmb is not a branch, then skip it. + IslandBranch*tmp_branch = dynamic_cast (tmp); + if (tmp_branch == 0) + continue; + + // If that is an uncommitted branch, then save + // it. When I finally choose an island for self, + // these branches will be scanned so that they join + // this island as well. + if (tmp_branch->island == 0) { + uncommitted_neighbors.push_back(tmp); + continue; + } + + ivl_assert(*obj, branch->island==0 || branch->island==tmp_branch->island); + + // We found an existing island to join. Join it + // now. Keep scanning in order to find more neighbors. + if (branch->island == 0) { + if (debug_elaborate) + cerr << obj->get_fileline() << ": debug: " + << "Join branch to existing island." << endl; + branch->island = tmp_branch->island; + + } else if (branch->island != tmp_branch->island) { + cerr << obj->get_fileline() << ": internal error: " + << "Oops, Found 2 neighboring islands." << endl; + ivl_assert(*obj, 0); + } + } + } + + // If after all that we did not find an island to join, then + // start the island not and join it. + if (branch->island == 0) { + branch->island = new ivl_island_s; + branch->island->discipline = 0; + if (debug_elaborate) + cerr << obj->get_fileline() << ": debug: " + << "Create new island for this branch" << endl; + } + + // Now scan all the uncommitted neighbors I found. Calling + // join_island() on them will cause them to notice me in the + // process, and thus they will join my island. This process + // will recurse until all the connected branches join this island. + for (list::iterator cur = uncommitted_neighbors.begin() + ; cur != uncommitted_neighbors.end() ; cur ++ ) { + join_island(*cur); + } +} diff --git a/netlist.cc b/netlist.cc index 22c9f03ee..51cf898f6 100644 --- a/netlist.cc +++ b/netlist.cc @@ -761,10 +761,6 @@ NetPartSelect::NetPartSelect(NetNet*sig, unsigned off, unsigned wid, pin(0).set_dir(Link::INPUT); pin(1).set_dir(Link::OUTPUT); break; - case NetPartSelect::BI: - pin(0).set_dir(Link::OUTPUT); - pin(1).set_dir(Link::OUTPUT); - break; } pin(0).set_name(perm_string::literal("Part"), 0); pin(1).set_name(perm_string::literal("Vect"), 0); @@ -787,10 +783,6 @@ NetPartSelect::NetPartSelect(NetNet*sig, NetNet*sel, pin(0).set_dir(Link::INPUT); pin(1).set_dir(Link::OUTPUT); break; - case NetPartSelect::BI: - pin(0).set_dir(Link::PASSIVE); - pin(1).set_dir(Link::PASSIVE); - break; } pin(2).set_dir(Link::INPUT); @@ -857,6 +849,24 @@ const NetScope* NetProcTop::scope() const return scope_; } +NetCastInt::NetCastInt(NetScope*scope, perm_string n, unsigned width) +: NetNode(scope, n, 2), width_(width) +{ + pin(0).set_dir(Link::OUTPUT); + pin(0).set_name(perm_string::literal("O"), 0); + pin(1).set_dir(Link::INPUT); + pin(1).set_name(perm_string::literal("I"), 0); +} + +NetCastReal::NetCastReal(NetScope*scope, perm_string n, bool signed_flag) +: NetNode(scope, n, 2), signed_flag_(signed_flag) +{ + pin(0).set_dir(Link::OUTPUT); + pin(0).set_name(perm_string::literal("O"), 0); + pin(1).set_dir(Link::INPUT); + pin(1).set_name(perm_string::literal("I"), 0); +} + NetConcat::NetConcat(NetScope*scope, perm_string n, unsigned wid, unsigned cnt) : NetNode(scope, n, cnt+1), width_(wid) { @@ -1504,6 +1514,11 @@ NetLiteral::~NetLiteral() { } +ivl_variable_type_t NetLiteral::data_type() const +{ + return IVL_VT_REAL; +} + const verireal& NetLiteral::value_real() const { return real_; diff --git a/netlist.h b/netlist.h index 191d91e08..d8963aeea 100644 --- a/netlist.h +++ b/netlist.h @@ -52,6 +52,7 @@ class Nexus; class NetEvent; class NetNet; class NetNode; +class NetObj; class NetProc; class NetProcTop; class NetRelease; @@ -71,6 +72,8 @@ struct functor_t; ostream& operator << (ostream&o, ivl_variable_type_t val); +extern void join_island(NetObj*obj); + /* ========= * A NetObj is anything that has any kind of behavior in the * netlist. Nodes can be gates, registers, etc. and are linked @@ -131,6 +134,20 @@ class NetObj : public Attrib, public virtual LineInfo { const NetExpr* delay3_; }; +/* +* Objects that can be island branches are derived from this. (It is +* possible for an object to be a NetObj and an IslandBranch.) This is +* used to collect island information about the node. +*/ + +class IslandBranch { + public: + IslandBranch() : island(0) { } + + public: + struct ivl_island_s* island; +}; + class Link { friend void connect(Link&, Link&); @@ -810,7 +827,7 @@ class NetScope : public Attrib { /* * This class implements the LPM_ABS component. The node has a single - * input, a signe expression, that it converts to the absolute + * input, a signed expression, that it converts to the absolute * value. The gate is simple: pin(0) is the output and pin(1) is the input. */ class NetAbs : public NetNode { @@ -899,6 +916,44 @@ class NetArrayDq : public NetNode { }; +/* + * Convert an IVL_VT_REAL input to a logical value with the + * given width. The input is pin(1) and the output is pin(0). + */ +class NetCastInt : public NetNode { + + public: + NetCastInt(NetScope*s, perm_string n, unsigned width); + + unsigned width() const { return width_; } + + virtual void dump_node(ostream&, unsigned ind) const; + virtual bool emit_node(struct target_t*) const; + + private: + unsigned width_; +}; + +/* + * Convert an input to IVL_VT_REAL. The input is pin(1), which can be + * any vector type (VT_BOOL or VT_LOGIC) and the output is pin(0), + * which is IVL_VT_REAL. The conversion interprets the input as an + * unsigned value unless the signed_flag is true. + */ +class NetCastReal : public NetNode { + + public: + NetCastReal(NetScope*s, perm_string n, bool signed_flag); + + bool signed_flag() const { return signed_flag_; } + + virtual void dump_node(ostream&, unsigned ind) const; + virtual bool emit_node(struct target_t*) const; + + private: + bool signed_flag_; +}; + /* * This type represents the LPM_CLSHIFT device. */ @@ -1359,19 +1414,31 @@ class NetSysFunc : public NetNode { const struct sfunc_return_type*def_; }; -class NetTran : public NetNode { +class NetTran : public NetNode, public IslandBranch { public: + // Tran devices other than TRAN_VP NetTran(NetScope*scope, perm_string n, ivl_switch_type_t type); + // Create a TRAN_VP + NetTran(NetScope*scope, perm_string n, unsigned wid, + unsigned part, unsigned off); ~NetTran(); ivl_switch_type_t type() const { return type_; } + // These are only used for IVL_SW_TRAN_PV + unsigned vector_width() const; + unsigned part_width() const; + unsigned part_offset() const; + virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: ivl_switch_type_t type_; + unsigned wid_; + unsigned part_; + unsigned off_; }; /* ========= @@ -1588,8 +1655,8 @@ class NetECRealParam : public NetECReal { * selector as the input. * * The NetPartSelect can be output from the signal (i.e. reading a - * part), input into the signal, or bi-directional. The DIR method - * gives the type of the node. + * part) or input into the signal. The DIR method gives the type of + * the node. * * VP (Vector-to-Part) * Output pin 0 is the part select, and input pin 1 is connected to @@ -1599,10 +1666,6 @@ class NetECRealParam : public NetECReal { * Output pin 1 is connected to the NetNet, and input pin 0 is the * part select. In this case, the node is driving the NetNet. * - * BI (BI-directional) - * Pin 0 is the part select and pin 1 is connected to the NetNet, but - * the ports are intended to be bi-directional. - * * Note that whatever the direction that data is intended to flow, * pin-0 is the part select and pin-1 is connected to the NetNet. */ @@ -1610,7 +1673,7 @@ class NetPartSelect : public NetNode { public: // enum for the device direction - enum dir_t { VP, PV, BI }; + enum dir_t { VP, PV}; explicit NetPartSelect(NetNet*sig, unsigned off, unsigned wid, dir_t dir); @@ -3613,6 +3676,7 @@ class Design { // Iterate over the design... void dump(ostream&) const; void functor(struct functor_t*); + void join_islands(void); int emit(struct target_t*) const; // This is incremented by elaboration when an error is diff --git a/netmisc.cc b/netmisc.cc index ac69d6885..2cab1dc8d 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -76,6 +76,46 @@ NetNet* add_to_net(Design*des, NetNet*sig, long val) #endif } +NetNet* cast_to_int(Design*des, NetScope*scope, NetNet*src, unsigned wid) +{ + if (src->data_type() != IVL_VT_REAL) + return src; + + NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, wid); + tmp->data_type(IVL_VT_LOGIC); + tmp->set_line(*src); + tmp->local_flag(true); + + NetCastInt*cast = new NetCastInt(scope, scope->local_symbol(), wid); + cast->set_line(*src); + des->add_node(cast); + + connect(cast->pin(0), tmp->pin(0)); + connect(cast->pin(1), src->pin(0)); + + return tmp; +} + +NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src) +{ + if (src->data_type() == IVL_VT_REAL) + return src; + + NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE); + tmp->data_type(IVL_VT_REAL); + tmp->set_line(*src); + tmp->local_flag(true); + + NetCastReal*cast = new NetCastReal(scope, scope->local_symbol(), src->get_signed()); + cast->set_line(*src); + des->add_node(cast); + + connect(cast->pin(0), tmp->pin(0)); + connect(cast->pin(1), src->pin(0)); + + return tmp; +} + /* * Add a signed constant to an existing expression. Generate a new * NetEBAdd node that has the input expression and an expression made @@ -123,6 +163,13 @@ NetExpr* make_sub_expr(long val, NetExpr*expr) return res; } +NetEConst* make_const_x(unsigned long wid) +{ + verinum xxx (verinum::Vx, wid); + NetEConst*resx = new NetEConst(xxx); + return resx; +} + NetExpr* condition_reduce(NetExpr*expr) { if (expr->expr_width() == 1) diff --git a/netmisc.h b/netmisc.h index 439e86422..20efe2d98 100644 --- a/netmisc.h +++ b/netmisc.h @@ -64,6 +64,13 @@ extern NetNet*pad_to_width(Design*des, NetNet*n, unsigned w); extern NetNet*pad_to_width_signed(Design*des, NetNet*n, unsigned w); +/* + * Generate the nodes necessary to cast an expression (a net) to a + * real value. + */ +extern NetNet*cast_to_int(Design*des, NetScope*scope, NetNet*src, unsigned wid); +extern NetNet*cast_to_real(Design*des, NetScope*scope, NetNet*src); + /* * Take the input expression and return a variation that assures that * the expression is 1-bit wide and logical. This reflects the needs @@ -102,6 +109,11 @@ extern NetNet*add_to_net(Design*des, NetNet*sig, long val); extern NetExpr*make_add_expr(NetExpr*expr, long val); extern NetExpr*make_sub_expr(long val, NetExpr*expr); +/* + * Make a NetEConst object that contains only X bits. + */ +extern NetEConst*make_const_x(unsigned long wid); + /* * In some cases the lval is accessible as a pointer to the head of * a list of NetAssign_ objects. This function returns the width of diff --git a/parse.y b/parse.y index 648a9c657..f63ee1d9b 100644 --- a/parse.y +++ b/parse.y @@ -323,10 +323,15 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) %left K_POW %left UNARY_PREC + /* to resolve dangling else ambiguity. */ %nonassoc less_than_K_else %nonassoc K_else + /* to resolve exclude (... ambiguity */ +%nonassoc '(' +%nonassoc K_exclude + %% /* A degenerate source file can be completely empty. */ @@ -427,7 +432,7 @@ block_item_decl /* Integer declarations are simpler in that they do not have all the trappings of a general variable declaration. All of that is - implicit in the "integer" of the declaratin. */ + implicit in the "integer" of the declaration. */ | attribute_list_opt K_integer register_variable_list ';' { pform_set_reg_integer($3); @@ -440,7 +445,7 @@ block_item_decl /* real declarations are fairly simple as there is no range of signed flag in the declaration. Create the real as a NetNet::REG - with real value. Note that real and realtime are interchangable + with real value. Note that real and realtime are interchangeable in this context. */ | attribute_list_opt K_real real_variable_list ';' @@ -1181,7 +1186,7 @@ expr_primary } /* Many of the VAMS built-in functions are available as builtin - functions with $system_function equivilents. */ + functions with $system_function equivalents. */ | K_acos '(' expression ')' { perm_string tn = perm_string::literal("$acos"); @@ -2195,6 +2200,15 @@ 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_count += 1; + cerr << @2 << ": warning: Anachronistic use of begin/end to surround generate schemes." << endl; + } + } + /* specify blocks are parsed but ignored. */ | K_specify K_endspecify @@ -2379,7 +2393,14 @@ parameter_assign_decl param_active_type = IVL_VT_LOGIC; } | K_integer - { param_active_range = 0; + { svector*range_stub = new svector(2); + PExpr*re; + re = new PENumber(new verinum(integer_width-1, integer_width)); + (*range_stub)[0] = re; + re = new PENumber(new verinum((uint64_t)0, integer_width)); + (*range_stub)[1] = re; + /* The default range is [31:0] */ + param_active_range = range_stub; param_active_signed = true; param_active_type = IVL_VT_LOGIC; } @@ -2388,6 +2409,23 @@ parameter_assign_decl param_active_signed = false; param_active_type = IVL_VT_LOGIC; } + | K_time + { svector*range_stub = new svector(2); + PExpr*re; + re = new PENumber(new verinum((uint64_t)63, integer_width)); + (*range_stub)[0] = re; + re = new PENumber(new verinum((uint64_t)0, integer_width)); + (*range_stub)[1] = re; + /* The range is [63:0] */ + param_active_range = range_stub; + param_active_signed = false; + param_active_type = IVL_VT_LOGIC; + } + parameter_assign_list + { param_active_range = 0; + param_active_signed = false; + param_active_type = IVL_VT_LOGIC; + } | K_real { param_active_range = 0; param_active_signed = true; @@ -2398,6 +2436,16 @@ parameter_assign_decl param_active_signed = false; param_active_type = IVL_VT_LOGIC; } + | K_realtime + { param_active_range = 0; + param_active_signed = true; + param_active_type = IVL_VT_REAL; + } + parameter_assign_list + { param_active_range = 0; + param_active_signed = false; + param_active_type = IVL_VT_LOGIC; + } ; parameter_assign_list @@ -2432,7 +2480,8 @@ parameter_value_range { $$ = pform_parameter_value_range($1, true, $3, false, $5); } | from_exclude '(' value_range_expression ':' value_range_expression ')' { $$ = pform_parameter_value_range($1, true, $3, true, $5); } - /* | K_exclude expression */ + | K_exclude expression + { $$ = pform_parameter_value_range(true, false, $2, false, $2); } ; value_range_expression @@ -2480,17 +2529,41 @@ localparam_assign_decl localparam_assign_list { param_active_range = 0; param_active_signed = false; - param_active_type = IVL_VT_NO_TYPE; + param_active_type = IVL_VT_LOGIC; } | K_integer - { param_active_range = 0; + { svector*range_stub = new svector(2); + PExpr*re; + re = new PENumber(new verinum(integer_width-1, integer_width)); + (*range_stub)[0] = re; + re = new PENumber(new verinum((uint64_t)0, integer_width)); + (*range_stub)[1] = re; + /* The default range is [31:0] */ + param_active_range = range_stub; param_active_signed = true; param_active_type = IVL_VT_LOGIC; } localparam_assign_list { param_active_range = 0; param_active_signed = false; - param_active_type = IVL_VT_NO_TYPE; + param_active_type = IVL_VT_LOGIC; + } + | K_time + { svector*range_stub = new svector(2); + PExpr*re; + re = new PENumber(new verinum((uint64_t)63, integer_width)); + (*range_stub)[0] = re; + re = new PENumber(new verinum((uint64_t)0, integer_width)); + (*range_stub)[1] = re; + /* The range is [63:0] */ + param_active_range = range_stub; + param_active_signed = false; + param_active_type = IVL_VT_LOGIC; + } + localparam_assign_list + { param_active_range = 0; + param_active_signed = false; + param_active_type = IVL_VT_LOGIC; } | K_real { param_active_range = 0; @@ -2500,7 +2573,17 @@ localparam_assign_decl localparam_assign_list { param_active_range = 0; param_active_signed = false; - param_active_type = IVL_VT_NO_TYPE; + param_active_type = IVL_VT_LOGIC; + } + | K_realtime + { param_active_range = 0; + param_active_signed = true; + param_active_type = IVL_VT_REAL; + } + localparam_assign_list + { param_active_range = 0; + param_active_signed = false; + param_active_type = IVL_VT_LOGIC; } ; diff --git a/parse_misc.cc b/parse_misc.cc index 60bd8b02e..546dec7cf 100644 --- a/parse_misc.cc +++ b/parse_misc.cc @@ -30,6 +30,15 @@ unsigned error_count = 0; unsigned warn_count = 0; unsigned long based_size = 0; +std::ostream& operator << (std::ostream&o, const YYLTYPE&loc) +{ + if (loc.text) + o << loc.text << ":"; + o << loc.first_line; + return o; +} + + void VLerror(const char*msg) { error_count += 1; @@ -39,20 +48,14 @@ void VLerror(const char*msg) void VLerror(const YYLTYPE&loc, const char*msg) { error_count += 1; - if (loc.text) - cerr << loc.text << ":"; - - cerr << loc.first_line << ": " << msg << endl; + cerr << loc << ": " << msg << endl; based_size = 0; /* Clear the base information if we have an error. */ } void yywarn(const YYLTYPE&loc, const char*msg) { warn_count += 1; - if (loc.text) - cerr << loc.text << ":"; - - cerr << loc.first_line << ": warning: " << msg << endl; + cerr << loc << ": warning: " << msg << endl; } int VLwrap() diff --git a/parse_misc.h b/parse_misc.h index 3960f1176..1d3e721a5 100644 --- a/parse_misc.h +++ b/parse_misc.h @@ -23,6 +23,7 @@ #endif # include +# include # include "compiler.h" # include "pform.h" @@ -62,6 +63,8 @@ extern void VLerror(const YYLTYPE&loc, const char*msg); #define yywarn VLwarn extern void VLwarn(const YYLTYPE&loc, const char*msg); +extern ostream& operator << (ostream&, const YYLTYPE&loc); + extern unsigned error_count, warn_count; extern unsigned long based_size; diff --git a/pform.cc b/pform.cc index 4aa94080d..022a30461 100644 --- a/pform.cc +++ b/pform.cc @@ -93,40 +93,64 @@ static PScope* lexical_scope = 0; void pform_pop_scope() { - lexical_scope = lexical_scope->pscope_parent(); + if (pform_cur_generate) { + assert(pform_cur_generate->lexical_scope); + PScope*cur = pform_cur_generate->lexical_scope; + pform_cur_generate->lexical_scope = cur->pscope_parent(); + } else { + assert(lexical_scope); + lexical_scope = lexical_scope->pscope_parent(); + } } PTask* pform_push_task_scope(char*name) { perm_string task_name = lex_strings.make(name); - PTask*task = new PTask(task_name, pform_cur_module); - // Add the task to the current module - pform_cur_module->add_task(task->pscope_name(), task); - // Make this the current lexical scope - lexical_scope = task; + PTask*task; + if (pform_cur_generate) { + task = new PTask(task_name, pform_cur_generate->lexical_scope); + pform_cur_generate->tasks[task->pscope_name()] = task; + pform_cur_generate->lexical_scope = task; + } else { + task = new PTask(task_name, lexical_scope); + pform_cur_module->tasks[task->pscope_name()] = task; + lexical_scope = task; + } + return task; } PFunction* pform_push_function_scope(char*name) { perm_string func_name = lex_strings.make(name); - PFunction*func = new PFunction(func_name, lexical_scope); - // Add the task to the current module - pform_cur_module->add_function(func->pscope_name(), func); - // Make this the current lexical scope - lexical_scope = func; + PFunction*func; + if (pform_cur_generate) { + func = new PFunction(func_name, pform_cur_generate->lexical_scope); + pform_cur_generate->funcs[func->pscope_name()] = func; + pform_cur_generate->lexical_scope = func; + } else { + func = new PFunction(func_name, lexical_scope); + pform_cur_module->funcs[func->pscope_name()] = func; + lexical_scope = func; + } + return func; } PBlock* pform_push_block_scope(char*name, PBlock::BL_TYPE bt) { perm_string block_name = lex_strings.make(name); - PBlock*block = new PBlock(block_name, lexical_scope, bt); - // Make this the current lexical scope - lexical_scope = block; + PBlock*block; + if (pform_cur_generate) { + block = new PBlock(block_name, pform_cur_generate->lexical_scope, bt); + pform_cur_generate->lexical_scope = block; + } else { + block = new PBlock(block_name, lexical_scope, bt); + lexical_scope = block; + } return block; } @@ -138,9 +162,34 @@ PWire*pform_get_wire_in_scope(perm_string name) cannot be within sub-scopes. Only directly in modules. */ if (pform_cur_generate) - return pform_cur_generate->get_wire(name); + if (pform_cur_generate->lexical_scope) + return pform_cur_generate->lexical_scope->wires_find(name); + else + return pform_cur_generate->wires_find(name); + else + return lexical_scope->wires_find(name); +} - return lexical_scope->wires_find(name); +static void pform_put_wire_in_scope(perm_string name, PWire*net) +{ + if (pform_cur_generate) + if (pform_cur_generate->lexical_scope) + pform_cur_generate->lexical_scope->wires[name] = net; + else + pform_cur_generate->wires[name] = net; + else + lexical_scope->wires[name] = net; +} + +static void pform_put_behavior_in_scope(PProcess*pp) +{ + if (pform_cur_generate) + if (pform_cur_generate->lexical_scope) + pform_cur_generate->lexical_scope->behaviors.push_back(pp); + else + pform_cur_generate->behaviors.push_back(pp); + else + lexical_scope->behaviors.push_back(pp); } void pform_set_default_nettype(NetNet::Type type, @@ -508,7 +557,7 @@ void pform_endgenerate() if (pform_cur_generate != 0) { assert(cur->scheme_type == PGenerate::GS_CASE_ITEM || pform_cur_generate->scheme_type != PGenerate::GS_CASE); - pform_cur_generate->generates.push_back(cur); + pform_cur_generate->generate_schemes.push_back(cur); } else { assert(cur->scheme_type != PGenerate::GS_CASE_ITEM); pform_cur_module->generate_schemes.push_back(cur); @@ -1236,7 +1285,7 @@ void pform_make_pgassign_list(svector*alist, void pform_make_reginit(const struct vlltype&li, perm_string name, PExpr*expr) { - PWire*cur = lexical_scope->wires_find(name); + PWire*cur = pform_get_wire_in_scope(name); if (cur == 0) { VLerror(li, "internal error: reginit to non-register?"); delete expr; @@ -1250,7 +1299,7 @@ void pform_make_reginit(const struct vlltype&li, PProcess*top = new PProcess(PProcess::PR_INITIAL, ass); FILE_NAME(top, li); - lexical_scope->behaviors.push_back(top); + pform_put_behavior_in_scope(top); } /* @@ -1271,7 +1320,7 @@ void pform_module_define_port(const struct vlltype&li, svector*range, svector*attr) { - PWire*cur = lexical_scope->wires_find(name); + PWire*cur = pform_get_wire_in_scope(name); if (cur) { ostringstream msg; msg << name << " definition conflicts with " @@ -1305,7 +1354,7 @@ void pform_module_define_port(const struct vlltype&li, cur->attributes[tmp->name] = tmp->parm; } } - lexical_scope->wires[name] = cur; + pform_put_wire_in_scope(name, cur); } /* @@ -1388,12 +1437,8 @@ void pform_makewire(const vlltype&li, perm_string name, } } - if (new_wire_flag) { - if (pform_cur_generate) - pform_cur_generate->wires[name] = cur; - else - lexical_scope->wires[name] = cur; - } + if (new_wire_flag) + pform_put_wire_in_scope(name, cur); } /* @@ -1469,11 +1514,11 @@ void pform_makewire(const vlltype&li, void pform_set_port_type(perm_string name, NetNet::PortType pt, const char*file, unsigned lineno) { - PWire*cur = lexical_scope->wires_find(name); + PWire*cur = pform_get_wire_in_scope(name); if (cur == 0) { cur = new PWire(name, NetNet::IMPLICIT, NetNet::PIMPLICIT, IVL_VT_NO_TYPE); FILE_NAME(cur, file, lineno); - lexical_scope->wires[name] = cur; + pform_put_wire_in_scope(name, cur); } switch (cur->get_port_type()) { @@ -1555,13 +1600,13 @@ svector*pform_make_task_ports(NetNet::PortType pt, /* Look for a preexisting wire. If it exists, set the port direction. If not, create it. */ - PWire*curw = lexical_scope->wires_find(name); + PWire*curw = pform_get_wire_in_scope(name); if (curw) { curw->set_port_type(pt); } else { curw = new PWire(name, NetNet::IMPLICIT_REG, pt, vtype); FILE_NAME(curw, file, lineno); - lexical_scope->wires[name] = curw; + pform_put_wire_in_scope(name, curw); } curw->set_signed(signed_flag); @@ -1622,7 +1667,7 @@ void pform_set_reg_idx(perm_string name, PExpr*l, PExpr*r) { PWire*cur = 0; if (pform_cur_generate) { - cur = pform_cur_generate->get_wire(name); + cur = pform_cur_generate->wires_find(name); } else { cur = lexical_scope->wires_find(name); } @@ -1805,13 +1850,13 @@ void pform_set_port_type(const struct vlltype&li, static void pform_set_reg_integer(perm_string name) { - PWire*cur = lexical_scope->wires_find(name); + PWire*cur = pform_get_wire_in_scope(name); if (cur == 0) { cur = new PWire(name, NetNet::INTEGER, NetNet::NOT_A_PORT, IVL_VT_LOGIC); cur->set_signed(true); - lexical_scope->wires[name] = cur; + pform_put_wire_in_scope(name, cur); } else { bool rc = cur->set_wire_type(NetNet::INTEGER); assert(rc); @@ -1839,10 +1884,10 @@ void pform_set_reg_integer(list*names) static void pform_set_reg_time(perm_string name) { - PWire*cur = lexical_scope->wires_find(name); + PWire*cur = pform_get_wire_in_scope(name); if (cur == 0) { cur = new PWire(name, NetNet::REG, NetNet::NOT_A_PORT, IVL_VT_LOGIC); - lexical_scope->wires[name] = cur; + pform_put_wire_in_scope(name, cur); } else { bool rc = cur->set_wire_type(NetNet::REG); assert(rc); @@ -1901,11 +1946,7 @@ PProcess* pform_make_behavior(PProcess::Type type, Statement*st, delete attr; } - if (pform_cur_generate) - pform_cur_generate->add_behavior(pp); - else - pform_cur_module->behaviors.push_back(pp); - + pform_put_behavior_in_scope(pp); return pp; } diff --git a/pform_dump.cc b/pform_dump.cc index c3e709ad4..94bf55d59 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -937,8 +937,8 @@ void PGenerate::dump(ostream&out, unsigned indent) const (*idx)->dump(out, indent+2); } - for (list::const_iterator idx = generates.begin() - ; idx != generates.end() ; idx++) { + for (list::const_iterator idx = generate_schemes.begin() + ; idx != generate_schemes.end() ; idx++) { (*idx)->dump(out, indent+2); } @@ -1089,8 +1089,8 @@ void Module::dump(ostream&out) const // Dump the task definitions. typedef map::const_iterator task_iter_t; - for (task_iter_t cur = tasks_.begin() - ; cur != tasks_.end() ; cur ++) { + for (task_iter_t cur = tasks.begin() + ; cur != tasks.end() ; cur ++) { out << " task " << (*cur).first << ";" << endl; (*cur).second->dump(out, 6); out << " endtask;" << endl; @@ -1098,8 +1098,8 @@ void Module::dump(ostream&out) const // Dump the function definitions. typedef map::const_iterator func_iter_t; - for (func_iter_t cur = funcs_.begin() - ; cur != funcs_.end() ; cur ++) { + for (func_iter_t cur = funcs.begin() + ; cur != funcs.end() ; cur ++) { out << " function " << (*cur).first << ";" << endl; (*cur).second->dump(out, 6); out << " endfunction;" << endl; diff --git a/scripts/CREATE_VERSION.sh b/scripts/CREATE_VERSION.sh new file mode 100644 index 000000000..2cbec4707 --- /dev/null +++ b/scripts/CREATE_VERSION.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# This script manually creates a version.h file. +# +# It is used when creating a MinGW executable from a Cygwin +# hosted git repository. It assumes that git is available. +# +# sh scripts/CREATE_VERSION.sh +# + +echo "Building verion.h with git describe" +tmp=`git describe | sed -e 's;\(.*\);#define VERSION_TAG "\1";'` +echo "$tmp" > version.h diff --git a/scripts/MAKE_SNAPSHOT.sh b/scripts/MAKE_SNAPSHOT.sh index b06437b7f..aacf28ac6 100644 --- a/scripts/MAKE_SNAPSHOT.sh +++ b/scripts/MAKE_SNAPSHOT.sh @@ -7,7 +7,7 @@ # sh scripts/MAKE_SNAPSHOT.sh 20080428 ~/tmp # # The above assumes that there is a tag "s20080428" at the point -# to be snaphot. (The tag has the "s", but the argument to this +# to be snapshot. (The tag has the "s", but the argument to this # script does not have the "s"). This script extracts based on the # tag, uses the temporary directory to stage intermediate results, # and finally creates a file called verilog-20080428.tar.gz that diff --git a/synth2.cc b/synth2.cc index 0217554f1..812dceb03 100644 --- a/synth2.cc +++ b/synth2.cc @@ -598,7 +598,7 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope, NetFF*ff, delete expr_input; /* Detect the case that this is a *synchronous* set/reset. It - is not asyncronous because we know the condition is not + is not asynchronous because we know the condition is not included in the sensitivity list, but if the if_ case is constant (has no inputs) then we can model this as a synchronous set/reset. diff --git a/t-dll-api.cc b/t-dll-api.cc index aa2733854..cf3c9b83c 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -555,6 +555,28 @@ extern "C" unsigned ivl_file_table_size() return fn_vector.size(); } +extern "C" int ivl_island_flag_set(ivl_island_t net, unsigned flag, int value) +{ + if (flag >= net->flags.size()) { + if (value == 0) + return 0; + else + net->flags.resize(flag+1, false); + } + + int old_flag = net->flags[flag]; + net->flags[flag] = value != 0; + return old_flag; +} + +extern "C" int ivl_island_flag_test(ivl_island_t net, unsigned flag) +{ + if (flag >= net->flags.size()) + return 0; + else + return net->flags[flag]; +} + extern "C" const char* ivl_logic_attr(ivl_net_logic_t net, const char*key) { assert(net); @@ -782,7 +804,6 @@ extern "C" unsigned ivl_lpm_base(ivl_lpm_t net) switch (net->type) { case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: - case IVL_LPM_PART_BI: return net->u_.part.base; default: assert(0); @@ -865,6 +886,8 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx) assert(net); switch (net->type) { case IVL_LPM_ABS: + case IVL_LPM_CAST_INT: + case IVL_LPM_CAST_REAL: assert(idx == 0); return net->u_.arith.a; @@ -918,7 +941,6 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx) case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: - case IVL_LPM_PART_BI: assert(idx <= 1); if (idx == 0) return net->u_.part.a; @@ -1008,20 +1030,19 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net, unsigned idx) switch (net->type) { case IVL_LPM_ABS: case IVL_LPM_ADD: - case IVL_LPM_DIVIDE: - case IVL_LPM_MOD: - case IVL_LPM_MULT: - case IVL_LPM_POW: - case IVL_LPM_SUB: - assert(idx == 0); - return net->u_.arith.q; - + case IVL_LPM_CAST_INT: + case IVL_LPM_CAST_REAL: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_NEE: + case IVL_LPM_DIVIDE: + case IVL_LPM_MOD: + case IVL_LPM_MULT: + case IVL_LPM_POW: + case IVL_LPM_SUB: assert(idx == 0); return net->u_.arith.q; @@ -1061,7 +1082,6 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net, unsigned idx) case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: - case IVL_LPM_PART_BI: assert(idx == 0); return net->u_.part.q; @@ -1125,6 +1145,7 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net) return 0; case IVL_LPM_ABS: case IVL_LPM_ADD: + case IVL_LPM_CAST_REAL: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_GE: @@ -1147,6 +1168,7 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net) case IVL_LPM_SHIFTL: case IVL_LPM_SHIFTR: return net->u_.shift.signed_flag; + case IVL_LPM_CAST_INT: case IVL_LPM_SIGN_EXT: // Sign extend is always signed. return 1; case IVL_LPM_SFUNC: @@ -1157,7 +1179,6 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net) return 0; case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: - case IVL_LPM_PART_BI: return net->u_.part.signed_flag; case IVL_LPM_REPEAT: return 0; @@ -1338,6 +1359,15 @@ extern "C" ivl_signal_t ivl_nexus_ptr_sig(ivl_nexus_ptr_t net) return net->l.sig; } +extern "C" ivl_switch_t ivl_nexus_ptr_switch(ivl_nexus_ptr_t net) +{ + if (net == 0) + return 0; + if (net->type_ != __NEXUS_PTR_SWI) + return 0; + return net->l.swi; +} + extern "C" const char* ivl_parameter_basename(ivl_parameter_t net) { assert(net); @@ -2162,6 +2192,11 @@ extern "C" const char*ivl_switch_basename(ivl_switch_t net) return net->name; } +extern "C" ivl_scope_t ivl_switch_scope(ivl_switch_t net) +{ + return net->scope; +} + extern "C" ivl_switch_type_t ivl_switch_type(ivl_switch_t net) { return net->type; @@ -2181,3 +2216,33 @@ extern "C" ivl_nexus_t ivl_switch_enable(ivl_switch_t net) { return net->pins[2]; } + +extern "C" unsigned ivl_switch_width(ivl_switch_t net) +{ + return net->width; +} + +extern "C" unsigned ivl_switch_part(ivl_switch_t net) +{ + return net->part; +} + +extern "C" unsigned ivl_switch_offset(ivl_switch_t net) +{ + return net->offset; +} + +extern "C" const char* ivl_switch_file(ivl_switch_t net) +{ + return net->file; +} + +extern "C" ivl_island_t ivl_switch_island(ivl_switch_t net) +{ + return net->island; +} + +extern "C" unsigned ivl_switch_lineno(ivl_switch_t net) +{ + return net->lineno; +} diff --git a/t-dll.cc b/t-dll.cc index 15cc19eac..5b6a22a52 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -1037,9 +1037,14 @@ bool dll_target::tran(const NetTran*net) { struct ivl_switch_s*obj = new struct ivl_switch_s; obj->type = net->type(); + obj->width = 0; + obj->part = 0; + obj->offset = 0; obj->name = net->name(); obj->scope = find_scope(des_, net->scope()); + obj->island = net->island; assert(obj->scope); + assert(obj->island); const Nexus*nex; @@ -1063,6 +1068,15 @@ bool dll_target::tran(const NetTran*net) obj->pins[2] = 0; } + if (obj->type == IVL_SW_TRAN_VP) { + obj->width = net->vector_width(); + obj->part = net->part_width(); + obj->offset= net->part_offset(); + } + + obj->file = net->get_file(); + obj->lineno = net->get_lineno(); + switch_attributes(obj, net); scope_add_switch(obj->scope, obj); @@ -1536,6 +1550,71 @@ void dll_target::lpm_clshift(const NetCLShift*net) scope_add_lpm(obj->scope, obj); } +bool dll_target::lpm_cast_int(const NetCastInt*net) +{ + ivl_lpm_t obj = new struct ivl_lpm_s; + obj->type = IVL_LPM_CAST_INT; + obj->name = net->name(); // NetCastInt names are permallocated + assert(net->scope()); + obj->scope = find_scope(des_, net->scope()); + assert(obj->scope); + + obj->width = net->width(); + + const Nexus*nex; + + nex = net->pin(0).nexus(); + assert(nex->t_cookie()); + + obj->u_.arith.q = nex->t_cookie(); + + nex = net->pin(1).nexus(); + assert(nex->t_cookie()); + obj->u_.arith.a = nex->t_cookie(); + + nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); + nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); + + make_lpm_delays_(obj, net); + + scope_add_lpm(obj->scope, obj); + + return true; +} + +bool dll_target::lpm_cast_real(const NetCastReal*net) +{ + ivl_lpm_t obj = new struct ivl_lpm_s; + obj->type = IVL_LPM_CAST_REAL; + obj->name = net->name(); // NetCastReal names are permallocated + assert(net->scope()); + obj->scope = find_scope(des_, net->scope()); + assert(obj->scope); + + obj->width = 0; + obj->u_.arith.signed_flag = net->signed_flag()? 1 : 0; + + const Nexus*nex; + + nex = net->pin(0).nexus(); + assert(nex->t_cookie()); + + obj->u_.arith.q = nex->t_cookie(); + + nex = net->pin(1).nexus(); + assert(nex->t_cookie()); + obj->u_.arith.a = nex->t_cookie(); + + nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); + nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); + + make_lpm_delays_(obj, net); + + scope_add_lpm(obj->scope, obj); + + return true; +} + /* * Make out of the NetCompare object an ivl_lpm_s object. The * comparators in ivl_target do not support < or <=, but they can be @@ -1991,9 +2070,6 @@ bool dll_target::part_select(const NetPartSelect*net) case NetPartSelect::PV: obj->type = IVL_LPM_PART_PV; break; - case NetPartSelect::BI: - obj->type = IVL_LPM_PART_BI; - break; } obj->name = net->name(); // NetPartSelect names are permallocated. assert(net->scope()); @@ -2047,35 +2123,12 @@ bool dll_target::part_select(const NetPartSelect*net) obj->u_.part.a = nex->t_cookie(); break; - case IVL_LPM_PART_BI: - /* For now, handle this exactly the same as a PV */ - - /* NetPartSelect:pin(0) is the output pin. */ - nex = net->pin(0).nexus(); - assert(nex->t_cookie()); - - obj->u_.part.q = nex->t_cookie(); - - /* NetPartSelect:pin(1) is the input pin. */ - nex = net->pin(1).nexus(); - assert(nex->t_cookie()); - - obj->u_.part.a = nex->t_cookie(); - break; - default: assert(0); } nexus_lpm_add(obj->u_.part.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); - - /* If the device is a PART_BI, then the "input" is also a - strength aware output, so attach it to the nexus with - strong driver. */ - if (obj->type == IVL_LPM_PART_BI) - nexus_lpm_add(obj->u_.part.a, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); - else - nexus_lpm_add(obj->u_.part.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); + nexus_lpm_add(obj->u_.part.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); /* The select input is optional. */ if (obj->u_.part.s) diff --git a/t-dll.h b/t-dll.h index 247a5dae7..5bc4c3fb4 100644 --- a/t-dll.h +++ b/t-dll.h @@ -21,6 +21,7 @@ # include "target.h" # include "ivl_target.h" +# include "ivl_target_priv.h" # include "StringHeap.h" # include "netlist.h" # include @@ -75,6 +76,8 @@ struct dll_target : public target_t, public expr_scan_t { void lpm_abs(const NetAbs*); void lpm_add_sub(const NetAddSub*); bool lpm_array_dq(const NetArrayDq*); + bool lpm_cast_int(const NetCastInt*); + bool lpm_cast_real(const NetCastReal*); void lpm_clshift(const NetCLShift*); void lpm_compare(const NetCompare*); void lpm_divide(const NetDivide*); @@ -450,9 +453,13 @@ struct ivl_net_logic_s { struct ivl_switch_s { ivl_switch_type_t type; + unsigned width; + unsigned part; + unsigned offset; perm_string name; ivl_scope_t scope; + ivl_island_t island; struct ivl_attribute_s*attr; unsigned nattr; diff --git a/target.cc b/target.cc index e474c7cf7..10e900027 100644 --- a/target.cc +++ b/target.cc @@ -107,6 +107,20 @@ bool target_t::lpm_array_dq(const NetArrayDq*) return false; } +bool target_t::lpm_cast_int(const NetCastInt*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled NetCastInt." << endl; + return false; +} + +bool target_t::lpm_cast_real(const NetCastReal*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled NetCastReal." << endl; + return false; +} + void target_t::lpm_clshift(const NetCLShift*) { cerr << "target (" << typeid(*this).name() << "): " diff --git a/target.h b/target.h index d002a9797..03fba0886 100644 --- a/target.h +++ b/target.h @@ -72,6 +72,8 @@ struct target_t { virtual void lpm_add_sub(const NetAddSub*); virtual bool lpm_array_dq(const NetArrayDq*); virtual void lpm_clshift(const NetCLShift*); + virtual bool lpm_cast_int(const NetCastInt*); + virtual bool lpm_cast_real(const NetCastReal*); virtual void lpm_compare(const NetCompare*); virtual void lpm_divide(const NetDivide*); virtual void lpm_modulo(const NetModulo*); diff --git a/tgt-stub/expression.c b/tgt-stub/expression.c index 500a82bab..823d359a6 100644 --- a/tgt-stub/expression.c +++ b/tgt-stub/expression.c @@ -178,6 +178,11 @@ void show_unary_expression(ivl_expr_t net, unsigned ind) break; } + if (ivl_expr_opcode(net) == '!' && ivl_expr_type(net)==IVL_VT_REAL) { + fprintf(out, "%*sERROR: Real argument to unary ! !?\n", ind,""); + stub_errors += 1; + } + fprintf(out, "%*s\n", ind, "", name, width, sign, vt); show_expression(ivl_expr_oper1(net), ind+4); diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index 5d390800f..927312765 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -240,6 +240,56 @@ static void show_lpm_array(ivl_lpm_t net) } } +static void show_lpm_cast_int(ivl_lpm_t net) +{ + unsigned width = ivl_lpm_width(net); + + fprintf(out, " LPM_CAST_INT %s: \n", + ivl_lpm_basename(net), width); + + ivl_nexus_t q = ivl_lpm_q(net,0); + ivl_nexus_t a = ivl_lpm_data(net,0); + fprintf(out, " O: %s\n", ivl_nexus_name(ivl_lpm_q(net,0))); + fprintf(out, " A: %s\n", ivl_nexus_name(ivl_lpm_data(net,0))); + + if (type_of_nexus(q) == IVL_VT_REAL) { + fprintf(out, " ERROR: Data type of Q is %s, expecting !real\n", + data_type_string(type_of_nexus(q))); + stub_errors += 1; + } + + if (type_of_nexus(a) != IVL_VT_REAL) { + fprintf(out, " ERROR: Data type of A is %s, expecting real\n", + data_type_string(type_of_nexus(a))); + stub_errors += 1; + } +} + +static void show_lpm_cast_real(ivl_lpm_t net) +{ + unsigned width = ivl_lpm_width(net); + + fprintf(out, " LPM_CAST_REAL %s: \n", + ivl_lpm_basename(net), width); + + ivl_nexus_t q = ivl_lpm_q(net,0); + ivl_nexus_t a = ivl_lpm_data(net,0); + fprintf(out, " O: %s\n", ivl_nexus_name(ivl_lpm_q(net,0))); + fprintf(out, " A: %s\n", ivl_nexus_name(ivl_lpm_data(net,0))); + + if (type_of_nexus(q) != IVL_VT_REAL) { + fprintf(out, " ERROR: Data type of Q is %s, expecting real\n", + data_type_string(type_of_nexus(q))); + stub_errors += 1; + } + + if (type_of_nexus(a) == IVL_VT_REAL) { + fprintf(out, " ERROR: Data type of A is %s, expecting !real\n", + data_type_string(type_of_nexus(a))); + stub_errors += 1; + } +} + static void show_lpm_divide(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); @@ -555,38 +605,6 @@ static void show_lpm_part(ivl_lpm_t net) } } -static void show_lpm_part_bi(ivl_lpm_t net) -{ - unsigned width = ivl_lpm_width(net); - unsigned base = ivl_lpm_base(net); - ivl_nexus_t port_p = ivl_lpm_q(net,0); - ivl_nexus_t port_v = ivl_lpm_data(net,0); - - fprintf(out, " LPM_PART_BI %s: \n", - ivl_lpm_basename(net), width, base, ivl_lpm_signed(net)); - fprintf(out, " P: %s\n", ivl_nexus_name(port_p)); - fprintf(out, " V: %s \n", ivl_nexus_name(port_v), - width_of_nexus(port_v)); - - - /* The data(0) port must be large enough for the part select. */ - if (width_of_nexus(ivl_lpm_data(net,0)) < (width+base)) { - fprintf(out, " ERROR: Part select is out of range." - " Data nexus width=%u, width+base=%u\n", - width_of_nexus(ivl_lpm_data(net,0)), width+base); - stub_errors += 1; - } - - /* The Q vector must be exactly the width of the part select. */ - if (width_of_nexus(ivl_lpm_q(net,0)) != width) { - fprintf(out, " ERROR: Part select input mismatch." - " Nexus width=%u, expect width=%u\n", - width_of_nexus(ivl_lpm_q(net,0)), width); - stub_errors += 1; - } -} - - /* * The reduction operators have similar characteristics and are * displayed here. @@ -834,6 +852,14 @@ static void show_lpm(ivl_lpm_t net) show_lpm_array(net); break; + case IVL_LPM_CAST_INT: + show_lpm_cast_int(net); + break; + + case IVL_LPM_CAST_REAL: + show_lpm_cast_real(net); + break; + case IVL_LPM_DIVIDE: show_lpm_divide(net); break; @@ -905,11 +931,6 @@ static void show_lpm(ivl_lpm_t net) show_lpm_part(net); break; - /* The BI part select is slightly special. */ - case IVL_LPM_PART_BI: - show_lpm_part_bi(net); - break; - case IVL_LPM_REPEAT: show_lpm_repeat(net); break; @@ -1059,6 +1080,7 @@ static void show_nexus_details(ivl_signal_t net, ivl_nexus_t nex) ivl_net_logic_t log; ivl_lpm_t lpm; ivl_signal_t sig; + ivl_switch_t swt; ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); const char*dr0 = str_tab[ivl_nexus_ptr_drive0(ptr)]; @@ -1092,6 +1114,11 @@ static void show_nexus_details(ivl_signal_t net, ivl_nexus_t nex) ivl_scope_name(ivl_lpm_scope(lpm)), ivl_lpm_basename(lpm), dr0, dr1); + } else if ((swt = ivl_nexus_ptr_switch(ptr))) { + fprintf(out, " SWITCH %s.%s\n", + ivl_scope_name(ivl_switch_scope(swt)), + ivl_switch_basename(swt)); + } else if ((con = ivl_nexus_ptr_con(ptr))) { signal_nexus_const(net, ptr, con); diff --git a/tgt-stub/switches.c b/tgt-stub/switches.c index 79dac554a..5fa383ab4 100644 --- a/tgt-stub/switches.c +++ b/tgt-stub/switches.c @@ -52,18 +52,23 @@ void show_switch(ivl_switch_t net) fprintf(out, " rtranif1 %s", name); has_enable = 1; break; + case IVL_SW_TRAN_VP: + fprintf(out, " tran(VP wid=%u, part=%u, off=%u) %s", + ivl_switch_width(net), ivl_switch_part(net), + ivl_switch_offset(net), name); + break; } - fprintf(out, "\n"); + fprintf(out, " island=%p\n", ivl_switch_island(net)); - ivl_nexus_t nex = ivl_switch_a(net); - const char*nex_name = nex? ivl_nexus_name(nex) : ""; - ivl_variable_type_t nex_type_a = nex? type_of_nexus(nex) : IVL_VT_NO_TYPE; + ivl_nexus_t nexa = ivl_switch_a(net); + const char*nex_name = nexa? ivl_nexus_name(nexa) : ""; + ivl_variable_type_t nex_type_a = nexa? type_of_nexus(nexa) : IVL_VT_NO_TYPE; fprintf(out, " A: %s \n", nex_name, data_type_string(nex_type_a)); - nex = ivl_switch_b(net); - nex_name = nex? ivl_nexus_name(nex) : ""; - ivl_variable_type_t nex_type_b = nex? type_of_nexus(nex) : IVL_VT_NO_TYPE; + ivl_nexus_t nexb = ivl_switch_b(net); + nex_name = nexb? ivl_nexus_name(nexb) : ""; + ivl_variable_type_t nex_type_b = nexb? type_of_nexus(nexb) : IVL_VT_NO_TYPE; fprintf(out, " B: %s \n", nex_name, data_type_string(nex_type_b)); /* The A/B pins of the switch must be present, and must match. */ @@ -80,13 +85,40 @@ void show_switch(ivl_switch_t net) stub_errors += 1; } + if (ivl_switch_type(net) == IVL_SW_TRAN_VP) { + /* The TRAN_VP nodes are special in that the specific + width matters for each port and should be exactly + right for both. */ + if (width_of_nexus(nexa) != ivl_switch_width(net)) { + fprintf(out, " A: ERROR: part vector nexus " + "width=%u, expecting width=%u\n", + width_of_nexus(nexa), ivl_switch_width(net)); + stub_errors += 1; + } + if (width_of_nexus(nexb) != ivl_switch_part(net)) { + fprintf(out, " B: ERROR: part select nexus " + "width=%u, expecting width=%u\n", + width_of_nexus(nexb), ivl_switch_part(net)); + stub_errors += 1; + } + } else { + /* All other TRAN nodes will have matching vector + widths, but the actual value doesn't matter. */ + if (width_of_nexus(nexa) != width_of_nexus(nexb)) { + fprintf(out, " A/B: ERROR: Width of ports don't match" + ": A=%u, B=%u\n", + width_of_nexus(nexa), width_of_nexus(nexb)); + stub_errors += 1; + } + } + if (has_enable) { - nex = ivl_switch_enable(net); - nex_name = nex? ivl_nexus_name(nex) : ""; + ivl_nexus_t nexe = ivl_switch_enable(net); + nex_name = nexe? ivl_nexus_name(nexe) : ""; fprintf(out, " E: %s\n", nex_name); - if (width_of_nexus(nex) != 1) { + if (width_of_nexus(nexe) != 1) { fprintf(out, " E: ERROR: Nexus width is %u\n", - width_of_nexus(nex)); + width_of_nexus(nexe)); } } } diff --git a/tgt-vvp/Makefile.in b/tgt-vvp/Makefile.in index c9b6b5098..323212332 100644 --- a/tgt-vvp/Makefile.in +++ b/tgt-vvp/Makefile.in @@ -51,7 +51,8 @@ dep: $(CC) $(CPPFLAGS) $(CFLAGS) -MD -c $< -o $*.o mv $*.d dep -O = vvp.o draw_mux.o draw_ufunc.o draw_vpi.o eval_bool.o eval_expr.o \ +O = vvp.o draw_mux.o draw_net_input.o draw_switch.o draw_ufunc.o draw_vpi.o \ +eval_bool.o eval_expr.o \ eval_real.o modpath.o vector.o \ vvp_process.o vvp_scope.o diff --git a/tgt-vvp/draw_mux.c b/tgt-vvp/draw_mux.c index 019ef32b7..c5c0861f2 100644 --- a/tgt-vvp/draw_mux.c +++ b/tgt-vvp/draw_mux.c @@ -47,9 +47,9 @@ static void draw_lpm_mux_ab(ivl_lpm_t net, const char*muxz) const char*dly = ""; if (d_rise != 0) { - assert(number_is_immediate(d_rise, 64)); - assert(number_is_immediate(d_fall, 64)); - assert(number_is_immediate(d_decay, 64)); + assert(number_is_immediate(d_rise, 64, 0)); + assert(number_is_immediate(d_fall, 64, 0)); + assert(number_is_immediate(d_decay, 64, 0)); dly = "/d"; fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n", net, get_number_immediate(d_rise), diff --git a/tgt-vvp/draw_net_input.c b/tgt-vvp/draw_net_input.c new file mode 100644 index 000000000..87c13733d --- /dev/null +++ b/tgt-vvp/draw_net_input.c @@ -0,0 +1,689 @@ +/* + * Copyright (c) 2001-2008 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +# include "vvp_priv.h" +#ifdef HAVE_MALLOC_H +# include +#endif +# include +# include +# include +# include +# include + +#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; + ivl_signal_type_t out = IVL_SIT_TRI; + + for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { + ivl_signal_type_t stype; + ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); + ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); + if (sig == 0) + continue; + + stype = ivl_signal_type(sig); + if (stype == IVL_SIT_REG) + continue; + if (stype == IVL_SIT_TRI) + continue; + if (stype == IVL_SIT_NONE) + continue; + out = stype; + } + + return out; +} + +static void draw_C4_repeated_constant(char bit_char, unsigned width) +{ + unsigned idx; + + fprintf(vvp_out, "C4<"); + for (idx = 0 ; idx < width ; idx += 1) + fprintf(vvp_out, "%c", bit_char); + + fprintf(vvp_out, ">"); +} + +static char* draw_C4_to_string(ivl_net_const_t cptr) +{ + const char*bits = ivl_const_bits(cptr); + unsigned idx; + + size_t result_len = 5 + ivl_const_width(cptr); + char*result = malloc(result_len); + char*dp = result; + strcpy(dp, "C4<"); + dp += strlen(dp); + + for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) { + char bitchar = bits[ivl_const_width(cptr)-idx-1]; + *dp++ = bitchar; + assert((dp - result) < result_len); + } + + strcpy(dp, ">"); + return result; +} + +static char* draw_C8_to_string(ivl_net_const_t cptr, + ivl_drive_t dr0, ivl_drive_t dr1) +{ + size_t nresult = 5 + 3*ivl_const_width(cptr); + char*result = malloc(nresult); + const char*bits = ivl_const_bits(cptr); + unsigned idx; + + char dr0c = "01234567"[dr0]; + char dr1c = "01234567"[dr1]; + char*dp = result; + + strcpy(dp, "C8<"); + dp += strlen(dp); + + for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) { + switch (bits[ivl_const_width(cptr)-idx-1]) { + case '0': + *dp++ = dr0c; + *dp++ = dr0c; + *dp++ = '0'; + break; + case '1': + *dp++ = dr1c; + *dp++ = dr1c; + *dp++ = '1'; + break; + case 'x': + case 'X': + *dp++ = dr0c; + *dp++ = dr1c; + *dp++ = 'x'; + break; + case 'z': + case 'Z': + *dp++ = '0'; + *dp++ = '0'; + *dp++ = 'z'; + break; + default: + assert(0); + break; + } + assert(dp - result < nresult); + } + + strcpy(dp, ">"); + return result; +} + +static struct vvp_nexus_data*new_nexus_data() +{ + struct vvp_nexus_data*data = calloc(1, sizeof(struct vvp_nexus_data)); + return data; +} + +static int nexus_drive_is_strength_aware(ivl_nexus_ptr_t nptr) +{ + if (ivl_nexus_ptr_drive0(nptr) != IVL_DR_STRONG) + return 1; + if (ivl_nexus_ptr_drive1(nptr) != IVL_DR_STRONG) + return 1; + + ivl_net_logic_t log = ivl_nexus_ptr_log(nptr); + if (log != 0) { + /* These logic gates are able to generate unusual + strength values and so their outputs are considered + strength aware. */ + if (ivl_logic_type(log) == IVL_LO_BUFIF0) + return 1; + if (ivl_logic_type(log) == IVL_LO_BUFIF1) + return 1; + if (ivl_logic_type(log) == IVL_LO_PMOS) + return 1; + if (ivl_logic_type(log) == IVL_LO_NMOS) + return 1; + if (ivl_logic_type(log) == IVL_LO_CMOS) + return 1; + } + + return 0; +} + +/* + * Given a nexus, look for a signal that has module delay + * paths. Return that signal. (There should be no more than 1.) If we + * don't find any, then return nil. + */ +static ivl_signal_t find_modpath(ivl_nexus_t nex) +{ + unsigned idx; + for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { + ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex,idx); + ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); + if (sig == 0) + continue; + if (ivl_signal_npath(sig) == 0) + continue; + + return sig; + } + + return 0; +} + +static void str_repeat(char*buf, const char*str, unsigned rpt) +{ + unsigned idx; + size_t len = strlen(str); + for (idx = 0 ; idx < rpt ; idx += 1) { + strcpy(buf, str); + buf += len; + } +} + +/* + * This function takes a nexus and looks for an input functor. It then + * draws to the output a string that represents that functor. What we + * are trying to do here is find the input to the net that is attached + * to this nexus. + */ + +static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr) +{ + unsigned nptr_pin = ivl_nexus_ptr_pin(nptr); + ivl_net_const_t cptr; + ivl_net_logic_t lptr; + ivl_signal_t sptr; + ivl_lpm_t lpm; + + lptr = ivl_nexus_ptr_log(nptr); + if (lptr && (ivl_logic_type(lptr) == IVL_LO_BUFZ) && (nptr_pin == 0)) + do { + if (! can_elide_bufz(lptr, nptr)) + break; + + return strdup(draw_net_input(ivl_logic_pin(lptr, 1))); + } while(0); + + /* If this is a pulldown device, then there is a single pin + that drives a constant value to the entire width of the + vector. The driver normally drives a pull0 value, so a C8<> + constant is appropriate, but if the drive is really strong, + then we can draw a C4<> constant instead. */ + if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLDOWN)) { + if (ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG) { + size_t result_len = ivl_logic_width(lptr) + 5; + char*result = malloc(result_len); + char*dp = result; + strcpy(dp, "C4<"); + dp += strlen(dp); + str_repeat(dp, "0", ivl_logic_width(lptr)); + dp += ivl_logic_width(lptr); + *dp++ = '>'; + *dp = 0; + assert((dp-result) <= result_len); + return result; + } else { + char val[4]; + size_t result_len = 3*ivl_logic_width(lptr) + 5; + char*result = malloc(result_len); + char*dp = result; + + val[0] = "01234567"[ivl_nexus_ptr_drive0(nptr)]; + val[1] = val[0]; + val[2] = '0'; + val[3] = 0; + + strcpy(dp, "C8<"); + dp += strlen(dp); + str_repeat(dp, val, ivl_logic_width(lptr)); + dp += 3*ivl_logic_width(lptr); + *dp++ = '>'; + *dp = 0; + assert((dp-result) <= result_len); + return result; + } + } + + if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLUP)) { + if (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG) { + size_t result_len = 5 + ivl_logic_width(lptr); + char*result = malloc(result_len); + char*dp = result; + strcpy(dp, "C4<"); + dp += strlen(dp); + str_repeat(dp, "1", ivl_logic_width(lptr)); + dp += ivl_logic_width(lptr); + *dp++ = '>'; + *dp = 0; + assert((dp-result) <= result_len); + return result; + } else { + char val[4]; + size_t result_len = 5 + 3*ivl_logic_width(lptr); + char*result = malloc(result_len); + char*dp = result; + + val[0] = "01234567"[ivl_nexus_ptr_drive0(nptr)]; + val[1] = val[0]; + val[2] = '1'; + val[3] = 0; + + strcpy(dp, "C8<"); + dp += strlen(dp); + str_repeat(dp, val, ivl_logic_width(lptr)); + dp += 3*ivl_logic_width(lptr); + *dp++ = '>'; + *dp = 0; + assert((dp-result) <= result_len); + return result; + } + } + + if (lptr && (nptr_pin == 0)) { + char tmp[128]; + snprintf(tmp, sizeof tmp, "L_%p", lptr); + return strdup(tmp); + } + + sptr = ivl_nexus_ptr_sig(nptr); + if (sptr && (ivl_signal_type(sptr) == IVL_SIT_REG)) { + char tmp[128]; + /* Input is a .var. This device may be a non-zero pin + because it may be an array of reg vectors. */ + snprintf(tmp, sizeof tmp, "v%p_%u", sptr, nptr_pin); + + if (ivl_signal_array_count(sptr) > 1) { + fprintf(vvp_out, "v%p_%u .array/port v%p, %u;\n", + sptr, nptr_pin, sptr, nptr_pin); + } + + return strdup(tmp); + } + + cptr = ivl_nexus_ptr_con(nptr); + if (cptr) { + /* Constants should have exactly 1 pin, with a literal value. */ + assert(nptr_pin == 0); + char *result = 0; + + switch (ivl_const_type(cptr)) { + case IVL_VT_LOGIC: + case IVL_VT_BOOL: + if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG) + && (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG)) { + + result = draw_C4_to_string(cptr); + + } else { + result = draw_C8_to_string(cptr, + ivl_nexus_ptr_drive0(nptr), + ivl_nexus_ptr_drive1(nptr)); + } + break; + + case IVL_VT_REAL: + result = draw_Cr_to_string(ivl_const_real(cptr)); + break; + + default: + assert(0); + break; + } + + ivl_expr_t d_rise = ivl_const_delay(cptr, 0); + ivl_expr_t d_fall = ivl_const_delay(cptr, 1); + ivl_expr_t d_decay = ivl_const_delay(cptr, 2); + + /* We have a delayed constant, so we need to build some code. */ + 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)); + + fprintf(vvp_out, "L_%p/d .functor BUFZ 1, %s, " + "C4<0>, C4<0>, C4<0>;\n", cptr, result); + + fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n", + cptr, get_number_immediate(d_rise), + get_number_immediate(d_rise), + get_number_immediate(d_rise), cptr); + + free(result); + char tmp[128]; + snprintf(tmp, sizeof tmp, "L_%p", cptr); + result = strdup(tmp); + } + + return result; + } + + lpm = ivl_nexus_ptr_lpm(nptr); + if (lpm) switch (ivl_lpm_type(lpm)) { + + case IVL_LPM_FF: + case IVL_LPM_ABS: + case IVL_LPM_ADD: + case IVL_LPM_ARRAY: + case IVL_LPM_CAST_INT: + case IVL_LPM_CAST_REAL: + case IVL_LPM_CONCAT: + case IVL_LPM_CMP_EEQ: + case IVL_LPM_CMP_EQ: + case IVL_LPM_CMP_GE: + case IVL_LPM_CMP_GT: + case IVL_LPM_CMP_NE: + case IVL_LPM_CMP_NEE: + case IVL_LPM_RE_AND: + case IVL_LPM_RE_OR: + case IVL_LPM_RE_XOR: + case IVL_LPM_RE_NAND: + case IVL_LPM_RE_NOR: + case IVL_LPM_RE_XNOR: + case IVL_LPM_SFUNC: + case IVL_LPM_SHIFTL: + case IVL_LPM_SHIFTR: + case IVL_LPM_SIGN_EXT: + case IVL_LPM_SUB: + case IVL_LPM_MULT: + case IVL_LPM_MUX: + case IVL_LPM_POW: + case IVL_LPM_DIVIDE: + case IVL_LPM_MOD: + case IVL_LPM_UFUNC: + case IVL_LPM_PART_VP: + case IVL_LPM_PART_PV: /* NOTE: This is only a partial driver. */ + case IVL_LPM_REPEAT: + if (ivl_lpm_q(lpm, 0) == nex) { + char tmp[128]; + snprintf(tmp, sizeof tmp, "L_%p", lpm); + return strdup(tmp); + } + break; + + } + + fprintf(stderr, "internal error: no input to nexus %s\n", + ivl_nexus_name(nex)); + assert(0); + return strdup("C"); +} + +static char* draw_island_port(ivl_island_t island, + ivl_nexus_t nex, const char*src) +{ + char result[64]; + if (ivl_island_flag_test(island,0) == 0) { + fprintf(vvp_out, "I%p .island tran;\n", island); + ivl_island_flag_set(island,0,1); + } + + fprintf(vvp_out, "p%p .port I%p, %s;\n", nex, island, src); + snprintf(result, sizeof result, "p%p", nex); + + return strdup(result); +} + +/* + * This function draws the input to a net into a string. What that + * means is that it returns a static string that can be used to + * represent a resolved driver to a nexus. If there are multiple + * drivers to the nexus, then it writes out the resolver declarations + * needed to perform strength resolution. + * + * The string that this returns is malloced, and that means that the + * caller must free the string or store it permanently. This function + * does *not* check for a previously calculated string. Use the + * draw_net_input for the general case. + */ + /* Omit LPMPART_BI device pin-data(0) drivers. */ +# define OMIT_PART_BI_DATA 0x0001 + +char* draw_net_input_x(ivl_nexus_t nex, + ivl_nexus_ptr_t omit_ptr, int omit_flags, + struct vvp_nexus_data*nex_data) +{ + ivl_island_t island = 0; + ivl_signal_type_t res; + char result[512]; + unsigned idx; + int level; + unsigned ndrivers = 0; + static ivl_nexus_ptr_t *drivers = 0x0; + static unsigned adrivers = 0; + + const char*resolv_type; + + char*nex_private = 0; + + /* Accumulate nex_data flags. */ + int nex_flags = 0; + + res = signal_type_of_nexus(nex); + switch (res) { + case IVL_SIT_TRI: + resolv_type = "tri"; + break; + case IVL_SIT_TRI0: + resolv_type = "tri0"; + nex_flags |= VVP_NEXUS_DATA_STR; + break; + case IVL_SIT_TRI1: + resolv_type = "tri1"; + nex_flags |= VVP_NEXUS_DATA_STR; + break; + case IVL_SIT_TRIAND: + resolv_type = "triand"; + break; + case IVL_SIT_TRIOR: + resolv_type = "trior"; + break; + default: + fprintf(stderr, "vvp.tgt: Unsupported signal type: %u\n", res); + assert(0); + resolv_type = "tri"; + break; + } + + + for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { + ivl_lpm_t lpm_tmp; + ivl_switch_t sw = 0; + ivl_nexus_ptr_t nptr = ivl_nexus_ptr(nex, idx); + + /* If this object is part of an island, then we'll be + making a port. Save the island cookie. */ + if ( (sw = ivl_nexus_ptr_switch(nptr)) ) { + island = ivl_switch_island(sw); + } + + /* If we are supposed to skip LPM_PART_BI data pins, + check that this driver is that. */ + if ((omit_flags&OMIT_PART_BI_DATA) + && (lpm_tmp = ivl_nexus_ptr_lpm(nptr)) + && (nex == ivl_lpm_data(lpm_tmp,0))) + continue; + + if (nptr == omit_ptr) + continue; + + /* Skip input only pins. */ + if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_HiZ) + && (ivl_nexus_ptr_drive1(nptr) == IVL_DR_HiZ)) + continue; + + /* Mark the strength-aware flag if the driver can + generate values other than the standard "6" + strength. */ + if (nexus_drive_is_strength_aware(nptr)) + nex_flags |= VVP_NEXUS_DATA_STR; + + /* Save this driver. */ + if (ndrivers >= adrivers) { + adrivers += 4; + drivers = realloc(drivers, adrivers*sizeof(ivl_nexus_ptr_t)); + assert(drivers); + } + drivers[ndrivers] = nptr; + ndrivers += 1; + } + + /* If the caller is collecting nexus information, then save + the nexus driver count in the nex_data. */ + if (nex_data) { + nex_data->drivers_count = ndrivers; + nex_data->flags |= nex_flags; + } + + /* If the nexus has no drivers, then send a constant HiZ into + the net. */ + if (ndrivers == 0) { + unsigned idx, wid = width_of_nexus(nex); + char*tmp = malloc(wid + 5); + nex_private = tmp; + strcpy(tmp, "C4<"); + tmp += strlen(tmp); + switch (res) { + case IVL_SIT_TRI: + for (idx = 0 ; idx < wid ; idx += 1) + *tmp++ = 'z'; + break; + case IVL_SIT_TRI0: + for (idx = 0 ; idx < wid ; idx += 1) + *tmp++ = '0'; + break; + case IVL_SIT_TRI1: + for (idx = 0 ; idx < wid ; idx += 1) + *tmp++ = '1'; + break; + default: + assert(0); + } + *tmp++ = '>'; + *tmp = 0; + if (island) { + char*tmp = draw_island_port(island, nex, nex_private); + free(nex_private); + nex_private = tmp; + } + return nex_private; + } + + + /* If the nexus has exactly one driver, then simply draw + it. Note that this will *not* work if the nexus is not a + TRI type nexus. */ + if (ndrivers == 1 && res == IVL_SIT_TRI) { + ivl_signal_t path_sig = find_modpath(nex); + if (path_sig) { + char*nex_str = draw_net_input_drive(nex, drivers[0]); + char modpath_label[64]; + snprintf(modpath_label, sizeof modpath_label, + "V_%p/m", path_sig); + nex_private = strdup(modpath_label); + draw_modpath(path_sig, nex_str); + + } else { + nex_private = draw_net_input_drive(nex, drivers[0]); + } + if (island) { + char*tmp = draw_island_port(island, nex, nex_private); + free(nex_private); + nex_private = tmp; + } + return nex_private; + } + + level = 0; + while (ndrivers) { + unsigned int inst; + for (inst = 0; inst < ndrivers; inst += 4) { + if (ndrivers > 4) + fprintf(vvp_out, "RS_%p/%d/%d .resolv tri", + nex, level, inst); + else + fprintf(vvp_out, "RS_%p .resolv %s", + nex, resolv_type); + + for (idx = inst; idx < ndrivers && idx < inst+4; idx += 1) { + if (level) { + fprintf(vvp_out, ", RS_%p/%d/%d", + nex, level - 1, idx*4); + } else { + char*drive = draw_net_input_drive(nex, drivers[idx]); + fprintf(vvp_out, ", %s", drive); + free(drive); + } + } + for ( ; idx < inst+4 ; idx += 1) { + fprintf(vvp_out, ", "); + draw_C4_repeated_constant('z',width_of_nexus(nex)); + } + + fprintf(vvp_out, ";\n"); + } + if (ndrivers > 4) + ndrivers = (ndrivers+3) / 4; + else + ndrivers = 0; + level += 1; + } + + snprintf(result, sizeof result, "RS_%p", nex); + + if (island) + nex_private = draw_island_port(island, nex, result); + else + nex_private = strdup(result); + + return nex_private; +} + +/* + * Get a cached description of the nexus input, or create one if this + * nexus has not been cached yet. This is a wrapper for the common + * case call to draw_net_input_x. + */ +const char*draw_net_input(ivl_nexus_t nex) +{ + struct vvp_nexus_data*nex_data = (struct vvp_nexus_data*) + ivl_nexus_get_private(nex); + + /* If this nexus already has a label, then its input is + already figured out. Just return the existing label. */ + if (nex_data && nex_data->net_input) + return nex_data->net_input; + + if (nex_data == 0) { + nex_data = new_nexus_data(); + ivl_nexus_set_private(nex, nex_data); + } + + assert(nex_data->net_input == 0); + nex_data->net_input = draw_net_input_x(nex, 0, 0, nex_data); + + return nex_data->net_input; +} + diff --git a/tgt-vvp/draw_switch.c b/tgt-vvp/draw_switch.c new file mode 100644 index 000000000..fa9c67895 --- /dev/null +++ b/tgt-vvp/draw_switch.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2008 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +# include "vvp_priv.h" +# include +#ifdef HAVE_MALLOC_H +# include +#endif +# include +# include + +static void draw_tran_island(ivl_island_t island) +{ + fprintf(vvp_out, "I%p .island tran;\n", island); + ivl_island_flag_set(island, 0, 1); +} + +void draw_switch_in_scope(ivl_switch_t sw) +{ + ivl_island_t island = ivl_switch_island(sw); + if (ivl_island_flag_test(island, 0) == 0) + draw_tran_island(island); + + ivl_nexus_t nex_a = ivl_switch_a(sw); + assert(nex_a); + const char*str_a = draw_net_input(nex_a); + + ivl_nexus_t nex_b = ivl_switch_b(sw); + assert(nex_b); + const char*str_b = draw_net_input(nex_b); + + ivl_nexus_t enable = ivl_switch_enable(sw); + const char*str_e = 0; + if (enable) + str_e = draw_net_input(enable); + + + switch (ivl_switch_type(sw)) { + case IVL_SW_TRAN: + fprintf(vvp_out, " .tran"); + break; + case IVL_SW_TRANIF0: + fprintf(vvp_out, " .tranif0"); + break; + case IVL_SW_TRANIF1: + fprintf(vvp_out, " .tranif1"); + break; + case IVL_SW_TRAN_VP: + fprintf(vvp_out, " .tranvp %u %u %u,", + ivl_switch_width(sw), ivl_switch_part(sw), ivl_switch_offset(sw)); + break; + + default: + fprintf(stderr, "%s:%u: sorry: vvp target does not support switch modeling.\n", + ivl_switch_file(sw), ivl_switch_lineno(sw)); + vvp_errors += 1; + return; + } + + fprintf(vvp_out, " I%p, %s %s", island, str_a, str_b); + if (enable) + fprintf(vvp_out, ", %s", str_e); + + fprintf(vvp_out, ";\n"); +} diff --git a/tgt-vvp/draw_vpi.c b/tgt-vvp/draw_vpi.c index ab290867c..1c2271116 100644 --- a/tgt-vvp/draw_vpi.c +++ b/tgt-vvp/draw_vpi.c @@ -62,7 +62,7 @@ static int is_fixed_memory_word(ivl_expr_t net) if (ivl_signal_type(sig) == IVL_SIT_REG) return 0; - if (number_is_immediate(ivl_expr_oper1(net), 8*sizeof(unsigned))) + if (number_is_immediate(ivl_expr_oper1(net), 8*sizeof(unsigned), 0)) return 1; return 0; @@ -182,7 +182,7 @@ static void draw_vpi_taskfunc_args(const char*call_string, ivl_expr_t word_ex = ivl_expr_oper1(expr); if (word_ex) { /* Some array select have been evaluated. */ - if (number_is_immediate(word_ex, 8*sizeof(unsigned))) { + if (number_is_immediate(word_ex, 8*sizeof(unsigned), 0)) { use_word = get_number_immediate(word_ex); word_ex = 0; } @@ -204,19 +204,72 @@ static void draw_vpi_taskfunc_args(const char*call_string, ivl_expr_t word_ex = ivl_expr_oper1(expr); if (word_ex) { /* Some array select have been evaluated. */ - if (number_is_immediate(word_ex, 8*sizeof(unsigned))) { + if (number_is_immediate(word_ex, 8*sizeof(unsigned), 0)) { use_word = get_number_immediate(word_ex); word_ex = 0; } } - if (word_ex) - break; - - snprintf(buffer, sizeof buffer, "&A", sig, use_word); + if (word_ex && ivl_expr_type(word_ex)==IVL_EX_SIGNAL) { + /* Special case: the index is a signal. */ + snprintf(buffer, sizeof buffer, + "&A", sig, + ivl_expr_signal(word_ex)); + } else if (word_ex) { + /* Fallback case: evaluate expression. */ + struct vector_info av; + av = draw_eval_expr(word_ex, STUFF_OK_XZ); + snprintf(buffer, sizeof buffer, + "&A", sig, av.base, av.wid); + args[idx].vec = av; + args[idx].vec_flag = 1; + } else { + snprintf(buffer, sizeof buffer, + "&A", sig, use_word); + } args[idx].text = strdup(buffer); continue; } + case IVL_EX_SELECT: { + ivl_expr_t vexpr = ivl_expr_oper1(expr); + assert(vexpr); + + /* This code is only for signals. */ + if (ivl_expr_type(vexpr) != IVL_EX_SIGNAL) break; + + /* The signal is part of an array. */ + /* Add &APV<> code here when it is finished. */ + if (ivl_expr_oper1(vexpr)) break; + + ivl_expr_t bexpr = ivl_expr_oper2(expr); + assert(bexpr); + + /* This is a constant bit/part select. */ + if (number_is_immediate(bexpr, 64, 1)) { + snprintf(buffer, sizeof buffer, "&PV", + ivl_expr_signal(vexpr), + get_number_immediate(bexpr), + ivl_expr_width(expr)); + /* This is an indexed bit/part select. */ + } else if (ivl_expr_type(bexpr) == IVL_EX_SIGNAL) { + /* Sepcial case: the base is a signal. */ + snprintf(buffer, sizeof buffer, "&PV", + ivl_expr_signal(vexpr), + ivl_expr_signal(bexpr), + ivl_expr_width(expr)); + } else { + /* Fallback case: evaluate the expression. */ + struct vector_info rv; + rv = draw_eval_expr(bexpr, STUFF_OK_XZ); + snprintf(buffer, sizeof buffer, "&PV", + ivl_expr_signal(vexpr), + rv.base, rv.wid, + ivl_expr_width(expr)); + } + args[idx].text = strdup(buffer); + continue; + } + /* Everything else will need to be evaluated and passed as a constant to the vpi task. */ default: diff --git a/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index e1fb2013c..a9931423e 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -28,7 +28,7 @@ static void draw_eval_expr_dest(ivl_expr_t exp, struct vector_info dest, int ok_flags); static void draw_signal_dest(ivl_expr_t exp, struct vector_info res, - int add_index, unsigned long immediate); + int add_index, long immediate); int number_is_unknown(ivl_expr_t ex) { @@ -54,9 +54,11 @@ int number_is_unknown(ivl_expr_t ex) * above bitX. The maximum size of the immediate may vary, so use * lim_wid at the width limit to use. */ -int number_is_immediate(ivl_expr_t ex, unsigned lim_wid) +int number_is_immediate(ivl_expr_t ex, unsigned lim_wid, int negative_ok_flag) { const char*bits; + unsigned nbits = ivl_expr_width(ex); + char pad_bit = '0'; unsigned idx; if (ivl_expr_type(ex) != IVL_EX_NUMBER @@ -64,20 +66,23 @@ int number_is_immediate(ivl_expr_t ex, unsigned lim_wid) return 0; bits = ivl_expr_bits(ex); - for (idx = lim_wid ; idx < ivl_expr_width(ex) ; idx += 1) - if (bits[idx] != '0') - return 0; - /* Negative numbers are not "immediate". */ - if (ivl_expr_signed(ex) && bits[ivl_expr_width(ex)-1]=='1') + if (ivl_expr_signed(ex) && bits[nbits-1]=='1') + pad_bit = '1'; + + if (pad_bit == '1' && !negative_ok_flag) return 0; + for (idx = lim_wid ; idx < nbits ; idx += 1) + if (bits[idx] != pad_bit) + return 0; + return 1; } -unsigned long get_number_immediate(ivl_expr_t ex) +long get_number_immediate(ivl_expr_t ex) { - unsigned long imm = 0; + long imm = 0; unsigned idx; switch (ivl_expr_type(ex)) { @@ -92,11 +97,14 @@ unsigned long get_number_immediate(ivl_expr_t ex) case '0': break; case '1': - imm |= 1UL << idx; + assert(idx < 8*sizeof(imm)); + imm |= 1L << idx; break; default: assert(0); } + if (ivl_expr_signed(ex) && bits[nbits-1]=='1' && nbits < 8*sizeof(imm)) + imm |= -1L << nbits; break; } @@ -111,30 +119,18 @@ static void eval_logic_into_integer(ivl_expr_t expr, unsigned ix) { switch (ivl_expr_type(expr)) { - case IVL_EX_NUMBER: { - unsigned value = 0; - unsigned idx, nbits = ivl_expr_width(expr); - const char*bits = ivl_expr_bits(expr); - - for (idx = 0 ; idx < nbits ; idx += 1) switch (bits[idx]) { - - case '0': - break; - case '1': - assert(idx < (8*sizeof value)); - value |= 1 << idx; - break; - default: - assert(0); - } - - fprintf(vvp_out, " %%ix/load %u, %u;\n", ix, value); - break; - } - + case IVL_EX_NUMBER: case IVL_EX_ULONG: - fprintf(vvp_out, " %%ix/load %u, %lu;\n", ix, ivl_expr_uvalue(expr)); - break; + { + long imm = get_number_immediate(expr); + if (imm >= 0) { + fprintf(vvp_out, " %%ix/load %u, %ld;\n", ix, imm); + } else { + fprintf(vvp_out, " %%ix/load %u, 0; loading %ld\n", ix, imm); + fprintf(vvp_out, " %%ix/sub %u, %ld;\n", ix, -imm); + } + } + break; case IVL_EX_SIGNAL: { ivl_signal_t sig = ivl_expr_signal(expr); @@ -155,7 +151,7 @@ static void eval_logic_into_integer(ivl_expr_t expr, unsigned ix) } ivl_expr_t ixe = ivl_expr_oper1(expr); - if (number_is_immediate(ixe, 8*sizeof(unsigned long))) + if (number_is_immediate(ixe, 8*sizeof(unsigned long), 0)) word = get_number_immediate(ixe); else { struct vector_info rv; @@ -383,7 +379,7 @@ static struct vector_info draw_binary_expr_eq(ivl_expr_t exp, return draw_binary_expr_eq_real(exp); } - if (number_is_immediate(re,16) && !number_is_unknown(re)) + if (number_is_immediate(re,16,0) && !number_is_unknown(re)) return draw_eq_immediate(exp, ewid, le, re, stuff_ok_flag); assert(ivl_expr_value(le) == IVL_VT_LOGIC @@ -814,7 +810,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t exp, switch (ivl_expr_opcode(exp)) { case 'G': rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ); - if (number_is_immediate(le,16) && !number_is_unknown(le)) { + if (number_is_immediate(le,16,0) && !number_is_unknown(le)) { unsigned imm = get_number_immediate(le); assert(imm >= 0); fprintf(vvp_out, " %%cmpi/%c %u, %u, %u;\n", s_flag, @@ -830,7 +826,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t exp, case 'L': lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ); - if (number_is_immediate(re,16) && !number_is_unknown(re)) { + if (number_is_immediate(re,16,0) && !number_is_unknown(re)) { unsigned imm = get_number_immediate(re); assert(imm >= 0); fprintf(vvp_out, " %%cmpi/%c %u, %u, %u;\n", s_flag, @@ -846,7 +842,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t exp, case '<': lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ); - if (number_is_immediate(re,16) && !number_is_unknown(re)) { + if (number_is_immediate(re,16,0) && !number_is_unknown(re)) { unsigned imm = get_number_immediate(re); assert(imm >= 0); fprintf(vvp_out, " %%cmpi/%c %u, %u, %u;\n", s_flag, @@ -861,7 +857,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t exp, case '>': rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ); - if (number_is_immediate(le,16) && !number_is_unknown(le)) { + if (number_is_immediate(le,16,0) && !number_is_unknown(le)) { unsigned imm = get_number_immediate(le); assert(imm >= 0); fprintf(vvp_out, " %%cmpi/%c %u, %u, %u;\n", s_flag, @@ -940,9 +936,9 @@ static struct vector_info draw_binary_expr_logic(ivl_expr_t exp, ivl_expr_t re = ivl_expr_oper2(exp); if (ivl_expr_opcode(exp) == '&') { - if (number_is_immediate(re, IMM_WID) && !number_is_unknown(re)) + if (number_is_immediate(re, IMM_WID, 0) && !number_is_unknown(re)) return draw_logic_immediate(exp, le, re, wid); - if (number_is_immediate(le, IMM_WID) && !number_is_unknown(le)) + if (number_is_immediate(le, IMM_WID, 0) && !number_is_unknown(le)) return draw_logic_immediate(exp, re, le, wid); } @@ -1162,12 +1158,11 @@ static struct vector_info draw_binary_expr_lrs(ivl_expr_t exp, unsigned wid) static struct vector_info draw_load_add_immediate(ivl_expr_t le, ivl_expr_t re, - unsigned wid) + unsigned wid, + int signed_flag) { struct vector_info lv; - unsigned long imm; - - imm = get_number_immediate(re); + long imm = get_number_immediate(re); lv.base = allocate_vector(wid); lv.wid = wid; if (lv.base == 0) { @@ -1180,7 +1175,7 @@ static struct vector_info draw_load_add_immediate(ivl_expr_t le, /* Load the signal value with a %load that adds the index register to the value being loaded. */ - draw_signal_dest(le, lv, 0, imm); + draw_signal_dest(le, lv, signed_flag, imm); return lv; } @@ -1197,23 +1192,50 @@ static struct vector_info draw_add_immediate(ivl_expr_t le, imm = get_number_immediate(re); - /* Now generate enough %addi instructions to add the entire - immediate value to the destination. The adds are done IMM_WID - bits at a time, but +1 bits are done to push the carry into - the higher bits if needed. */ - { unsigned base; - for (base = 0 ; base < lv.wid ; base += IMM_WID) { - unsigned long tmp = imm & 0xffffffffUL; - unsigned add_wid = lv.wid - base; + /* This shouldn't generally happen, because the elaborator + should take care of simple constant propagation like this, + but it doesn't have to and it is easy to catch here. */ + if (imm == 0) + return lv; - imm >>= IMM_WID; + switch (lv.base) { + case 0: /* Left expression is 0. */ + lv.base = allocate_vector(wid); + if (lv.base == 0) { + fprintf(stderr, "%s:%u: vvp.tgt error: " + "Unable to allocate %u thread bits " + "for result of addition.\n", + ivl_expr_file(re), ivl_expr_lineno(re), wid); + vvp_errors += 1; + } + fprintf(vvp_out, " %%movi %u, %lu %u;\n", lv.base, imm, wid); + break; - fprintf(vvp_out, " %%addi %u, %lu, %u;\n", - lv.base+base, tmp, add_wid); + case 1: /* Left expression is 1...1 (i.e. -1) */ + imm -= 1; + if (imm == 0) { + lv.base = 0; + } else { + lv.base = allocate_vector(wid); + if (lv.base == 0) { + fprintf(stderr, "%s:%u: vvp.tgt error: " + "Unable to allocate %u thread bits " + "for result of addition.\n", + ivl_expr_file(re), ivl_expr_lineno(re), wid); + vvp_errors += 1; + } + fprintf(vvp_out, " %%movi %u, %lu %u;\n", lv.base, imm, wid); + } + break; - if (imm == 0) - break; - } + case 2: /* Left expression is X or Z */ + case 3: + lv.base = 2; + break; + + default: /* The regular case. */ + fprintf(vvp_out, " %%addi %u, %lu, %u;\n", lv.base, imm, wid); + break; } return lv; @@ -1234,7 +1256,8 @@ static struct vector_info draw_sub_immediate(ivl_expr_t le, assert(lv.wid == wid); imm = get_number_immediate(re); - assert( (imm & ~0xffffffffUL) == 0 ); + if (imm == 0) + return lv; switch (lv.base) { case 0: @@ -1248,21 +1271,21 @@ static struct vector_info draw_sub_immediate(ivl_expr_t le, vvp_errors += 1; } - fprintf(vvp_out, " %%mov %u, %u, %u;\n", tmp, lv.base, wid); + fprintf(vvp_out, " %%mov %u, %u, %u;\n", tmp, lv.base, wid); lv.base = tmp; - fprintf(vvp_out, " %%subi %u, %lu, %u;\n", lv.base, imm, wid); - return lv; + fprintf(vvp_out, " %%subi %u, %lu, %u;\n", lv.base, imm, wid); + break; case 2: case 3: lv.base = 2; - return lv; + break; default: - fprintf(vvp_out, " %%subi %u, %lu, %u;\n", lv.base, imm, wid); + fprintf(vvp_out, " %%subi %u, %lu, %u;\n", lv.base, imm, wid); + break; } - return lv; } @@ -1277,8 +1300,10 @@ static struct vector_info draw_mul_immediate(ivl_expr_t le, assert(lv.wid == wid); imm = get_number_immediate(re); + if (imm == 0) + return lv; - fprintf(vvp_out, " %%muli %u, %lu, %u;\n", lv.base, imm, lv.wid); + fprintf(vvp_out, " %%muli %u, %lu, %u;\n", lv.base, imm, lv.wid); return lv; } @@ -1293,25 +1318,27 @@ static struct vector_info draw_binary_expr_arith(ivl_expr_t exp, unsigned wid) const char*sign_string = ivl_expr_signed(le) && ivl_expr_signed(re)? "/s" : ""; + int signed_flag = ivl_expr_signed(exp)? 1 : 0; + if ((ivl_expr_opcode(exp) == '+') && (ivl_expr_type(le) == IVL_EX_SIGNAL) && (ivl_expr_type(re) == IVL_EX_ULONG)) - return draw_load_add_immediate(le, re, wid); + return draw_load_add_immediate(le, re, wid, signed_flag); if ((ivl_expr_opcode(exp) == '+') && (ivl_expr_type(le) == IVL_EX_SIGNAL) && (ivl_expr_type(re) == IVL_EX_NUMBER)) - return draw_load_add_immediate(le, re, wid); + return draw_load_add_immediate(le, re, wid, signed_flag); if ((ivl_expr_opcode(exp) == '+') && (ivl_expr_type(re) == IVL_EX_SIGNAL) && (ivl_expr_type(le) == IVL_EX_ULONG)) - return draw_load_add_immediate(re, le, wid); + return draw_load_add_immediate(re, le, wid, signed_flag); if ((ivl_expr_opcode(exp) == '+') && (ivl_expr_type(re) == IVL_EX_SIGNAL) && (ivl_expr_type(le) == IVL_EX_NUMBER)) - return draw_load_add_immediate(re, le, wid); + return draw_load_add_immediate(re, le, wid, signed_flag); if ((ivl_expr_opcode(exp) == '+') && (ivl_expr_type(re) == IVL_EX_ULONG)) @@ -1320,7 +1347,7 @@ static struct vector_info draw_binary_expr_arith(ivl_expr_t exp, unsigned wid) if ((ivl_expr_opcode(exp) == '+') && (ivl_expr_type(re) == IVL_EX_NUMBER) && (! number_is_unknown(re)) - && number_is_immediate(re, 8*sizeof(unsigned long))) + && number_is_immediate(re, 8*sizeof(unsigned long), 0)) return draw_add_immediate(le, re, wid); if ((ivl_expr_opcode(exp) == '-') @@ -1330,13 +1357,13 @@ static struct vector_info draw_binary_expr_arith(ivl_expr_t exp, unsigned wid) if ((ivl_expr_opcode(exp) == '-') && (ivl_expr_type(re) == IVL_EX_NUMBER) && (! number_is_unknown(re)) - && number_is_immediate(re, IMM_WID)) + && number_is_immediate(re, IMM_WID, 0)) return draw_sub_immediate(le, re, wid); if ((ivl_expr_opcode(exp) == '*') && (ivl_expr_type(re) == IVL_EX_NUMBER) && (! number_is_unknown(re)) - && number_is_immediate(re, IMM_WID)) + && number_is_immediate(re, IMM_WID, 0)) return draw_mul_immediate(le, re, wid); lv = draw_eval_expr_wid(le, wid, STUFF_OK_XZ); @@ -1643,7 +1670,7 @@ static struct vector_info draw_number_expr(ivl_expr_t exp, unsigned wid) vvp_errors += 1; } - if ((!number_is_unknown(exp)) && number_is_immediate(exp, IMM_WID)) { + if ((!number_is_unknown(exp)) && number_is_immediate(exp, IMM_WID,0)) { unsigned long val = get_number_immediate(exp); fprintf(vvp_out, " %%movi %u, %lu, %u;\n", res.base, val, wid); return res; @@ -1937,11 +1964,13 @@ void pad_expr_in_place(ivl_expr_t exp, struct vector_info res, unsigned swid) * offsetting the read from the lsi (least significant index) of the * signal. * - * If the add_index is >=0, then generate a %load/vp0 to add the - * word0 value to the loaded value before storing it into the destination. + * If the add_index is 0, then generate a %load/vp0 to add the + * word0 value to the loaded value before storing it into the + * destination. If the add_index is 1, then generate a %load/vp0/s to + * do a signed load. */ static void draw_signal_dest(ivl_expr_t exp, struct vector_info res, - int add_index, unsigned long immediate) + int add_index, long immediate) { unsigned swid = ivl_expr_width(exp); ivl_signal_t sig = ivl_expr_signal(exp); @@ -1958,17 +1987,17 @@ static void draw_signal_dest(ivl_expr_t exp, struct vector_info res, draw_eval_expr_into_integer(ix, 3); if (add_index < 0) { - fprintf(vvp_out, " %%load/av %u, v%p, %u;\n", + fprintf(vvp_out, " %%load/av %u, v%p, %u;\n", res.base, sig, swid); + pad_expr_in_place(exp, res, swid); } else { - assert(add_index == 0); + const char*sign_flag = (add_index>0)? "/s" : ""; /* Add an immediate value to an array value. */ - fprintf(vvp_out, " %%ix/load 0, %lu;\n", immediate); - fprintf(vvp_out, " %%load/avp0 %u, v%p, %u;\n", - res.base, sig, swid); + fprintf(vvp_out, " %%ix/load 0, %lu;\n", immediate); + fprintf(vvp_out, " %%load/avp0%s %u, v%p, %u;\n", + sign_flag, res.base, sig, res.wid); } - pad_expr_in_place(exp, res, swid); return; } @@ -1983,13 +2012,17 @@ static void draw_signal_dest(ivl_expr_t exp, struct vector_info res, } else if (add_index >= 0) { - assert(add_index == 0); + const char*sign_flag = add_index==1? "/s" : ""; /* If this is a REG (a variable) then I can do a vector read. */ - fprintf(vvp_out, " %%ix/load 0, %lu;\n", immediate); - fprintf(vvp_out, " %%ix/load 2, %u;\n", res.wid); - fprintf(vvp_out, " %%load/vp0 %u, v%p_%u, %u;\n", - res.base, sig, word, swid); + if (immediate >= 0) { + fprintf(vvp_out, " %%ix/load 0, %lu;\n", immediate); + } else { + fprintf(vvp_out, " %%ix/load 0, 0; immediate=%ld\n", immediate); + fprintf(vvp_out, " %%ix/sub 0, %ld;\n", -immediate); + } + fprintf(vvp_out, " %%load/vp0%s %u, v%p_%u, %u;\n", sign_flag, + res.base, sig,word, res.wid); swid = res.wid; } else { @@ -2078,7 +2111,6 @@ static struct vector_info draw_select_signal(ivl_expr_t sube, { ivl_signal_t sig = ivl_expr_signal(sube); struct vector_info res; - unsigned idx; /* Use this word of the signal. */ unsigned use_word = 0; @@ -2093,7 +2125,7 @@ static struct vector_info draw_select_signal(ivl_expr_t sube, ivl_expr_t ix = ivl_expr_oper1(sube); if (ivl_signal_type(sig)==IVL_SIT_REG - || !number_is_immediate(ix, 8*sizeof(unsigned long))) + || !number_is_immediate(ix, 8*sizeof(unsigned long),0)) return draw_select_array(sube, bit_idx, bit_wid, wid); /* The index is constant, so we can return to direct @@ -2104,7 +2136,7 @@ static struct vector_info draw_select_signal(ivl_expr_t sube, /* Try the special case that the part is at the beginning of the signal (or the entire width). Just load the early bits in one go. */ - if (number_is_immediate(bit_idx, 32) + if (number_is_immediate(bit_idx, 32, 0) && get_number_immediate(bit_idx) == 0 && (ivl_expr_width(sube) >= bit_wid)) { @@ -2122,23 +2154,23 @@ static struct vector_info draw_select_signal(ivl_expr_t sube, return res; } - draw_eval_expr_into_integer(bit_idx, 0); - /* Alas, do it the hard way. */ + + draw_eval_expr_into_integer(bit_idx, 1); + res.base = allocate_vector(wid); res.wid = wid; assert(res.base); - for (idx = 0 ; idx < res.wid ; idx += 1) { - if (idx >= bit_wid) { - fprintf(vvp_out, " %%movi %u, 0, %u; Pad from %u to %u\n", - res.base+idx, res.wid-idx, - ivl_expr_width(sube), wid); - break; - } - fprintf(vvp_out, " %%load/x.p %u, v%p_%u, 0;\n", - res.base+idx, sig, use_word); - } + unsigned use_wid = res.wid; + if (use_wid > bit_wid) + use_wid = bit_wid; + + fprintf(vvp_out, " %%load/x1p %u, v%p_%u, %u;\n", + res.base, sig, use_word, use_wid); + if (use_wid < res.wid) + fprintf(vvp_out, " %%movi %u, 0, %u;\n", + res.base + use_wid, res.wid - use_wid); return res; } diff --git a/tgt-vvp/eval_real.c b/tgt-vvp/eval_real.c index 96405d6dc..27e803d8d 100644 --- a/tgt-vvp/eval_real.c +++ b/tgt-vvp/eval_real.c @@ -328,7 +328,7 @@ static int draw_signal_real_real(ivl_expr_t exp) if (ivl_signal_array_count(sig) > 1) { ivl_expr_t ix = ivl_expr_oper1(exp); - if (!number_is_immediate(ix, 8*sizeof(word))) { + if (!number_is_immediate(ix, 8*sizeof(word), 0)) { /* XXXX Need to generate a %load/ar instruction. */ assert(0); return res; @@ -441,7 +441,22 @@ static int draw_unary_real(ivl_expr_t exp) return res; } + if (ivl_expr_opcode(exp) == '!') { + struct vector_info vi; + vi = draw_eval_expr(exp, STUFF_OK_XZ); + int res = allocate_word(); + const char*sign_flag = ivl_expr_signed(exp)? "/s" : ""; + fprintf(vvp_out, " %%ix/get%s %d, %u, %u;\n", + sign_flag, res, vi.base, vi.wid); + + fprintf(vvp_out, " %%cvt/ri %d, %d;\n", res, res); + + clr_vector(vi); + return res; + } + ivl_expr_t sube = ivl_expr_oper1(exp); + int sub = draw_eval_real(sube); if (ivl_expr_opcode(exp) == '+') @@ -461,8 +476,8 @@ static int draw_unary_real(ivl_expr_t exp) return sub; } - fprintf(vvp_out, "; XXXX unary (%c)\n", ivl_expr_opcode(exp)); - fprintf(stderr, "XXXX evaluate unary (%c)\n", ivl_expr_opcode(exp)); + fprintf(vvp_out, "; XXXX unary (%c) on sube in %d\n", ivl_expr_opcode(exp), sub); + fprintf(stderr, "XXXX evaluate unary (%c) on sube in %d\n", ivl_expr_opcode(exp), sub); return 0; } diff --git a/tgt-vvp/vvp_priv.h b/tgt-vvp/vvp_priv.h index 15cf5d886..b3ca6efd8 100644 --- a/tgt-vvp/vvp_priv.h +++ b/tgt-vvp/vvp_priv.h @@ -1,7 +1,7 @@ #ifndef __vvp_priv_H #define __vvp_priv_H /* - * Copyright (c) 2001-2005 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -51,6 +51,8 @@ struct vector_info { extern const char *vvp_mangle_id(const char *); extern const char *vvp_mangle_name(const char *); +extern char* draw_Cr_to_string(double value); + /* * This generates a string from a signal that uniquely identifies * that signal with letters that can be used in a label. @@ -65,6 +67,8 @@ extern const char* vvp_signal_label(ivl_signal_t sig); extern unsigned width_of_nexus(ivl_nexus_t nex); extern ivl_variable_type_t data_type_of_nexus(ivl_nexus_t nex); +extern int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr); + /* * This function draws a process (initial or always) into the output * file. It normally returns 0, but returns !0 of there is some sort @@ -90,7 +94,7 @@ extern void pad_expr_in_place(ivl_expr_t exp, struct vector_info res, unsigned s * draw_modpath arranges for a .modpath record to be written out. * * cleanup_modpath() cleans up any pending .modpath records that may - * have been scheduled by draw_modpath() but not yet writte. + * have been scheduled by draw_modpath() but not yet written. * * Note: draw_modpath drive_label must be malloc'ed by the * caller. This function will free the string sometime in the future. @@ -110,6 +114,24 @@ extern struct vector_info draw_vpi_func_call(ivl_expr_t exp, unsigned wid); extern int draw_vpi_rfunc_call(ivl_expr_t exp); +/* + * Switches (tran) + */ +extern void draw_switch_in_scope(ivl_switch_t sw); + +/* Draw_net_input and friends uses this. */ +struct vvp_nexus_data { + /* draw_net_input uses this */ + const char*net_input; + unsigned drivers_count; + int flags; + /* draw_net_in_scope uses these to identify the controlling word. */ + ivl_signal_t net; + unsigned net_word; +}; +#define VVP_NEXUS_DATA_STR 0x0001 + + /* * Given a nexus, draw a string that represents the functor output * that feeds the nexus. This function can be used to get the input to @@ -120,6 +142,17 @@ extern int draw_vpi_rfunc_call(ivl_expr_t exp); */ extern const char* draw_net_input(ivl_nexus_t nex); +/* + * See draw_net_input.c for details on draw_net_input_x. (It would be + * nice if this can be made private.) + */ + /* Omit LPMPART_BI device pin-data(0) drivers. */ +# define OMIT_PART_BI_DATA 0x0001 +struct vvp_nexus_data; +extern char* draw_net_input_x(ivl_nexus_t nex, + ivl_nexus_ptr_t omit_ptr, int omit_flags, + struct vvp_nexus_data*nex_data); + /* * This function is different from draw_net_input in that it will * return a reference to a net as its first choice. This reference @@ -236,8 +269,8 @@ extern unsigned allocate_vector_exp(ivl_expr_t exp, unsigned wid, int exclusive_flag); extern int number_is_unknown(ivl_expr_t ex); -extern int number_is_immediate(ivl_expr_t ex, unsigned lim_wid); -extern unsigned long get_number_immediate(ivl_expr_t ex); +extern int number_is_immediate(ivl_expr_t ex, unsigned lim_wid, int negative_is_ok); +extern long get_number_immediate(ivl_expr_t ex); /* * draw_eval_real evaluates real value expressions. The return code diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index f104175e1..fa550cdde 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -83,7 +83,7 @@ static void set_to_lvariable(ivl_lval_t lval, if (part_off_ex == 0) { part_off = 0; - } else if (number_is_immediate(part_off_ex, 64)) { + } else if (number_is_immediate(part_off_ex, 64, 0)) { part_off = get_number_immediate(part_off_ex); part_off_ex = 0; } @@ -91,7 +91,7 @@ static void set_to_lvariable(ivl_lval_t lval, /* 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, 8*sizeof(use_word))) { + if (word_ix && number_is_immediate(word_ix, 8*sizeof(use_word), 0)) { use_word = get_number_immediate(word_ix); word_ix = 0; } @@ -227,14 +227,14 @@ static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix, unsigned part_off = 0; if (part_off_ex == 0) { part_off = 0; - } else if (number_is_immediate(part_off_ex, 64)) { + } else if (number_is_immediate(part_off_ex, 64, 0)) { part_off = get_number_immediate(part_off_ex); part_off_ex = 0; } if (dexp == 0) { /* Constant delay... */ - if (number_is_immediate(word_ix, 64)) { + if (number_is_immediate(word_ix, 64, 0)) { fprintf(vvp_out, " %%ix/load 3, %lu; address\n", get_number_immediate(word_ix)); } else { @@ -259,7 +259,7 @@ static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix, /* Calculated delay... */ int delay_index = allocate_word(); draw_eval_expr_into_integer(dexp, delay_index); - if (number_is_immediate(word_ix, 64)) { + if (number_is_immediate(word_ix, 64, 0)) { fprintf(vvp_out, " %%ix/load 3, %lu; address\n", get_number_immediate(word_ix)); } else { @@ -306,7 +306,7 @@ static void assign_to_lvector(ivl_lval_t lval, unsigned bit, if (part_off_ex == 0) { part_off = 0; - } else if (number_is_immediate(part_off_ex, 64)) { + } else if (number_is_immediate(part_off_ex, 64, 0)) { part_off = get_number_immediate(part_off_ex); part_off_ex = 0; } @@ -543,7 +543,7 @@ static int show_stmt_assign_nb_real(ivl_statement_t net) if (ivl_signal_array_count(sig) > 1) { word_ix = ivl_lval_idx(lval); assert(word_ix); - assert(number_is_immediate(word_ix, 8*sizeof(use_word))); + assert(number_is_immediate(word_ix, 8*sizeof(use_word), 0)); use_word = get_number_immediate(word_ix); } @@ -700,7 +700,7 @@ static int show_stmt_case(ivl_statement_t net, ivl_scope_t sscope) if ((ivl_statement_type(net) == IVL_ST_CASE) && (ivl_expr_type(cex) == IVL_EX_NUMBER) && (! number_is_unknown(cex)) - && number_is_immediate(cex, 16)) { + && number_is_immediate(cex, 16, 0)) { unsigned long imm = get_number_immediate(cex); @@ -926,12 +926,12 @@ static void force_vector_to_lval(ivl_statement_t net, struct vector_info rvec) if (part_off_ex == 0) { part_off = 0; } else { - assert(number_is_immediate(part_off_ex, 64)); + assert(number_is_immediate(part_off_ex, 64, 0)); part_off = get_number_immediate(part_off_ex); } if (word_idx != 0) { - assert(number_is_immediate(word_idx, 8*sizeof(unsigned long))); + assert(number_is_immediate(word_idx, 8*sizeof(unsigned long), 0)); use_word = get_number_immediate(word_idx); } @@ -1004,12 +1004,12 @@ static void force_link_rval(ivl_statement_t net, ivl_expr_t rval) /* At least for now, only handle force to fixed words of an array. */ if ((lword_idx = ivl_lval_idx(lval)) != 0) { - assert(number_is_immediate(lword_idx, 8*sizeof(unsigned long))); + assert(number_is_immediate(lword_idx, 8*sizeof(unsigned long), 0)); use_lword = get_number_immediate(lword_idx); } if ((rword_idx = ivl_expr_oper1(rval)) != 0) { - assert(number_is_immediate(rword_idx, 8*sizeof(unsigned long))); + assert(number_is_immediate(rword_idx, 8*sizeof(unsigned long), 0)); use_rword = get_number_immediate(rword_idx); } @@ -1087,12 +1087,12 @@ static int show_stmt_deassign(ivl_statement_t net) ivl_expr_t part_off_ex = ivl_lval_part_off(lval); unsigned part_off = 0; if (part_off_ex != 0) { - assert(number_is_immediate(part_off_ex, 64)); + assert(number_is_immediate(part_off_ex, 64, 0)); part_off = get_number_immediate(part_off_ex); } if (word_idx != 0) { - assert(number_is_immediate(word_idx, 8*sizeof(use_word))); + assert(number_is_immediate(word_idx, 8*sizeof(use_word), 0)); use_word = get_number_immediate(word_idx); } @@ -1369,7 +1369,7 @@ static int show_stmt_release(ivl_statement_t net) ivl_expr_t part_off_ex = ivl_lval_part_off(lval); unsigned part_off = 0; if (part_off_ex != 0) { - assert(number_is_immediate(part_off_ex, 64)); + assert(number_is_immediate(part_off_ex, 64, 0)); part_off = get_number_immediate(part_off_ex); } @@ -1383,7 +1383,7 @@ static int show_stmt_release(ivl_statement_t net) } if (word_idx != 0) { - assert(number_is_immediate(word_idx, 8*sizeof(use_word))); + assert(number_is_immediate(word_idx, 8*sizeof(use_word), 0)); use_word = get_number_immediate(word_idx); } diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 7900c30fb..c55bcb590 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -31,23 +31,6 @@ #define snprintf _snprintf #endif -struct vvp_nexus_data { - /* draw_net_input uses this */ - const char*net_input; - unsigned drivers_count; - int flags; - /* draw_net_in_scope uses these to identify the controlling word. */ - ivl_signal_t net; - unsigned net_word; -}; -#define VVP_NEXUS_DATA_STR 0x0001 - - -static struct vvp_nexus_data*new_nexus_data() -{ - struct vvp_nexus_data*data = calloc(1, sizeof(struct vvp_nexus_data)); - return data; -} /* * Escape non-symbol characters in ids, and quotes in strings. @@ -147,27 +130,6 @@ const char *vvp_mangle_name(const char *id) return out; } -static void draw_C4_repeated_constant(char bit_char, unsigned width) -{ - unsigned idx; - - fprintf(vvp_out, "C4<"); - for (idx = 0 ; idx < width ; idx += 1) - fprintf(vvp_out, "%c", bit_char); - - fprintf(vvp_out, ">"); -} - -static void str_repeat(char*buf, const char*str, unsigned rpt) -{ - unsigned idx; - size_t len = strlen(str); - for (idx = 0 ; idx < rpt ; idx += 1) { - strcpy(buf, str); - buf += len; - } -} - /* REMOVE ME: vvp_signal_label should not be used. DEAD CODE * Given a signal, generate a string name that is suitable for use as * a label. The only rule is that the same signal will always have the @@ -198,31 +160,6 @@ ivl_signal_t signal_of_nexus(ivl_nexus_t nex, unsigned*word) return 0; } -ivl_signal_type_t signal_type_of_nexus(ivl_nexus_t nex) -{ - unsigned idx; - ivl_signal_type_t out = IVL_SIT_TRI; - - for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { - ivl_signal_type_t stype; - ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); - ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); - if (sig == 0) - continue; - - stype = ivl_signal_type(sig); - if (stype == IVL_SIT_REG) - continue; - if (stype == IVL_SIT_TRI) - continue; - if (stype == IVL_SIT_NONE) - continue; - out = stype; - } - - return out; -} - unsigned width_of_nexus(ivl_nexus_t nex) { unsigned idx; @@ -332,7 +269,7 @@ const char*drive_string(ivl_drive_t drive) * gate will be generated for this node. Otherwise, the code generator * will connect its input to its output and skip the gate. */ -static int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr) +int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr) { ivl_nexus_t in_n; unsigned idx; @@ -380,100 +317,7 @@ static int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr) return 1; } -/* - * Given a nexus, look for a signal that has module delay - * paths. Return that signal. (There should be no more than 1.) If we - * don't find any, then return nil. - */ -static ivl_signal_t find_modpath(ivl_nexus_t nex) -{ - unsigned idx; - for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { - ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex,idx); - ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); - if (sig == 0) - continue; - if (ivl_signal_npath(sig) == 0) - continue; - - return sig; - } - - return 0; -} - -static char* draw_C4_to_string(ivl_net_const_t cptr) -{ - const char*bits = ivl_const_bits(cptr); - unsigned idx; - - size_t result_len = 5 + ivl_const_width(cptr); - char*result = malloc(result_len); - char*dp = result; - strcpy(dp, "C4<"); - dp += strlen(dp); - - for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) { - char bitchar = bits[ivl_const_width(cptr)-idx-1]; - *dp++ = bitchar; - assert((dp - result) < result_len); - } - - strcpy(dp, ">"); - return result; -} - -static char* draw_C8_to_string(ivl_net_const_t cptr, - ivl_drive_t dr0, ivl_drive_t dr1) -{ - size_t nresult = 5 + 3*ivl_const_width(cptr); - char*result = malloc(nresult); - const char*bits = ivl_const_bits(cptr); - unsigned idx; - - char dr0c = "01234567"[dr0]; - char dr1c = "01234567"[dr1]; - char*dp = result; - - strcpy(dp, "C8<"); - dp += strlen(dp); - - for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) { - switch (bits[ivl_const_width(cptr)-idx-1]) { - case '0': - *dp++ = dr0c; - *dp++ = dr0c; - *dp++ = '0'; - break; - case '1': - *dp++ = dr1c; - *dp++ = dr1c; - *dp++ = '1'; - break; - case 'x': - case 'X': - *dp++ = dr0c; - *dp++ = dr1c; - *dp++ = 'x'; - break; - case 'z': - case 'Z': - *dp++ = '0'; - *dp++ = '0'; - *dp++ = 'z'; - break; - default: - assert(0); - break; - } - assert(dp - result < nresult); - } - - strcpy(dp, ">"); - return result; -} - -static char* draw_Cr_to_string(double value) +char* draw_Cr_to_string(double value) { char tmp[256]; @@ -508,488 +352,6 @@ static char* draw_Cr_to_string(double value) return strdup(tmp); } -/* - * This function takes a nexus and looks for an input functor. It then - * draws to the output a string that represents that functor. What we - * are trying to do here is find the input to the net that is attached - * to this nexus. - */ - -static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr) -{ - unsigned nptr_pin = ivl_nexus_ptr_pin(nptr); - ivl_net_const_t cptr; - ivl_net_logic_t lptr; - ivl_signal_t sptr; - ivl_lpm_t lpm; - - lptr = ivl_nexus_ptr_log(nptr); - if (lptr && (ivl_logic_type(lptr) == IVL_LO_BUFZ) && (nptr_pin == 0)) - do { - if (! can_elide_bufz(lptr, nptr)) - break; - - return strdup(draw_net_input(ivl_logic_pin(lptr, 1))); - } while(0); - - /* If this is a pulldown device, then there is a single pin - that drives a constant value to the entire width of the - vector. The driver normally drives a pull0 value, so a C8<> - constant is appropriate, but if the drive is really strong, - then we can draw a C4<> constant instead. */ - if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLDOWN)) { - if (ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG) { - size_t result_len = ivl_logic_width(lptr) + 5; - char*result = malloc(result_len); - char*dp = result; - strcpy(dp, "C4<"); - dp += strlen(dp); - str_repeat(dp, "0", ivl_logic_width(lptr)); - dp += ivl_logic_width(lptr); - *dp++ = '>'; - *dp = 0; - assert((dp-result) <= result_len); - return result; - } else { - char val[4]; - size_t result_len = 3*ivl_logic_width(lptr) + 5; - char*result = malloc(result_len); - char*dp = result; - - val[0] = "01234567"[ivl_nexus_ptr_drive0(nptr)]; - val[1] = val[0]; - val[2] = '0'; - val[3] = 0; - - strcpy(dp, "C8<"); - dp += strlen(dp); - str_repeat(dp, val, ivl_logic_width(lptr)); - dp += 3*ivl_logic_width(lptr); - *dp++ = '>'; - *dp = 0; - assert((dp-result) <= result_len); - return result; - } - } - - if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLUP)) { - if (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG) { - size_t result_len = 5 + ivl_logic_width(lptr); - char*result = malloc(result_len); - char*dp = result; - strcpy(dp, "C4<"); - dp += strlen(dp); - str_repeat(dp, "1", ivl_logic_width(lptr)); - dp += ivl_logic_width(lptr); - *dp++ = '>'; - *dp = 0; - assert((dp-result) <= result_len); - return result; - } else { - char val[4]; - size_t result_len = 5 + 3*ivl_logic_width(lptr); - char*result = malloc(result_len); - char*dp = result; - - val[0] = "01234567"[ivl_nexus_ptr_drive0(nptr)]; - val[1] = val[0]; - val[2] = '1'; - val[3] = 0; - - strcpy(dp, "C8<"); - dp += strlen(dp); - str_repeat(dp, val, ivl_logic_width(lptr)); - dp += 3*ivl_logic_width(lptr); - *dp++ = '>'; - *dp = 0; - assert((dp-result) <= result_len); - return result; - } - } - - if (lptr && (nptr_pin == 0)) { - char tmp[128]; - snprintf(tmp, sizeof tmp, "L_%p", lptr); - return strdup(tmp); - } - - sptr = ivl_nexus_ptr_sig(nptr); - if (sptr && (ivl_signal_type(sptr) == IVL_SIT_REG)) { - char tmp[128]; - /* Input is a .var. This device may be a non-zero pin - because it may be an array of reg vectors. */ - snprintf(tmp, sizeof tmp, "v%p_%u", sptr, nptr_pin); - - if (ivl_signal_array_count(sptr) > 1) { - fprintf(vvp_out, "v%p_%u .array/port v%p, %u;\n", - sptr, nptr_pin, sptr, nptr_pin); - } - - return strdup(tmp); - } - - cptr = ivl_nexus_ptr_con(nptr); - if (cptr) { - /* Constants should have exactly 1 pin, with a literal value. */ - assert(nptr_pin == 0); - char *result = 0; - - switch (ivl_const_type(cptr)) { - case IVL_VT_LOGIC: - case IVL_VT_BOOL: - if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG) - && (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG)) { - - result = draw_C4_to_string(cptr); - - } else { - result = draw_C8_to_string(cptr, - ivl_nexus_ptr_drive0(nptr), - ivl_nexus_ptr_drive1(nptr)); - } - break; - - case IVL_VT_REAL: - result = draw_Cr_to_string(ivl_const_real(cptr)); - break; - - default: - assert(0); - break; - } - - ivl_expr_t d_rise = ivl_const_delay(cptr, 0); - ivl_expr_t d_fall = ivl_const_delay(cptr, 1); - ivl_expr_t d_decay = ivl_const_delay(cptr, 2); - - /* We have a delayed constant, so we need to build some code. */ - if (d_rise != 0) { - assert(number_is_immediate(d_rise, 64)); - assert(number_is_immediate(d_fall, 64)); - assert(number_is_immediate(d_decay, 64)); - - fprintf(vvp_out, "L_%p/d .functor BUFZ 1, %s, " - "C4<0>, C4<0>, C4<0>;\n", cptr, result); - - fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n", - cptr, get_number_immediate(d_rise), - get_number_immediate(d_rise), - get_number_immediate(d_rise), cptr); - - free(result); - char tmp[128]; - snprintf(tmp, sizeof tmp, "L_%p", cptr); - result = strdup(tmp); - } - - return result; - } - - lpm = ivl_nexus_ptr_lpm(nptr); - if (lpm) switch (ivl_lpm_type(lpm)) { - - case IVL_LPM_FF: - case IVL_LPM_ABS: - case IVL_LPM_ADD: - case IVL_LPM_ARRAY: - case IVL_LPM_CONCAT: - case IVL_LPM_CMP_EEQ: - case IVL_LPM_CMP_EQ: - case IVL_LPM_CMP_GE: - case IVL_LPM_CMP_GT: - case IVL_LPM_CMP_NE: - case IVL_LPM_CMP_NEE: - case IVL_LPM_RE_AND: - case IVL_LPM_RE_OR: - case IVL_LPM_RE_XOR: - case IVL_LPM_RE_NAND: - case IVL_LPM_RE_NOR: - case IVL_LPM_RE_XNOR: - case IVL_LPM_SFUNC: - case IVL_LPM_SHIFTL: - case IVL_LPM_SHIFTR: - case IVL_LPM_SIGN_EXT: - case IVL_LPM_SUB: - case IVL_LPM_MULT: - case IVL_LPM_MUX: - case IVL_LPM_POW: - case IVL_LPM_DIVIDE: - case IVL_LPM_MOD: - case IVL_LPM_UFUNC: - case IVL_LPM_PART_VP: - case IVL_LPM_PART_PV: /* NOTE: This is only a partial driver. */ - case IVL_LPM_REPEAT: - if (ivl_lpm_q(lpm, 0) == nex) { - char tmp[128]; - snprintf(tmp, sizeof tmp, "L_%p", lpm); - return strdup(tmp); - } - break; - - case IVL_LPM_PART_BI: - if (ivl_lpm_q(lpm, 0) == nex) { - char tmp[128]; - snprintf(tmp, sizeof tmp, "L_%p/P", lpm); - return strdup(tmp); - } else if (ivl_lpm_data(lpm,0) == nex) { - char tmp[128]; - snprintf(tmp, sizeof tmp, "L_%p/V", lpm); - return strdup(tmp); - } - break; - } - - fprintf(stderr, "internal error: no input to nexus %s\n", - ivl_nexus_name(nex)); - assert(0); - return strdup("C"); -} - -static int nexus_drive_is_strength_aware(ivl_nexus_ptr_t nptr) -{ - if (ivl_nexus_ptr_drive0(nptr) != IVL_DR_STRONG) - return 1; - if (ivl_nexus_ptr_drive1(nptr) != IVL_DR_STRONG) - return 1; - - ivl_net_logic_t log = ivl_nexus_ptr_log(nptr); - if (log != 0) { - /* These logic gates are able to generate unusual - strength values and so their outputs are considered - strength aware. */ - if (ivl_logic_type(log) == IVL_LO_BUFIF0) - return 1; - if (ivl_logic_type(log) == IVL_LO_BUFIF1) - return 1; - if (ivl_logic_type(log) == IVL_LO_PMOS) - return 1; - if (ivl_logic_type(log) == IVL_LO_NMOS) - return 1; - if (ivl_logic_type(log) == IVL_LO_CMOS) - return 1; - } - - return 0; -} - -/* - * This function draws the input to a net into a string. What that - * means is that it returns a static string that can be used to - * represent a resolved driver to a nexus. If there are multiple - * drivers to the nexus, then it writes out the resolver declarations - * needed to perform strength resolution. - * - * The string that this returns is malloced, and that means that the - * caller must free the string or store it permanently. This function - * does *not* check for a previously calculated string. Use the - * draw_net_input for the general case. - */ - /* Omit LPMPART_BI device pin-data(0) drivers. */ -# define OMIT_PART_BI_DATA 0x0001 - -static char* draw_net_input_x(ivl_nexus_t nex, - ivl_nexus_ptr_t omit_ptr, int omit_flags, - struct vvp_nexus_data*nex_data) -{ - ivl_signal_type_t res; - char result[512]; - unsigned idx; - int level; - unsigned ndrivers = 0; - static ivl_nexus_ptr_t *drivers = 0x0; - static unsigned adrivers = 0; - - const char*resolv_type; - - char*nex_private = 0; - - /* Accumulate nex_data flags. */ - int nex_flags = 0; - - res = signal_type_of_nexus(nex); - switch (res) { - case IVL_SIT_TRI: - resolv_type = "tri"; - break; - case IVL_SIT_TRI0: - resolv_type = "tri0"; - nex_flags |= VVP_NEXUS_DATA_STR; - break; - case IVL_SIT_TRI1: - resolv_type = "tri1"; - nex_flags |= VVP_NEXUS_DATA_STR; - break; - case IVL_SIT_TRIAND: - resolv_type = "triand"; - break; - case IVL_SIT_TRIOR: - resolv_type = "trior"; - break; - default: - fprintf(stderr, "vvp.tgt: Unsupported signal type: %u\n", res); - assert(0); - resolv_type = "tri"; - break; - } - - - for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { - ivl_lpm_t lpm_tmp; - ivl_nexus_ptr_t nptr = ivl_nexus_ptr(nex, idx); - - /* If we are supposed to skip LPM_PART_BI data pins, - check that this driver is that. */ - if ((omit_flags&OMIT_PART_BI_DATA) - && (lpm_tmp = ivl_nexus_ptr_lpm(nptr)) - && (nex == ivl_lpm_data(lpm_tmp,0))) - continue; - - if (nptr == omit_ptr) - continue; - - /* Skip input only pins. */ - if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_HiZ) - && (ivl_nexus_ptr_drive1(nptr) == IVL_DR_HiZ)) - continue; - - /* Mark the strength-aware flag if the driver can - generate values other than the standard "6" - strength. */ - if (nexus_drive_is_strength_aware(nptr)) - nex_flags |= VVP_NEXUS_DATA_STR; - - /* Save this driver. */ - if (ndrivers >= adrivers) { - adrivers += 4; - drivers = realloc(drivers, adrivers*sizeof(ivl_nexus_ptr_t)); - assert(drivers); - } - drivers[ndrivers] = nptr; - ndrivers += 1; - } - - /* If the caller is collecting nexus information, then save - the nexus driver count in the nex_data. */ - if (nex_data) { - nex_data->drivers_count = ndrivers; - nex_data->flags |= nex_flags; - } - - /* If the nexus has no drivers, then send a constant HiZ into - the net. */ - if (ndrivers == 0) { - unsigned idx, wid = width_of_nexus(nex); - char*tmp = malloc(wid + 5); - nex_private = tmp; - strcpy(tmp, "C4<"); - tmp += strlen(tmp); - switch (res) { - case IVL_SIT_TRI: - for (idx = 0 ; idx < wid ; idx += 1) - *tmp++ = 'z'; - break; - case IVL_SIT_TRI0: - for (idx = 0 ; idx < wid ; idx += 1) - *tmp++ = '0'; - break; - case IVL_SIT_TRI1: - for (idx = 0 ; idx < wid ; idx += 1) - *tmp++ = '1'; - break; - default: - assert(0); - } - *tmp++ = '>'; - *tmp = 0; - return nex_private; - } - - - /* If the nexus has exactly one driver, then simply draw - it. Note that this will *not* work if the nexus is not a - TRI type nexus. */ - if (ndrivers == 1 && res == IVL_SIT_TRI) { - ivl_signal_t path_sig = find_modpath(nex); - if (path_sig) { - char*nex_str = draw_net_input_drive(nex, drivers[0]); - char modpath_label[64]; - snprintf(modpath_label, sizeof modpath_label, - "V_%p/m", path_sig); - nex_private = strdup(modpath_label); - draw_modpath(path_sig, nex_str); - - } else { - nex_private = draw_net_input_drive(nex, drivers[0]); - } - return nex_private; - } - - level = 0; - while (ndrivers) { - unsigned int inst; - for (inst = 0; inst < ndrivers; inst += 4) { - if (ndrivers > 4) - fprintf(vvp_out, "RS_%p/%d/%d .resolv tri", - nex, level, inst); - else - fprintf(vvp_out, "RS_%p .resolv %s", - nex, resolv_type); - - for (idx = inst; idx < ndrivers && idx < inst+4; idx += 1) { - if (level) { - fprintf(vvp_out, ", RS_%p/%d/%d", - nex, level - 1, idx*4); - } else { - char*drive = draw_net_input_drive(nex, drivers[idx]); - fprintf(vvp_out, ", %s", drive); - free(drive); - } - } - for ( ; idx < inst+4 ; idx += 1) { - fprintf(vvp_out, ", "); - draw_C4_repeated_constant('z',width_of_nexus(nex)); - } - - fprintf(vvp_out, ";\n"); - } - if (ndrivers > 4) - ndrivers = (ndrivers+3) / 4; - else - ndrivers = 0; - level += 1; - } - - sprintf(result, "RS_%p", nex); - nex_private = strdup(result); - return nex_private; -} - -/* - * Get a cached description of the nexus input, or create one if this - * nexus has not been cached yet. This is a wrapper for the common - * case call to draw_net_input_x. - */ -const char*draw_net_input(ivl_nexus_t nex) -{ - struct vvp_nexus_data*nex_data = (struct vvp_nexus_data*) - ivl_nexus_get_private(nex); - - /* If this nexus already has a label, then its input is - already figured out. Just return the existing label. */ - if (nex_data && nex_data->net_input) - return nex_data->net_input; - - if (nex_data == 0) { - nex_data = new_nexus_data(); - ivl_nexus_set_private(nex, nex_data); - } - - assert(nex_data->net_input == 0); - nex_data->net_input = draw_net_input_x(nex, 0, 0, nex_data); - - return nex_data->net_input; -} - const char*draw_input_from_net(ivl_nexus_t nex) { static char result[32]; @@ -1184,9 +546,9 @@ static void draw_delay(ivl_net_logic_t lptr) return; /* FIXME: Assume that the expression is a constant */ - assert(number_is_immediate(d0, 64)); - assert(number_is_immediate(d1, 64)); - assert(number_is_immediate(d2, 64)); + assert(number_is_immediate(d0, 64, 0)); + assert(number_is_immediate(d1, 64, 0)); + assert(number_is_immediate(d2, 64, 0)); if (d0 == d1 && d1 == d2) fprintf(vvp_out, " (%lu)", get_number_immediate(d0)); @@ -1500,9 +862,9 @@ static void draw_logic_in_scope(ivl_net_logic_t lptr) ivl_expr_t fall_exp = ivl_logic_delay(lptr,1); ivl_expr_t decay_exp = ivl_logic_delay(lptr,2); - if (number_is_immediate(rise_exp,64) - && number_is_immediate(fall_exp,64) - && number_is_immediate(decay_exp,64)) { + if (number_is_immediate(rise_exp,64,0) + && number_is_immediate(fall_exp,64,0) + && number_is_immediate(decay_exp,64,0)) { fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n", lptr, get_number_immediate(rise_exp), @@ -1693,9 +1055,9 @@ static const char* draw_lpm_output_delay(ivl_lpm_t net) const char*dly = ""; if (d_rise != 0) { - assert(number_is_immediate(d_rise, 64)); - assert(number_is_immediate(d_fall, 64)); - assert(number_is_immediate(d_decay, 64)); + assert(number_is_immediate(d_rise, 64, 0)); + assert(number_is_immediate(d_fall, 64, 0)); + assert(number_is_immediate(d_decay, 64, 0)); dly = "/d"; fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n", net, get_number_immediate(d_rise), @@ -1717,6 +1079,31 @@ static void draw_lpm_abs(ivl_lpm_t net) net, dly, src_table[0]); } +static void draw_lpm_cast_int(ivl_lpm_t net) +{ + const char*src_table[1]; + draw_lpm_data_inputs(net, 0, 1, src_table); + + const char*dly = draw_lpm_output_delay(net); + + fprintf(vvp_out, "L_%p%s .cast/int %u, %s;\n", + net, dly, ivl_lpm_width(net), src_table[0]); +} + +static void draw_lpm_cast_real(ivl_lpm_t net) +{ + const char*src_table[1]; + draw_lpm_data_inputs(net, 0, 1, src_table); + + const char*dly = draw_lpm_output_delay(net); + + const char*is_signed = ""; + if (ivl_lpm_signed(net)) is_signed = ".s"; + + fprintf(vvp_out, "L_%p%s .cast/real%s %s;\n", + net, dly, is_signed, src_table[0]); +} + static void draw_lpm_add(ivl_lpm_t net) { const char*src_table[2]; @@ -1767,20 +1154,9 @@ static void draw_lpm_add(ivl_lpm_t net) case IVL_LPM_POW: if (dto == IVL_VT_REAL) type = "pow.r"; - else if (ivl_lpm_signed(net)) { + else if (ivl_lpm_signed(net)) type = "pow.s"; - if (width > 8*sizeof(long)) { - fprintf(stderr, "%s:%u: sorry (vvp-tgt): Signed power " -#ifdef __MINGW32__ /* MinGW does not know about z. */ - "result must be no more than %u bits.\n", -#else - "result must be no more than %zu bits.\n", -#endif - ivl_lpm_file(net), ivl_lpm_lineno(net), - 8*sizeof(long)); - exit(1); - } - } else + else type = "pow"; break; default: @@ -2249,94 +1625,6 @@ static void draw_lpm_part_pv(ivl_lpm_t net) fprintf(vvp_out, ", %u, %u, %u;\n", base, width, signal_width); } -/* - * Handle the drawing of a bi-directional part select. The two ports - * are simultaneously input and output. A simple minded connect of the - * input to the output causes a functor cycle which will lock into an - * X value, so something special is needed. - * - * NOTE: The inputs of the tran device at this point need to be from - * all the drivers of the nexus *except* the tran itself. This - * function will draw three labels that can be linked: - * - * The ivl_lpm_q of a part(bi) may be a smaller vector then the - * ivl_lpm_data, the tran acts like a forward part select in that - * way. - * - * The device creates these nodes: - * - * - L_%p/i - * This is the Q port of the tran resolved and padded to the maximum - * width of the tran. The tran itself is not included in the - * resolution of this port. - * - * - L_%p/V - * This is the Q and D parts resolved together, still without the tran - * driving anything. - * - * - L_%p/P - * This is the /V node part-selected back to the dimensions of the Q - * side. - */ -static void draw_lpm_part_bi(ivl_lpm_t net) -{ - unsigned width = ivl_lpm_width(net); - unsigned base = ivl_lpm_base(net); - unsigned signal_width = width_of_nexus(ivl_lpm_data(net,0)); - - unsigned idx; - ivl_nexus_t nex; - ivl_nexus_ptr_t ptr = 0; - - char*p_str; - char*v_str; - - /* It seems implausible that the two inputs of a tran will be - connected together. So assert that this is so to simplify - the code to look for the nexus_ptr_t objects. */ - assert(ivl_lpm_q(net,0) != ivl_lpm_data(net,0)); - - nex = ivl_lpm_q(net,0); - for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { - ptr = ivl_nexus_ptr(nex, idx); - if (ivl_nexus_ptr_lpm(ptr) == net) - break; - } - assert(ptr != 0); - p_str = draw_net_input_x(nex, ptr, 0, 0); - - nex = ivl_lpm_data(net,0); - for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { - ptr = ivl_nexus_ptr(nex, idx); - if (ivl_nexus_ptr_lpm(ptr) == net) - break; - } - v_str = draw_net_input_x(nex, ptr, OMIT_PART_BI_DATA, 0); - - /* Pad the part-sized input out to a common width... - The /i label is the Q side of the tran, resolved except for - the tran itself and padded (with z) to the larger width. */ - fprintf(vvp_out, "L_%p/i .part/pv %s, %u, %u, %u;\n", - net, p_str, base, width, signal_width); - - /* Resolve together the two halves of the tran... - The /V label is the ports of the tran (now the same width) - resolved together. Neither input to this resolver includes - the tran itself. */ - fprintf(vvp_out, "L_%p/V .resolv tri, L_%p/i, %s;\n", - net, net, v_str); - - /* The full-width side is created by the tran device, all we - have left to to is take a part select of that for the - smaller output, and this becomes the part select output of - the BI device. */ - fprintf(vvp_out, "L_%p/P .part L_%p/V, %u, %u;\n", net, - net, base, width); - - free(p_str); - free(v_str); -} - /* * Draw unary reduction devices. */ @@ -2370,6 +1658,14 @@ static void draw_lpm_in_scope(ivl_lpm_t net) draw_lpm_abs(net); return; + case IVL_LPM_CAST_INT: + draw_lpm_cast_int(net); + return; + + case IVL_LPM_CAST_REAL: + draw_lpm_cast_real(net); + return; + case IVL_LPM_ADD: case IVL_LPM_SUB: case IVL_LPM_MULT: @@ -2383,10 +1679,6 @@ static void draw_lpm_in_scope(ivl_lpm_t net) draw_lpm_array(net); return; - case IVL_LPM_PART_BI: - draw_lpm_part_bi(net); - return; - case IVL_LPM_PART_VP: draw_lpm_part(net); return; @@ -2569,6 +1861,11 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent) draw_lpm_in_scope(lpm); } + for (idx = 0 ; idx < ivl_scope_switches(net) ; idx += 1) { + ivl_switch_t sw = ivl_scope_switch(net, idx); + draw_switch_in_scope(sw); + } + if (ivl_scope_type(net) == IVL_SCT_TASK) draw_task_definition(net); diff --git a/verinum.cc b/verinum.cc index a69b2d194..87f28de04 100644 --- a/verinum.cc +++ b/verinum.cc @@ -205,7 +205,7 @@ verinum::verinum(int64_t that) bits_ = new V[nbits_]; for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) { bits_[idx] = (that & 1)? V1 : V0; - that /= 2; + that >>= 1; } } @@ -508,7 +508,7 @@ verinum trim_vnum(const verinum&that) /* Now top is the index of the highest non-zero bit. If that turns out to the highest bit in the vector, then - tere is no trimming possible. */ + there is no trimming possible. */ if (top+1 == that.len()) return that; diff --git a/vpi/Makefile.in b/vpi/Makefile.in index 4b8bca964..f9b28a57d 100644 --- a/vpi/Makefile.in +++ b/vpi/Makefile.in @@ -59,7 +59,8 @@ O = sys_table.o sys_convert.o sys_deposit.o sys_display.o sys_fileio.o \ sys_finish.o sys_icarus.o sys_plusargs.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 priv.o sdf_lexor.o sdf_parse.o stringheap.o +mt19937int.o sys_priv.o sdf_lexor.o sdf_parse.o stringheap.o \ +vams_simparam.o ifeq (@HAVE_LIBZ@,yes) ifeq (@HAVE_LIBBZ2@,yes) diff --git a/vpi/sdf_lexor.lex b/vpi/sdf_lexor.lex index c14b89cca..0a92c5eb6 100644 --- a/vpi/sdf_lexor.lex +++ b/vpi/sdf_lexor.lex @@ -139,7 +139,7 @@ static int lookup_keyword(const char*text) } /* - * Create a string witout the leading and trailing quotes. + * Create a string without the leading and trailing quotes. */ static void process_quoted_string(void) { diff --git a/vpi/sys_display.c b/vpi/sys_display.c index c291d00e3..0cd571cc9 100644 --- a/vpi/sys_display.c +++ b/vpi/sys_display.c @@ -60,16 +60,6 @@ struct strobe_cb_info { unsigned mcd; }; -int is_constant(vpiHandle obj) -{ - if (vpi_get(vpiType, obj) == vpiConstant) - return vpiConstant; - if (vpi_get(vpiType, obj) == vpiParameter) - return vpiParameter; - - return 0; -} - // The number of decimal digits needed to represent a // nr_bits binary number is floor(nr_bits*log_10(2))+1, // where log_10(2) = 0.30102999566398.... and I approximate @@ -277,7 +267,7 @@ static void format_time(unsigned mcd, int fsize, /* Fill the leading characters to make up the desired width. This may require a '0' if the last character written was the decimal point. This may also require a '0' - if there are no other characters at all in the ouput. */ + if there are no other characters at all in the output. */ if (fusize > 0) { while (bp > start_address) { if (*bp == '.' || strcmp(bp, timeformat_info.suff) == 0) @@ -665,7 +655,7 @@ static int format_str_char(vpiHandle scope, unsigned int mcd, return 0; } - if (is_constant(argv[idx]) + if (is_constant_obj(argv[idx]) && (vpi_get(vpiConstType, argv[idx]) == vpiRealConst)) { value.format = vpiRealVal; @@ -827,6 +817,7 @@ static void do_display(unsigned int mcd, struct strobe_cb_info*info) case vpiReg: case vpiIntegerVar: case vpiMemoryWord: + case vpiPartSelect: do_display_numeric(mcd, info, item); break; @@ -1118,11 +1109,17 @@ static PLI_INT32 sys_monitor_calltf(PLI_BYTE8*name) for (idx = 0 ; idx < monitor_info.nitems ; idx += 1) { switch (vpi_get(vpiType, monitor_info.items[idx])) { + case vpiMemoryWord: + /* + * We only support constant selections. Make this + * better when we add a real compiletf routine. + */ + assert(vpi_get(vpiConstantSelect, monitor_info.items[idx])); case vpiNet: case vpiReg: case vpiIntegerVar: case vpiRealVar: - case vpiMemoryWord: + case vpiPartSelect: /* Monitoring reg and net values involves setting a callback for value changes. Pass the storage pointer for the callback itself as user_data so @@ -1418,7 +1415,7 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus, vpi_printf("WARNING: incompatible value for %s%s.\n", info->name, fmtb); } else { - /* If a width was not giveni, use a width of zero. */ + /* If a width was not given, use a width of zero. */ if (width == -1) width = 0; if (ljust == 0) sprintf(result, "%*c", width, value.value.str[strlen(value.value.str)-1]); @@ -1662,7 +1659,7 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus, /* If a width was not given use a width of zero. */ if (width == -1) width = 0; nbits = vpi_get(vpiSize, info->items[*idx]); - /* This is 4 chars for all but the last bit (strenght + "_") + /* This is 4 chars for all but the last bit (strength + "_") * which only needs three chars (strength), but then you need * space for the EOS '\0', so it is just number of bits * 4. */ rsize = nbits*4; @@ -1836,6 +1833,7 @@ static char *get_display(unsigned int *rtnsz, struct strobe_cb_info *info) case vpiReg: case vpiIntegerVar: case vpiMemoryWord: + case vpiPartSelect: width = get_numeric(&result, info, item); rtn = realloc(rtn, (size+width)*sizeof(char)); memcpy(rtn+size-1, result, width); @@ -2185,12 +2183,7 @@ static PLI_INT32 sys_printtimescale_calltf(PLI_BYTE8*xx) vpiHandle argv = vpi_iterate(vpiArgument, sys); vpiHandle scope; if (!argv) { - vpiHandle parent = vpi_handle(vpiScope, sys); - scope = NULL; /* fallback value if parent is NULL */ - while (parent) { - scope = parent; - parent = vpi_handle(vpiScope, scope); - } + scope = sys_func_module(sys); } else { scope = vpi_scan(argv); vpi_free_object(argv); diff --git a/vpi/sys_fileio.c b/vpi/sys_fileio.c index 996566640..b763bb776 100644 --- a/vpi/sys_fileio.c +++ b/vpi/sys_fileio.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -16,13 +16,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: sys_fileio.c,v 1.10 2007/03/14 04:05:51 steve Exp $" -#endif # include "vpi_user.h" # include "sys_priv.h" # include +# include # include # include # include @@ -33,95 +31,178 @@ /* * Implement the $fopen system function. */ -static PLI_INT32 sys_fopen_compiletf(PLI_BYTE8*name) +static PLI_INT32 sys_fopen_compiletf(PLI_BYTE8 *name) { - vpiHandle sys = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, sys); - vpiHandle item; + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + assert(callh != 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + /* Check that there is a file name argument and that it is a string. */ if (argv == 0) { - vpi_printf("%s: file name argument missing.\n", name); + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a string file name argument.\n", name); vpi_control(vpiFinish, 1); - return -1; - } - - item = vpi_scan(argv); - if (item == 0) { - vpi_printf("%s: file name argument missing.\n", name); - vpi_control(vpiFinish, 1); - return -1; - } - - item = vpi_scan(argv); - if (item == 0) { - /* The mode argument is optional. It is OK for it - to be missing. In this case, there are no more - arguments, and we're done. */ return 0; } - - if (! is_constant(item)) { - vpi_printf("ERROR: %s mode argument must be a constant\n", name); + if (! is_string_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's file name argument must be a string.\n", name); vpi_control(vpiFinish, 1); } - if (vpi_get(vpiConstType, item) != vpiStringConst) { - vpi_printf("ERROR: %s mode argument must be a string.\n", name); + /* The type argument is optional. */ + arg = vpi_scan(argv); + if (arg == 0) return 0; + + /* When provided, the type argument must be a string. */ + if (! is_string_obj(arg)) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's type argument must be a string.\n", name); vpi_control(vpiFinish, 1); } - - item = vpi_scan(argv); - if (item == 0) { - /* There should be no more arguments. */ - return 0; + + /* Make sure there are no extra arguments. */ + if (vpi_scan(argv) != 0) { + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 1; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s %s takes at most two string arguments.\n", + msg, name); + vpi_printf("%*s Found %u extra argument%s.\n", + (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); } - vpi_free_object(argv); - vpi_printf("%s: Too many arguments to system function.\n", name); return 0; } static PLI_INT32 sys_fopen_calltf(PLI_BYTE8*name) { - s_vpi_value value; + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + s_vpi_value val; + int fail = 0; char *mode_string = 0; - - vpiHandle call_handle = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, call_handle); + unsigned idx; vpiHandle item = vpi_scan(argv); vpiHandle mode = vpi_scan(argv); - assert(item); - + /* Get the mode handle if it exists. */ if (mode) { - value.format = vpiStringVal; - vpi_get_value(mode, &value); - mode_string = strdup(value.value.str); + val.format = vpiStringVal; + vpi_get_value(mode, &val); + /* Verify that we have a string and that it is not NULL. */ + if (val.format != vpiStringVal || !*(val.value.str)) { + vpi_printf("WARNING: %s line %d: ", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's mode argument is not a valid string.\n", + name); + fail = 1; + } - vpi_free_object(argv); + /* Make sure the mode string is correct. */ + if (strlen(val.value.str) > 3) { + vpi_printf("WARNING: %s line %d: ", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's mode argument (%s) is too long.\n", + name, val.value.str); + fail = 1; + } else { + unsigned bin = 0, plus = 0; + switch (val.value.str[0]) { + case 'r': + case 'w': + case 'a': + for (idx = 1; idx < 3 ; idx++) { + if (val.value.str[idx] == '\0') break; + switch (val.value.str[idx]) { + case 'b': + if (bin) fail = 1; + bin = 1; + break; + case '+': + if (plus) fail = 1; + plus = 1; + break; + default: + fail = 1; + break; + } + } + if (! fail) break; + + default: + vpi_printf("WARNING: %s line %d: ", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's mode argument (%s) is invalid.\n", + name, val.value.str); + fail = 1; + break; + } + } + + mode_string = strdup(val.value.str); + + vpi_free_object(argv); } /* Get the string form of the file name from the file name argument. */ - value.format = vpiStringVal; - vpi_get_value(item, &value); + val.format = vpiStringVal; + vpi_get_value(item, &val); - if ((value.format != vpiStringVal) || !value.value.str) { - vpi_printf("ERROR: %s: File name argument (type=%d)" - " does not have a string value\n", - name, vpi_get(vpiType, item)); + /* Verify that we have a string and that it is not NULL. */ + if (val.format != vpiStringVal || !*(val.value.str)) { + vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's file name argument is not a valid string.\n", + name); + fail = 1; if (mode) free(mode_string); - return 0; } - value.format = vpiIntVal; + /* + * Verify that the file name is composed of only printable + * characters. + */ + unsigned len = strlen(val.value.str); + for (idx = 0; idx < len; idx++) { + if (! isprint(val.value.str[idx])) { + char msg [64]; + snprintf(msg, 64, "WARNING: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s %s's file name argument contains non-" + "printable characters.\n", msg, name); + vpi_printf("%*s \"%s\"\n", (int) strlen(msg), " ", val.value.str); + fail = 1; + if (mode) free(mode_string); + } + } + + /* If either the mode or file name are not valid just return. */ + if (fail) return 0; + + val.format = vpiIntVal; if (mode) { - value.value.integer = vpi_fopen(value.value.str, mode_string); + val.value.integer = vpi_fopen(val.value.str, mode_string); free(mode_string); } else - value.value.integer = vpi_mcd_open(value.value.str); + val.value.integer = vpi_mcd_open(val.value.str); - vpi_put_value(call_handle, &value, 0, vpiNoDelay); + vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } @@ -130,59 +211,56 @@ static PLI_INT32 sys_fopen_calltf(PLI_BYTE8*name) * Implement the $fopenr(), $fopenw() and $fopena() system functions * from Chris Spear's File I/O for Verilog. */ -static PLI_INT32 sys_fopenrwa_compiletf(PLI_BYTE8*name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle file; - - /* Check that there are arguments. */ - if (argv == 0) { - vpi_printf("ERROR: %s requires a single argument.\n", name); - vpi_control(vpiFinish, 1); - return 0; - } - - file = vpi_scan(argv); /* This should never be zero. */ - - /* These functions take at most one argument. */ - file = vpi_scan(argv); - if (file != 0) { - vpi_printf("ERROR: %s takes only a single argument.\n", name); - vpi_control(vpiFinish, 1); - return 0; - } - - /* vpi_scan returning 0 (NULL) has already freed argv. */ - return 0; -} static PLI_INT32 sys_fopenrwa_calltf(PLI_BYTE8*name) { - s_vpi_value val; - char *mode; vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle file = vpi_scan(argv); vpi_free_object(argv); - /* Get the mode. */ + s_vpi_value val; + char *mode; + + /* Get the mode. */ mode = name + strlen(name) - 1; - /* Get the filename. */ + /* Get the filename. */ val.format = vpiStringVal; vpi_get_value(file, &val); - if ((val.format != vpiStringVal) || !val.value.str) { - vpi_printf("ERROR: %s's file name argument must be a string.\n", - name); - vpi_control(vpiFinish, 1); + + /* Verify that we have a string and that it is not NULL. */ + if (val.format != vpiStringVal || !*(val.value.str)) { + vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's file name argument is not a valid string.\n", + name); return 0; } - /* Open the file and return the result. */ + /* + * Verify that the file name is composed of only printable + * characters. + */ + unsigned idx, len = strlen(val.value.str); + for (idx = 0; idx < len; idx++) { + if (! isprint(val.value.str[idx])) { + char msg [64]; + snprintf(msg, 64, "WARNING: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s %s's file name argument contains non-" + "printable characters.\n", msg, name); + vpi_printf("%*s \"%s\"\n", (int) strlen(msg), " ", val.value.str); + return 0; + } + } + + /* Open the file and return the result. */ val.format = vpiIntVal; val.value.integer = vpi_fopen(val.value.str, mode); vpi_put_value(callh, &val, 0, vpiNoDelay); + return 0; } @@ -190,76 +268,22 @@ static PLI_INT32 sys_fopenrwa_calltf(PLI_BYTE8*name) * Implement $fclose system function */ static PLI_INT32 sys_fclose_calltf(PLI_BYTE8*name) -{ - unsigned int mcd; - int type; - s_vpi_value value; - - vpiHandle sys = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, sys); - vpiHandle item = vpi_scan(argv); - - if (item == 0) { - vpi_printf("%s: mcd parameter missing.\n", name); - return 0; - } - type = vpi_get(vpiType, item); - switch (type) { - case vpiReg: - case vpiRealVal: - case vpiIntegerVar: - break; - default: - vpi_printf("ERROR: %s mcd parameter must be of integral type", - name); - vpi_printf(", got vpiType=%d\n", type); - vpi_free_object(argv); - return 0; - } - - value.format = vpiIntVal; - vpi_get_value(item, &value); - mcd = value.value.integer; - - vpi_mcd_close(mcd); - return 0; -} - -static PLI_INT32 sys_fflush_compiletf(PLI_BYTE8 *ud) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle item; - PLI_INT32 type; + vpiHandle fd = vpi_scan(argv); + vpi_free_object(argv); + (void) name; /* Not used! */ - /* The argument is optional. */ - if (argv == 0) { - return 0; - } - /* Check that the file/MC descriptor is the right type. */ - item = vpi_scan(argv); - type = vpi_get(vpiType, item); - switch (type) { - case vpiReg: - case vpiRealVal: - case vpiIntegerVar: - break; - default: - vpi_printf("ERROR: %s fd parameter must be integral", ud); - vpi_printf(", got vpiType=%d\n", type); - vpi_control(vpiFinish, 1); - return 0; - } + s_vpi_value val; + PLI_UINT32 fd_mcd; - /* Check that there is at most one argument. */ - item = vpi_scan(argv); - if (item != 0) { - vpi_printf("ERROR: %s takes at most a single argument.\n", ud); - vpi_control(vpiFinish, 1); - return 0; - } + val.format = vpiIntVal; + vpi_get_value(fd, &val); + fd_mcd = val.value.integer; + + vpi_mcd_close(fd_mcd); - /* vpi_scan returning 0 (NULL) has already freed argv. */ return 0; } @@ -267,28 +291,29 @@ static PLI_INT32 sys_fflush_calltf(PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle item; + vpiHandle arg; s_vpi_value val; - PLI_INT32 fd_mcd; + PLI_UINT32 fd_mcd; FILE *fp; + (void) name; /* Not used! */ - /* If we have no argument then flush all the streams. */ + /* If we have no argument then flush all the streams. */ if (argv == 0) { fflush(NULL); return 0; } - /* Get the file/MC descriptor. */ - item = vpi_scan(argv); + /* Get the file/MC descriptor. */ + arg = vpi_scan(argv); vpi_free_object(argv); val.format = vpiIntVal; - vpi_get_value(item, &val); + vpi_get_value(arg, &val); fd_mcd = val.value.integer; if (IS_MCD(fd_mcd)) { vpi_mcd_flush(fd_mcd); } else { - /* If we have a valid file descriptor flush the file. */ + /* If we have a valid file descriptor flush the file. */ fp = vpi_get_file(fd_mcd); if (fp) fflush(fp); } @@ -296,495 +321,406 @@ static PLI_INT32 sys_fflush_calltf(PLI_BYTE8*name) return 0; } - static PLI_INT32 sys_fputc_calltf(PLI_BYTE8*name) { - unsigned int mcd; - int type; - unsigned char x; - s_vpi_value value, xvalue; - vpiHandle sys = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, sys); - vpiHandle item = vpi_scan(argv); + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + s_vpi_value val; + PLI_UINT32 fd_mcd; FILE *fp; + unsigned char chr; + (void) name; /* Not used! */ - if (item == 0) { - vpi_printf("%s: mcd parameter missing.\n", name); - return 0; - } + /* Get the character. */ + arg = vpi_scan(argv); + val.format = vpiIntVal; + vpi_get_value(arg, &val); + chr = val.value.integer; - type = vpi_get(vpiType, item); - switch (type) { - case vpiReg: - case vpiRealVal: - case vpiIntegerVar: - break; - default: - vpi_printf("ERROR: %s mcd parameter must be of integral", name); - vpi_printf(", got vpiType=%d\n", type); - vpi_free_object(argv); - return 0; - } - value.format = vpiIntVal; - vpi_get_value(item, &value); - mcd = value.value.integer; + /* Get the file/MC descriptor. */ + arg = vpi_scan(argv); + vpi_free_object(argv); + val.format = vpiIntVal; + vpi_get_value(arg, &val); + fd_mcd = val.value.integer; - if (IS_MCD(mcd)) return EOF; - - item = vpi_scan(argv); - - xvalue.format = vpiIntVal; - vpi_get_value(item, &xvalue); - x = xvalue.value.integer; - - fp = vpi_get_file(mcd); - if (!fp) return EOF; - - return fputc(x, fp); -} - -static PLI_INT32 sys_fgetc_calltf(PLI_BYTE8*name) -{ - unsigned int mcd; - int type; - s_vpi_value value, rval; - vpiHandle sys = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, sys); - vpiHandle item = vpi_scan(argv); - FILE *fp; - - if (item == 0) { - vpi_printf("%s: mcd parameter missing.\n", name); - return 0; - } - - type = vpi_get(vpiType, item); - switch (type) { - case vpiReg: - case vpiRealVal: - case vpiIntegerVar: - break; - default: - vpi_printf("ERROR: %s mcd parameter must be of integral", name); - vpi_printf(", got vpiType=%d\n", type); - vpi_free_object(argv); - return 0; - } - - value.format = vpiIntVal; - vpi_get_value(item, &value); - mcd = value.value.integer; - - rval.format = vpiIntVal; - - fp = vpi_get_file(mcd); - if (!fp || IS_MCD(mcd)) - rval.value.integer = EOF; - else - rval.value.integer = fgetc(fp); - - vpi_put_value(sys, &rval, 0, vpiNoDelay); + /* Put the character and return the result. */ + fp = vpi_get_file(fd_mcd); + val.format = vpiIntVal; + if (!fp || IS_MCD(fd_mcd)) { + vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("invalid file descriptor (0x%x) given to %s.\n", fd_mcd, + name); + val.value.integer = EOF; + } else + val.value.integer = fputc(chr, fp); + vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } static PLI_INT32 sys_fgets_compiletf(PLI_BYTE8*name) { - vpiHandle sys = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, sys); - vpiHandle item = vpi_scan(argv); - int type; + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; - if (item == 0) { - vpi_printf("%s: string parameter missing.\n", name); + /* + * Check that there are two arguments and that the first is a + * register and that the second is numeric. + */ + if (argv == 0) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires two arguments.\n", name); + vpi_control(vpiFinish, 1); return 0; } - type = vpi_get(vpiType, item); + if (vpi_get(vpiType, vpi_scan(argv)) != vpiReg) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's first argument must be a reg.\n", name); + vpi_control(vpiFinish, 1); + } - if (type != vpiReg) { - vpi_printf("%s: string parameter must be a reg.\n", name); - vpi_free_object(argv); + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a second (numeric) argument.\n", name); + vpi_control(vpiFinish, 1); return 0; } - item = vpi_scan(argv); - if (item == 0) { - vpi_printf("%s: mcd parameter missing.\n", name); - return 0; + if (! is_numeric_obj(arg)) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's second argument must be numeric.\n", name); + vpi_control(vpiFinish, 1); } - /* That should be all the arguments. */ - item = vpi_scan(argv); - assert(item == 0); + /* Make sure there are no extra arguments. */ + if (vpi_scan(argv) != 0) { + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 1; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s %s takes two arguments.\n", msg, name); + vpi_printf("%*s Found %u extra argument%s.\n", + (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); + } return 0; } static PLI_INT32 sys_fgets_calltf(PLI_BYTE8*name) { - unsigned int mcd; - FILE*fd; - s_vpi_value value, rval; + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle regh; + vpiHandle arg; + s_vpi_value val; + PLI_UINT32 fd_mcd; + FILE *fp; + PLI_INT32 reg_size; + char*text; + (void) name; /* Not used! */ - char*txt; - unsigned txt_len; + /* Get the register handle. */ + regh = vpi_scan(argv); - vpiHandle sys = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, sys); - vpiHandle str = vpi_scan(argv); - vpiHandle mch = vpi_scan(argv); + /* Get the file/MCD descriptor. */ + arg = vpi_scan(argv); + vpi_free_object(argv); + val.format = vpiIntVal; + vpi_get_value(arg, &val); + fd_mcd = val.value.integer; - value.format = vpiIntVal; - vpi_get_value(mch, &value); - mcd = value.value.integer; - - fd = vpi_get_file(mcd); - if (!fd || IS_MCD(mcd)) { - rval.format = vpiIntVal; - rval.value.integer = 0; - vpi_put_value(sys, &rval, 0, vpiNoDelay); + /* Return zero if this is not a valid fd. */ + fp = vpi_get_file(fd_mcd); + if (!fp || IS_MCD(fd_mcd)) { + vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("invalid file descriptor (0x%x) given to %s.\n", fd_mcd, + name); + val.format = vpiIntVal; + val.value.integer = 0; + vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } - txt_len = vpi_get(vpiSize, str) / 8; - txt = malloc(txt_len + 1); + /* Get the register size in bytes and allocate the buffer. */ + reg_size = vpi_get(vpiSize, regh) / 8; + text = malloc(reg_size + 1); - if (fgets(txt, txt_len+1, fd) == 0) { - rval.format = vpiIntVal; - rval.value.integer = 0; - vpi_put_value(sys, &rval, 0, vpiNoDelay); - free(txt); + /* Read in the bytes. Return 0 if there was an error. */ + if (fgets(text, reg_size+1, fp) == 0) { + val.format = vpiIntVal; + val.value.integer = 0; + vpi_put_value(callh, &val, 0, vpiNoDelay); + free(text); return 0; } - rval.format = vpiIntVal; - rval.value.integer = strlen(txt); - vpi_put_value(sys, &rval, 0, vpiNoDelay); + /* Return the number of character read. */ + val.format = vpiIntVal; + val.value.integer = strlen(text); + vpi_put_value(callh, &val, 0, vpiNoDelay); - value.format = vpiStringVal; - value.value.str = txt; - vpi_put_value(str, &value, 0, vpiNoDelay); - - free(txt); - - return 0; -} - -static PLI_INT32 sys_ungetc_compiletf(PLI_BYTE8*name) -{ - int type; - vpiHandle sys = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, sys); - vpiHandle item = vpi_scan(argv); - - if (item == 0) { - vpi_printf("%s: character parameter missing.\n", name); - return 0; - } - type = vpi_get(vpiType, item); - switch (type) { - case vpiReg: - case vpiRealVal: // Is this correct? - case vpiIntegerVar: - break; - default: - vpi_printf("ERROR: %s character parameter must be ", name); - vpi_printf("integral, got vpiType=%d\n", type); - vpi_free_object(argv); - return 0; - } - - item = vpi_scan(argv); - type = vpi_get(vpiType, item); - switch (type) { - case vpiReg: - case vpiRealVal: // Is this correct? - case vpiIntegerVar: - break; - default: - vpi_printf("ERROR: %s mcd parameter must be integral, ", name); - vpi_printf("got vpiType=%d\n", type); - vpi_free_object(argv); - return 0; - } - - /* That should be all the arguments. */ - item = vpi_scan(argv); - assert(item == 0); + /* Return the characters to the register. */ + val.format = vpiStringVal; + val.value.str = text; + vpi_put_value(regh, &val, 0, vpiNoDelay); + free(text); return 0; } static PLI_INT32 sys_ungetc_calltf(PLI_BYTE8*name) -{ - unsigned int mcd; - unsigned char x; - s_vpi_value val, rval; - vpiHandle sys = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, sys); - vpiHandle item = vpi_scan(argv); - FILE *fp; - - rval.format = vpiIntVal; - - val.format = vpiIntVal; - vpi_get_value(item, &val); - x = val.value.integer; - - item = vpi_scan(argv); - - val.format = vpiIntVal; - vpi_get_value(item, &val); - mcd = val.value.integer; - - if (IS_MCD(mcd)) { - rval.value.integer = EOF; - vpi_put_value(sys, &rval, 0, vpiNoDelay); - return 0; - } - - fp = vpi_get_file(mcd); - if ( !fp ) { - rval.value.integer = EOF; - vpi_put_value(sys, &rval, 0, vpiNoDelay); - return 0; - } - - ungetc(x, fp); - - rval.value.integer = 0; - vpi_put_value(sys, &rval, 0, vpiNoDelay); - return 0; -} - -static PLI_INT32 sys_check_fd_compiletf(PLI_BYTE8 *ud) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle item; - PLI_INT32 type; - - /* Check that there is an argument. */ - if (argv == 0) { - vpi_printf("ERROR: %s requires an argument.\n", ud); - vpi_control(vpiFinish, 1); - return 0; - } - /* Check that the file descriptor is the right type. */ - item = vpi_scan(argv); - type = vpi_get(vpiType, item); - switch (type) { - case vpiReg: - case vpiRealVal: - case vpiIntegerVar: - break; - default: - vpi_printf("ERROR: %s fd parameter must be integral", ud); - vpi_printf(", got vpiType=%d\n", type); - vpi_control(vpiFinish, 1); - return 0; - } - - /* Check that there is at most one argument. */ - item = vpi_scan(argv); - if (item != 0) { - vpi_printf("ERROR: %s takes a single argument.\n", ud); - vpi_control(vpiFinish, 1); - return 0; - } - - /* vpi_scan returning 0 (NULL) has already freed argv. */ - return 0; -} - -static PLI_INT32 sys_rewind_calltf(PLI_BYTE8 *ud) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle item = vpi_scan(argv); + vpiHandle arg; s_vpi_value val; - PLI_INT32 fd; + PLI_UINT32 fd_mcd; FILE *fp; + unsigned char chr; + (void) name; /* Not used! */ - /* Get the file pointer. */ + /* Get the character. */ + arg = vpi_scan(argv); + val.format = vpiIntVal; + vpi_get_value(arg, &val); + chr = val.value.integer; + + /* Get the file/MC descriptor. */ + arg = vpi_scan(argv); vpi_free_object(argv); val.format = vpiIntVal; - vpi_get_value(item, &val); - fd = val.value.integer; - if (IS_MCD(fd)) { - vpi_printf("ERROR: %s cannot be used with a MCD.\n", ud); - vpi_control(vpiFinish, 1); + vpi_get_value(arg, &val); + fd_mcd = val.value.integer; + + /* Return EOF if this is not a valid fd. */ + fp = vpi_get_file(fd_mcd); + if (!fp || IS_MCD(fd_mcd)) { + vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("invalid file descriptor (0x%x) given to %s.\n", fd_mcd, + name); + val.format = vpiIntVal; + val.value.integer = EOF; + vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } - fp = vpi_get_file(fd); - /* If we have a valid file descriptor rewind the file. */ - if (!fp) { - val.value.integer = EOF; - } else { - rewind(fp); - val.value.integer = 0; - } - vpi_put_value(callh, &val, 0 , vpiNoDelay); - - return 0; -} - -/* $feof() is from 1364-2005. */ -static PLI_INT32 sys_ftell_feof_calltf(PLI_BYTE8 *ud) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle item = vpi_scan(argv); - s_vpi_value val; - PLI_INT32 fd; - FILE *fp; - - /* Get the file pointer. */ - vpi_free_object(argv); + /* ungetc the character and return the result. */ val.format = vpiIntVal; - vpi_get_value(item, &val); - fd = val.value.integer; - if (IS_MCD(fd)) { - vpi_printf("ERROR: %s cannot be used with a MCD.\n", ud); - vpi_control(vpiFinish, 1); - return 0; - } - fp = vpi_get_file(fd); - - /* If we do not have a valid file descriptor return EOF, otherwise - * return that value. */ - if (!fp) { - val.value.integer = EOF; - } else { - if (ud[2] == 'e') { - val.value.integer = feof(fp); - } else { - val.value.integer = ftell(fp); - } - } - vpi_put_value(callh, &val, 0 , vpiNoDelay); + val.value.integer = ungetc(chr, fp); + vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } -static PLI_INT32 sys_fseek_compiletf(PLI_BYTE8 *ud) +static PLI_INT32 sys_fseek_compiletf(PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle item; - PLI_INT32 type; + vpiHandle arg; - /* Check that there is an argument. */ + /* Check that there are three numeric arguments. */ if (argv == 0) { - vpi_printf("ERROR: %s requires three arguments.\n", ud); - vpi_control(vpiFinish, 1); - return 0; - } - /* Check that the file descriptor is the right type. */ - item = vpi_scan(argv); - type = vpi_get(vpiType, item); - switch (type) { - case vpiReg: - case vpiRealVal: - case vpiIntegerVar: - break; - default: - vpi_printf("ERROR: %s fd parameter must be integral", ud); - vpi_printf(", got vpiType=%d\n", type); - vpi_control(vpiFinish, 1); - return 0; - } - - /* Check that there is an offset argument. */ - item = vpi_scan(argv); - if (item == 0) { - vpi_printf("ERROR: %s is missing an offset and operation " - "argument.\n", ud); + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires three arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } - /* Check that there is an operation argument. */ - item = vpi_scan(argv); - if (item == 0) { - vpi_printf("ERROR: %s is missing an operation argument.\n", ud); + /* Check that the first argument is numeric. */ + if (! is_numeric_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's first argument must be numeric.\n", name); + vpi_control(vpiFinish, 1); + } + + /* Check that the second argument exists and is numeric. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a second (numeric) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } - /* Check that there is at most one argument. */ - item = vpi_scan(argv); - if (item != 0) { - vpi_printf("ERROR: %s takes at most three argument.\n", ud); + if (!arg || !is_numeric_obj(arg)) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's second argument must be numeric.\n", name); + vpi_control(vpiFinish, 1); + } + + /* Check that the third argument exists and is numeric. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a third (numeric) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } - /* vpi_scan returning 0 (NULL) has already freed argv. */ + if (!arg || !is_numeric_obj(arg)) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's third argument must be numeric.\n", name); + vpi_control(vpiFinish, 1); + } + + /* Make sure there are no extra arguments. */ + if (vpi_scan(argv) != 0) { + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 1; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s %s takes three arguments.\n", msg, name); + vpi_printf("%*s Found %u extra argument%s.\n", + (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); + } return 0; } -static PLI_INT32 sys_fseek_calltf(PLI_BYTE8 *ud) +static PLI_INT32 sys_fseek_calltf(PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle item; + vpiHandle arg; s_vpi_value val; - PLI_INT32 fd, offset, oper; + PLI_UINT32 fd_mcd; + PLI_INT32 offset, oper; FILE *fp; + + /* Get the file pointer. */ + arg = vpi_scan(argv); val.format = vpiIntVal; + vpi_get_value(arg, &val); + fd_mcd = val.value.integer; - /* Get the file pointer. */ - item = vpi_scan(argv); - vpi_get_value(item, &val); - fd = val.value.integer; - if (IS_MCD(fd)) { - vpi_printf("ERROR: %s cannot be used with a MCD.\n", ud); - vpi_control(vpiFinish, 1); - return 0; - } - fp = vpi_get_file(fd); - - /* Get the offset. */ - item = vpi_scan(argv); - vpi_get_value(item, &val); + /* Get the offset. */ + arg = vpi_scan(argv); + val.format = vpiIntVal; + vpi_get_value(arg, &val); offset = val.value.integer; - /* Get the operation. */ - item = vpi_scan(argv); - vpi_get_value(item, &val); + /* Get the operation. */ + arg = vpi_scan(argv); + vpi_free_object(argv); + val.format = vpiIntVal; + vpi_get_value(arg, &val); oper = val.value.integer; - /* Should this translate to the SEEK_??? codes? */ - /* What about verifying offset value vs operation ($fseek(fd, -1, 0))? */ + + /* Check that the operation is in the valid range. */ if ((oper < 0) || (oper > 2)) { - vpi_printf("ERROR: %s's operation must be 0, 1 or 2 given %d.\n", - ud, oper); - vpi_control(vpiFinish, 1); + vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's operation must be 0, 1 or 2 given %d.\n", + name, oper); + val.format = vpiIntVal; + val.value.integer = EOF; + vpi_put_value(callh, &val, 0, vpiNoDelay); return 0; } - /* If we do not have a valid file descriptor return EOF, otherwise - * return that value. */ - if (!fp) { + /* Return EOF if this is not a valid fd. */ + fp = vpi_get_file(fd_mcd); + if (!fp || IS_MCD(fd_mcd)) { + vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("invalid file descriptor (0x%x) given to %s.\n", fd_mcd, + name); + val.format = vpiIntVal; val.value.integer = EOF; - } else { - fseek(fp, offset, oper); - val.value.integer = 0; + vpi_put_value(callh, &val, 0, vpiNoDelay); + return 0; } + + val.format = vpiIntVal; + val.value.integer = fseek(fp, offset, oper); vpi_put_value(callh, &val, 0 , vpiNoDelay); - vpi_free_object(argv); return 0; } -static PLI_INT32 sys_integer_sizetf(PLI_BYTE8 *ud) +static PLI_INT32 sys_common_fd_calltf(PLI_BYTE8*name) { - return 32; + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + s_vpi_value val; + PLI_UINT32 fd_mcd; + FILE *fp; + + /* Get the file pointer. */ + arg = vpi_scan(argv); + vpi_free_object(argv); + val.format = vpiIntVal; + vpi_get_value(arg, &val); + fd_mcd = val.value.integer; + + /* Return EOF if this is not a valid fd. */ + fp = vpi_get_file(fd_mcd); + if (!fp || IS_MCD(fd_mcd)) { + vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("invalid file descriptor (0x%x) given to %s.\n", fd_mcd, + name); + val.format = vpiIntVal; + val.value.integer = EOF; + vpi_put_value(callh, &val, 0, vpiNoDelay); + return 0; + } + + val.format = vpiIntVal; + switch (name[4]) { + case 'l': /* $ftell() */ + val.value.integer = ftell(fp); + break; + case 'f': /* $feof() is from 1264-2005*/ + val.value.integer = feof(fp); + break; + case 'i': /* $rewind() */ + val.value.integer = fseek(fp, 0L, SEEK_SET); + break; + case 't': /* $fgetc() */ + val.value.integer = fgetc(fp); + break; + default: + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s cannot be processed with this routine.\n", name); + assert(0); + break; + } + vpi_put_value(callh, &val, 0 , vpiNoDelay); + + return 0; } void sys_fileio_register() @@ -792,38 +728,40 @@ void sys_fileio_register() s_vpi_systf_data tf_data; //============================== fopen - tf_data.type = vpiSysFunc; - tf_data.tfname = "$fopen"; - tf_data.calltf = sys_fopen_calltf; - tf_data.compiletf = sys_fopen_compiletf; - tf_data.sizetf = sys_integer_sizetf; - tf_data.user_data = "$fopen"; + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiIntFunc; + tf_data.tfname = "$fopen"; + tf_data.calltf = sys_fopen_calltf; + tf_data.compiletf = sys_fopen_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$fopen"; vpi_register_systf(&tf_data); //============================== fopenr - tf_data.type = vpiSysFunc; - tf_data.tfname = "$fopenr"; - tf_data.calltf = sys_fopenrwa_calltf; - tf_data.compiletf = sys_fopenrwa_compiletf; - tf_data.sizetf = sys_integer_sizetf; - tf_data.user_data = "$fopenr"; + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiIntFunc; + tf_data.tfname = "$fopenr"; + tf_data.calltf = sys_fopenrwa_calltf; + tf_data.compiletf = sys_one_string_arg_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$fopenr"; vpi_register_systf(&tf_data); //============================== fopenw - tf_data.tfname = "$fopenw"; - tf_data.user_data = "$fopenw"; + tf_data.tfname = "$fopenw"; + tf_data.user_data = "$fopenw"; vpi_register_systf(&tf_data); //============================== fopena - tf_data.tfname = "$fopena"; - tf_data.user_data = "$fopena"; + tf_data.tfname = "$fopena"; + tf_data.user_data = "$fopena"; vpi_register_systf(&tf_data); //============================== fclose tf_data.type = vpiSysTask; tf_data.tfname = "$fclose"; tf_data.calltf = sys_fclose_calltf; - tf_data.compiletf = 0; + tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fclose"; vpi_register_systf(&tf_data); @@ -832,7 +770,7 @@ void sys_fileio_register() tf_data.type = vpiSysTask; tf_data.tfname = "$fflush"; tf_data.calltf = sys_fflush_calltf; - tf_data.compiletf = sys_fflush_compiletf; + tf_data.compiletf = sys_one_opt_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fflush"; vpi_register_systf(&tf_data); @@ -841,24 +779,24 @@ void sys_fileio_register() tf_data.type = vpiSysTask; tf_data.tfname = "$fputc"; tf_data.calltf = sys_fputc_calltf; - tf_data.compiletf = 0; + tf_data.compiletf = sys_two_numeric_args_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$fputc"; vpi_register_systf(&tf_data); //============================== fgetc tf_data.type = vpiSysFunc; - tf_data.sysfunctype = vpiSysFuncInt; + tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$fgetc"; - tf_data.calltf = sys_fgetc_calltf; - tf_data.compiletf = 0; - tf_data.sizetf = sys_integer_sizetf; + tf_data.calltf = sys_common_fd_calltf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; + tf_data.sizetf = 0; tf_data.user_data = "$fgetc"; vpi_register_systf(&tf_data); //============================== fgets tf_data.type = vpiSysFunc; - tf_data.sysfunctype = vpiSysFuncInt; + tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$fgets"; tf_data.calltf = sys_fgets_calltf; tf_data.compiletf = sys_fgets_compiletf; @@ -868,54 +806,53 @@ void sys_fileio_register() //============================== ungetc tf_data.type = vpiSysFunc; - tf_data.sysfunctype = vpiSysFuncInt; + tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$ungetc"; tf_data.calltf = sys_ungetc_calltf; - tf_data.compiletf = sys_ungetc_compiletf; - tf_data.sizetf = sys_integer_sizetf; + tf_data.compiletf = sys_two_numeric_args_compiletf; + tf_data.sizetf = 0; tf_data.user_data = "$ungetc"; vpi_register_systf(&tf_data); //============================== ftell tf_data.type = vpiSysFunc; - tf_data.sysfunctype = vpiSysFuncInt; + tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$ftell"; - tf_data.calltf = sys_ftell_feof_calltf; - tf_data.compiletf = sys_check_fd_compiletf; - tf_data.sizetf = sys_integer_sizetf; + tf_data.calltf = sys_common_fd_calltf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; + tf_data.sizetf = 0; tf_data.user_data = "$ftell"; vpi_register_systf(&tf_data); //============================== fseek tf_data.type = vpiSysFunc; - tf_data.sysfunctype = vpiSysFuncInt; + tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$fseek"; tf_data.calltf = sys_fseek_calltf; tf_data.compiletf = sys_fseek_compiletf; - tf_data.sizetf = sys_integer_sizetf; + tf_data.sizetf = 0; tf_data.user_data = "$fseek"; vpi_register_systf(&tf_data); //============================== rewind tf_data.type = vpiSysFunc; - tf_data.sysfunctype = vpiSysFuncInt; + tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$rewind"; - tf_data.calltf = sys_rewind_calltf; - tf_data.compiletf = sys_check_fd_compiletf; - tf_data.sizetf = sys_integer_sizetf; + tf_data.calltf = sys_common_fd_calltf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; + tf_data.sizetf = 0; tf_data.user_data = "$rewind"; vpi_register_systf(&tf_data); /* $feof() is from 1364-2005. */ //============================== feof tf_data.type = vpiSysFunc; - tf_data.sysfunctype = vpiSysFuncInt; + tf_data.sysfunctype = vpiIntFunc; tf_data.tfname = "$feof"; - tf_data.calltf = sys_ftell_feof_calltf; - tf_data.compiletf = sys_check_fd_compiletf; - tf_data.sizetf = sys_integer_sizetf; + tf_data.calltf = sys_common_fd_calltf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; + tf_data.sizetf = 0; tf_data.user_data = "$feof"; vpi_register_systf(&tf_data); } - diff --git a/vpi/sys_finish.c b/vpi/sys_finish.c index 504bb5475..f55dd312e 100644 --- a/vpi/sys_finish.c +++ b/vpi/sys_finish.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -16,44 +16,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: sys_finish.c,v 1.11 2007/04/09 22:49:33 steve Exp $" -#endif -# include "vpi_config.h" - -# include "vpi_user.h" -# include - -static PLI_INT32 sys_finish_compiletf(PLI_BYTE8 *name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle arg; - - /* The argument is optional. */ - if (argv == 0) return 0; - arg = vpi_scan(argv); - - /* A string diagnostic message level makes no sense. */ - if (vpi_get(vpiType, arg) == vpiConstant && - vpi_get(vpiConstType, arg) == vpiStringConst) { - vpi_printf("Error: %s does not take a string argument.\n", name); - vpi_control(vpiFinish, 1); - return 0; - } - - /* These functions take at most one argument (diagnostic message). */ - arg = vpi_scan(argv); - if (arg != 0) { - vpi_printf("Error: %s takes at most one argument.\n", name); - vpi_control(vpiFinish, 1); - return 0; - } - - /* vpi_scan returning 0 (NULL) has already freed argv. */ - return 0; -} +#include "vpi_config.h" +#include "vpi_user.h" +#include "sys_priv.h" +#include static PLI_INT32 sys_finish_calltf(PLI_BYTE8 *name) { @@ -88,55 +55,16 @@ void sys_finish_register() tf_data.type = vpiSysTask; tf_data.tfname = "$finish"; tf_data.calltf = sys_finish_calltf; - tf_data.compiletf = sys_finish_compiletf; + tf_data.compiletf = sys_one_opt_numeric_arg_compiletf; tf_data.sizetf = 0; - tf_data.user_data = (PLI_BYTE8*)"$finish"; + tf_data.user_data = "$finish"; vpi_register_systf(&tf_data); tf_data.type = vpiSysTask; tf_data.tfname = "$stop"; tf_data.calltf = sys_finish_calltf; - tf_data.compiletf = sys_finish_compiletf; + tf_data.compiletf = sys_one_opt_numeric_arg_compiletf; tf_data.sizetf = 0; - tf_data.user_data = (PLI_BYTE8*)"$stop"; + tf_data.user_data = "$stop"; vpi_register_systf(&tf_data); } - -/* - * $Log: sys_finish.c,v $ - * Revision 1.11 2007/04/09 22:49:33 steve - * More strict use of PLI_BYTE8 type. - * - * Revision 1.10 2006/10/30 22:45:37 steve - * Updates for Cygwin portability (pr1585922) - * - * Revision 1.9 2004/01/21 01:22:53 steve - * Give the vip directory its own configure and vpi_config.h - * - * Revision 1.8 2003/02/21 03:24:03 steve - * Make the $stop system task really vpiStop. - * - * Revision 1.7 2002/08/12 01:35:04 steve - * conditional ident string using autoconfig. - * - * Revision 1.6 2001/07/25 03:10:50 steve - * Create a config.h.in file to hold all the config - * junk, and support gcc 3.0. (Stephan Boettcher) - * - * Revision 1.5 2001/01/01 19:33:44 steve - * Add $stop that does a finish. - * - * Revision 1.4 2000/02/23 02:56:56 steve - * Macintosh compilers do not support ident. - * - * Revision 1.3 1999/08/28 02:10:44 steve - * Call the right vpiFinish code. - * - * Revision 1.2 1999/08/19 02:51:03 steve - * Add vpi_sim_control - * - * Revision 1.1 1999/08/15 01:23:56 steve - * Convert vvm to implement system tasks with vpi. - * - */ - diff --git a/vpi/sys_icarus.c b/vpi/sys_icarus.c index af92be7db..d2e1921d2 100644 --- a/vpi/sys_icarus.c +++ b/vpi/sys_icarus.c @@ -16,110 +16,17 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "vpi_config.h" #include #include +#include "sys_priv.h" - -/* - * Routine to return the width in bits of a CPU word (long). - */ -static PLI_INT32 vvp_cpu_wordsize_calltf(PLI_BYTE8* ud) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - assert(callh != 0); - s_vpi_value val; - (void) ud; /* Not used! */ - - /* Calculate the result */ - val.format = vpiIntVal; - val.value.integer = 8*sizeof(long); - - /* Return the result */ - vpi_put_value(callh, &val, 0, vpiNoDelay); - - return 0; -} - -static PLI_INT32 size_32(PLI_BYTE8* ud) -{ - (void) ud; /* Not used! */ - - return 32; -} - - -/* - * Routine to finish the simulation and return a value to the - * calling environment. - */ -static PLI_INT32 finish_and_return_compiletf(PLI_BYTE8* ud) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle arg; - (void) ud; /* Not used! */ - - /* We must have at least one argument. */ - if (argv == 0) { - vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("$finish_and_return requires an argument.\n"); - vpi_control(vpiFinish, 1); - return 0; - } - - /* This must be a numeric argument. */ - arg = vpi_scan(argv); - switch(vpi_get(vpiType, arg)) { - case vpiConstant: - case vpiParameter: - /* String constants are invalid numeric values. */ - if (vpi_get(vpiConstType, arg) == vpiStringConst) { - vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("The argument to $finish_and_return must be numeric.\n"); - vpi_control(vpiFinish, 1); - return 0; - } - break; - - case vpiIntegerVar: - case vpiMemoryWord: - case vpiNet: - case vpiRealVar: - case vpiReg: - case vpiTimeVar: - break; - - default: - vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("The argument to $finish_and_return must be numeric.\n"); - vpi_control(vpiFinish, 1); - return 0; - break; - } - - /* We can only have one argument. */ - if (vpi_scan(argv) != 0) { - vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("$finish_and_return takes a single argument.\n"); - vpi_control(vpiFinish, 1); - return 0; - } - - return 0; -} - -static PLI_INT32 finish_and_return_calltf(PLI_BYTE8* ud) +static PLI_INT32 finish_and_return_calltf(PLI_BYTE8* name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; s_vpi_value val; - (void) ud; /* Not used! */ + (void) name; /* Not used! */ /* Get the return value. */ arg = vpi_scan(argv); @@ -142,20 +49,11 @@ void sys_special_register(void) { s_vpi_systf_data tf_data; - tf_data.type = vpiSysFunc; - tf_data.sysfunctype = vpiIntFunc; - tf_data.calltf = vvp_cpu_wordsize_calltf; - tf_data.compiletf = 0; - tf_data.sizetf = size_32; - tf_data.tfname = "$vvp_cpu_wordsize"; - tf_data.user_data = 0; - vpi_register_systf(&tf_data); - tf_data.type = vpiSysTask; tf_data.calltf = finish_and_return_calltf; - tf_data.compiletf = finish_and_return_compiletf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.tfname = "$finish_and_return"; - tf_data.user_data = 0; + tf_data.user_data = "$finish_and_return"; vpi_register_systf(&tf_data); } diff --git a/vpi/sys_lxt.c b/vpi/sys_lxt.c index 708aa4849..21a198f62 100644 --- a/vpi/sys_lxt.c +++ b/vpi/sys_lxt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -16,9 +16,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: sys_lxt.c,v 1.28 2007/03/14 04:05:51 steve Exp $" -#endif # include "sys_priv.h" # include "lxt_write.h" @@ -324,9 +321,9 @@ inline static int install_dumpvars_callback(void) return 0; if (dumpvars_status == 2) { - vpi_mcd_printf(1, "LXT warning:" " $dumpvars ignored, previously" - " called at simtime %" PLI_UINT64_FMT "\n", - dumpvars_time); + vpi_printf("LXT warning: $dumpvars ignored, previously" + " called at simtime %" PLI_UINT64_FMT "\n", + dumpvars_time); return 1; } @@ -431,22 +428,23 @@ static void *close_dumpfile(void) return (dump_file = NULL); } -static void open_dumpfile(void) +static void open_dumpfile(vpiHandle callh) { if (dump_path == 0) dump_path = strdup("dump.lxt"); dump_file = lt_init(dump_path); if (dump_file == 0) { - vpi_mcd_printf(1, "LXT Error: Unable to open %s for output.\n", - dump_path); + vpi_printf("LXT Error: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Unable to open %s for output.\n", dump_path); vpi_control(vpiFinish, 1); return; } else { int prec = vpi_get(vpiTimePrecision, 0); - vpi_mcd_printf(1, "LXT info: dumpfile %s opened for output.\n", - dump_path); + vpi_printf("LXT info: dumpfile %s opened for output.\n", + dump_path); assert(prec >= -15); lt_set_timescale(dump_file, prec); @@ -468,9 +466,9 @@ static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name) /* $dumpfile must be called before $dumpvars starts! */ if (dumpvars_status != 0) { - vpi_mcd_printf(1, "LXT warning: %s called after $dumpvars started" - ",\n using existing file (%s).\n", - name, dump_path); + vpi_printf("LXT warning: %s called after $dumpvars started,\n" + " using existing file (%s).\n", + name, dump_path); return 0; } @@ -480,8 +478,8 @@ static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name) path = strdup(value.value.str); if (dump_path) { - vpi_mcd_printf(1, "LXT warning: Overriding dump file %s with" - " %s\n", dump_path, path); + vpi_printf("LXT warning: Overriding dump file %s with %s\n", + dump_path, path); free(dump_path); } dump_path = path; @@ -542,16 +540,9 @@ static void scan_item(unsigned depth, vpiHandle item, int skip) switch (vpi_get(vpiType, item)) { - case vpiMemory: - /* don't know how to watch memories. */ - break; - - case vpiNamedEvent: - /* There is nothing in named events to dump. */ - break; - case vpiNet: type = "wire"; if(0){ case vpiIntegerVar: + case vpiMemoryWord: case vpiTimeVar: case vpiReg: type = "reg"; } @@ -644,18 +635,16 @@ static void scan_item(unsigned depth, vpiHandle item, int skip) vpi_get_str(vpiFullName, item); #if 0 - vpi_mcd_printf(1, - "LXT info:" - " scanning scope %s, %u levels\n", - fullname, depth); + vpi_printf("LXT info: scanning scope %s, %u levels\n", + fullname, depth); #endif nskip = 0 != vcd_names_search(&lxt_tab, fullname); if (!nskip) vcd_names_add(&lxt_tab, fullname); else - vpi_mcd_printf(1, "LXT warning: ignoring signals in" - " previously scanned scope %s\n", fullname); + vpi_printf("LXT warning: ignoring signals in " + "previously scanned scope %s\n", fullname); name = vpi_get_str(vpiName, item); @@ -674,8 +663,8 @@ static void scan_item(unsigned depth, vpiHandle item, int skip) break; default: - vpi_mcd_printf(1, "LXT warning: $dumpvars: Unsupported parameter" - " type (%d)\n", vpi_get(vpiType, item)); + vpi_printf("LXT warning: $dumpvars: Unsupported parameter " + "type (%s)\n", vpi_get_str(vpiType, item)); } } @@ -684,7 +673,6 @@ static int draw_scope(vpiHandle item) { int depth; const char *name; -// char *type; // Not needed, see below. vpiHandle scope = vpi_handle(vpiScope, item); if (!scope) @@ -693,20 +681,6 @@ static int draw_scope(vpiHandle item) depth = 1 + draw_scope(scope); name = vpi_get_str(vpiName, scope); -#if 0 /* The type information is not needed by the LXT dumper. */ - switch (vpi_get(vpiType, scope)) { - case vpiNamedBegin: type = "begin"; break; - case vpiTask: type = "task"; break; - case vpiFunction: type = "function"; break; - case vpiNamedFork: type = "fork"; break; - case vpiModule: type = "module"; break; - default: - vpi_mcd_printf(1, "LXT Error: $dumpvars: Unsupported scope " - "type (%d)\n", vpi_get(vpiType, item)); - assert(0); - } -#endif - push_scope(name); return depth; @@ -721,7 +695,7 @@ static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name) unsigned depth = 0; if (dump_file == 0) { - open_dumpfile(); + open_dumpfile(callh); if (dump_file == 0) return 0; } @@ -784,7 +758,7 @@ void sys_lxt_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpall"; tf_data.calltf = sys_dumpall_calltf; - tf_data.compiletf = sys_dumpall_compiletf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpall"; vpi_register_systf(&tf_data); @@ -792,7 +766,7 @@ void sys_lxt_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpfile"; tf_data.calltf = sys_dumpfile_calltf; - tf_data.compiletf = sys_dumpfile_compiletf; + tf_data.compiletf = sys_one_string_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpfile"; vpi_register_systf(&tf_data); @@ -800,7 +774,7 @@ void sys_lxt_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpflush"; tf_data.calltf = sys_dumpflush_calltf; - tf_data.compiletf = sys_dumpflush_compiletf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpflush"; vpi_register_systf(&tf_data); @@ -808,7 +782,7 @@ void sys_lxt_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumplimit"; tf_data.calltf = sys_dumplimit_calltf; - tf_data.compiletf = sys_dumplimit_compiletf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumplimit"; vpi_register_systf(&tf_data); @@ -816,7 +790,7 @@ void sys_lxt_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpoff"; tf_data.calltf = sys_dumpoff_calltf; - tf_data.compiletf = sys_dumpoff_compiletf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpoff"; vpi_register_systf(&tf_data); @@ -824,7 +798,7 @@ void sys_lxt_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpon"; tf_data.calltf = sys_dumpon_calltf; - tf_data.compiletf = sys_dumpon_compiletf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpon"; vpi_register_systf(&tf_data); @@ -837,4 +811,3 @@ void sys_lxt_register() tf_data.user_data = "$dumpvars"; vpi_register_systf(&tf_data); } - diff --git a/vpi/sys_lxt2.c b/vpi/sys_lxt2.c index 6564a5c54..b85486244 100644 --- a/vpi/sys_lxt2.c +++ b/vpi/sys_lxt2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2004 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -16,9 +16,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: sys_lxt2.c,v 1.10 2007/03/14 04:05:51 steve Exp $" -#endif # include "sys_priv.h" # include "lxt2_write.h" @@ -322,9 +319,9 @@ inline static int install_dumpvars_callback(void) if (dumpvars_status == 1) return 0; if (dumpvars_status == 2) { - vpi_mcd_printf(1, "LXT2 warning: $dumpvars ignored, previously" - " called at simtime %" PLI_UINT64_FMT "\n", - dumpvars_time); + vpi_printf("LXT2 warning: $dumpvars ignored, previously" + " called at simtime %" PLI_UINT64_FMT "\n", + dumpvars_time); return 1; } @@ -429,22 +426,23 @@ static void *close_dumpfile(void) return 0; } -static void open_dumpfile(void) +static void open_dumpfile(vpiHandle callh) { if (dump_path == 0) dump_path = strdup("dump.lx2"); dump_file = lxt2_wr_init(dump_path); if (dump_file == 0) { - vpi_mcd_printf(1, "LXT2 Error: Unable to open %s for output.\n", - dump_path); + vpi_printf("LXT2 Error: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Unable to open %s for output.\n", dump_path); vpi_control(vpiFinish, 1); return; } else { int prec = vpi_get(vpiTimePrecision, 0); - vpi_mcd_printf(1, "LXT2 info: dumpfile %s opened for output.\n", - dump_path); + vpi_printf("LXT2 info: dumpfile %s opened for output.\n", + dump_path); assert(prec >= -15); lxt2_wr_set_timescale(dump_file, prec); @@ -468,9 +466,9 @@ static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name) /* $dumpfile must be called before $dumpvars starts! */ if (dumpvars_status != 0) { - vpi_mcd_printf(1, "LXT2 warning: %s called after $dumpvars started" - ",\n using existing file (%s).\n", - name, dump_path); + vpi_printf("LXT2 warning: %s called after $dumpvars started,\n" + " using existing file (%s).\n", + name, dump_path); return 0; } @@ -480,8 +478,8 @@ static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name) path = strdup(value.value.str); if (dump_path) { - vpi_mcd_printf(1, "LXT2 warning: Overriding dump file %s with" - " %s.\n", dump_path, path); + vpi_printf("LXT2 warning: Overriding dump file %s with %s.\n", + dump_path, path); free(dump_path); } dump_path = path; @@ -548,16 +546,9 @@ static void scan_item(unsigned depth, vpiHandle item, int skip) switch (vpi_get(vpiType, item)) { - case vpiMemory: - /* don't know how to watch memories. */ - break; - - case vpiNamedEvent: - /* There is nothing in named events to dump. */ - break; - case vpiNet: type = "wire"; if(0){ case vpiIntegerVar: + case vpiMemoryWord: case vpiTimeVar: case vpiReg: type = "reg"; } @@ -657,18 +648,16 @@ static void scan_item(unsigned depth, vpiHandle item, int skip) vpi_get_str(vpiFullName, item); #if 0 - vpi_mcd_printf(1, - "LXT2 info:" - " scanning scope %s, %u levels\n", - fullname, depth); + vpi_printf("LXT2 info: scanning scope %s, %u levels\n", + fullname, depth); #endif nskip = 0 != vcd_names_search(&lxt_tab, fullname); if (!nskip) vcd_names_add(&lxt_tab, fullname); else - vpi_mcd_printf(1, "LXT2 warning: ignoring signals in" - " previously scanned scope %s\n", fullname); + vpi_printf("LXT2 warning: ignoring signals in " + "previously scanned scope %s\n", fullname); name = vpi_get_str(vpiName, item); @@ -687,8 +676,8 @@ static void scan_item(unsigned depth, vpiHandle item, int skip) break; default: - vpi_mcd_printf(1, "LXT2 warning: $dumpvars: Unsupported parameter" - " type (%d)\n", vpi_get(vpiType, item)); + vpi_printf("LXT2 warning: $dumpvars: Unsupported parameter " + "type (%s)\n", vpi_get_str(vpiType, item)); } } @@ -697,7 +686,6 @@ static int draw_scope(vpiHandle item) { int depth; const char *name; -// char *type; // Not needed, see below. vpiHandle scope = vpi_handle(vpiScope, item); if (!scope) return 0; @@ -705,20 +693,6 @@ static int draw_scope(vpiHandle item) depth = 1 + draw_scope(scope); name = vpi_get_str(vpiName, scope); -#if 0 /* The type information is not needed by the LXT2 dumper. */ - switch (vpi_get(vpiType, item)) { - case vpiNamedBegin: type = "begin"; break; - case vpiTask: type = "task"; break; - case vpiFunction: type = "function"; break; - case vpiNamedFork: type = "fork"; break; - case vpiModule: type = "module"; break; - default: - vpi_mcd_printf(1, "LXT2 Error: $dumpvars: Unsupported scope " - "type (%d)\n", vpi_get(vpiType, item)); - assert(0); - } -#endif - push_scope(name); return depth; @@ -733,7 +707,7 @@ static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name) unsigned depth = 0; if (dump_file == 0) { - open_dumpfile(); + open_dumpfile(callh); if (dump_file == 0) return 0; } @@ -799,7 +773,7 @@ void sys_lxt2_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpall"; tf_data.calltf = sys_dumpall_calltf; - tf_data.compiletf = sys_dumpall_compiletf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpall"; vpi_register_systf(&tf_data); @@ -807,7 +781,7 @@ void sys_lxt2_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpfile"; tf_data.calltf = sys_dumpfile_calltf; - tf_data.compiletf = sys_dumpfile_compiletf; + tf_data.compiletf = sys_one_string_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpfile"; vpi_register_systf(&tf_data); @@ -815,7 +789,7 @@ void sys_lxt2_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpflush"; tf_data.calltf = sys_dumpflush_calltf; - tf_data.compiletf = sys_dumpflush_compiletf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpflush"; vpi_register_systf(&tf_data); @@ -823,7 +797,7 @@ void sys_lxt2_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumplimit"; tf_data.calltf = sys_dumplimit_calltf; - tf_data.compiletf = sys_dumplimit_compiletf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumplimit"; vpi_register_systf(&tf_data); @@ -831,7 +805,7 @@ void sys_lxt2_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpoff"; tf_data.calltf = sys_dumpoff_calltf; - tf_data.compiletf = sys_dumpoff_compiletf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpoff"; vpi_register_systf(&tf_data); @@ -839,7 +813,7 @@ void sys_lxt2_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpon"; tf_data.calltf = sys_dumpon_calltf; - tf_data.compiletf = sys_dumpon_compiletf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpon"; vpi_register_systf(&tf_data); @@ -852,4 +826,3 @@ void sys_lxt2_register() tf_data.user_data = "$dumpvars"; vpi_register_systf(&tf_data); } - diff --git a/vpi/sys_priv.c b/vpi/sys_priv.c new file mode 100644 index 000000000..5e1dcba4b --- /dev/null +++ b/vpi/sys_priv.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2003-2008 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include "sys_priv.h" + +PLI_UINT64 timerec_to_time64(const struct t_vpi_time*time) +{ + PLI_UINT64 tmp; + + tmp = time->high; + tmp <<= 32; + tmp |= (PLI_UINT64) time->low; + + return tmp; +} + +/* + * This routine returns 1 if the argument is a constant value, + * otherwise it returns 0. + */ +unsigned is_constant_obj(vpiHandle obj) +{ + assert(obj); + unsigned rtn = 0; + + switch(vpi_get(vpiType, obj)) { + case vpiConstant: + case vpiParameter: + rtn = 1; + break; + } + + return rtn; +} + +/* + * This routine returns 1 if the argument supports has a numeric value, + * otherwise it returns 0. + */ +unsigned is_numeric_obj(vpiHandle obj) +{ + assert(obj); + unsigned rtn = 0; + + switch(vpi_get(vpiType, obj)) { + case vpiConstant: + case vpiParameter: + /* These cannot be a string constant. */ + if (vpi_get(vpiConstType, obj) != vpiStringConst) rtn = 1; + break; + + /* These can have a valid numeric value. */ + case vpiIntegerVar: + case vpiMemoryWord: + case vpiNet: + case vpiPartSelect: + case vpiRealVar: + case vpiReg: + case vpiTimeVar: + rtn = 1;; + break; + } + + return rtn; +} + + +/* + * This routine returns 1 if the argument supports a valid string value, + * otherwise it returns 0. + */ +unsigned is_string_obj(vpiHandle obj) +{ + assert(obj); + unsigned rtn = 0; + + switch(vpi_get(vpiType, obj)) { + case vpiConstant: + case vpiParameter: { + /* These must be a string or binary constant. */ + PLI_INT32 ctype = vpi_get(vpiConstType, obj); + if (ctype == vpiStringConst || ctype == vpiBinaryConst) rtn = 1; + break; + } + + /* These can have a valid string value. */ + case vpiIntegerVar: + case vpiMemoryWord: + case vpiNet: + case vpiPartSelect: + case vpiReg: + case vpiTimeVar: + rtn = 1;; + break; + } + + return rtn; +} + + +/* + * Find the enclosing module. + */ +vpiHandle sys_func_module(vpiHandle obj) +{ + assert(obj); + + while (vpi_get(vpiType, obj) != vpiModule) { + obj = vpi_handle(vpiScope, obj); + assert(obj); + } + + return obj; +} + +/* + * Standard compiletf routines. + */ + +/* For system tasks/functions that do not take an argument. */ +PLI_INT32 sys_no_arg_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + + /* Make sure there are no arguments. */ + if (argv != 0) { + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 0; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s %s does not take an argument.\n", msg, name); + vpi_printf("%*s Found %u extra argument%s.\n", + (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); + } + + return 0; +} + +/* For system tasks/functions that take a single numeric argument. */ +PLI_INT32 sys_one_numeric_arg_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + + /* Check that there is an argument and that it is numeric. */ + if (argv == 0) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a single numeric argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + if (! is_numeric_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's argument must be numeric.\n", name); + vpi_control(vpiFinish, 1); + } + + /* Make sure there are no extra arguments. */ + if (vpi_scan(argv) != 0) { + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 1; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s %s takes a single numeric argument.\n", msg, name); + vpi_printf("%*s Found %u extra argument%s.\n", + (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); + } + + return 0; +} + +/* For system tasks/functions that take a single optional numeric argument. */ +PLI_INT32 sys_one_opt_numeric_arg_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + + /* The argument is optional so just return if none are found. */ + if (argv == 0) return 0; + + if (! is_numeric_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's argument must be numeric.\n", name); + vpi_control(vpiFinish, 1); + } + + /* Make sure there are no extra arguments. */ + if (vpi_scan(argv) != 0) { + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 1; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s %s takes at most one numeric argument.\n", + msg, name); + vpi_printf("%*s Found %u extra argument%s.\n", + (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); + } + + return 0; +} + +/* For system tasks/functions that take two numeric arguments. */ +PLI_INT32 sys_two_numeric_args_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are two argument and that they are numeric. */ + if (argv == 0) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires two numeric arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + if (! is_numeric_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's first argument must be numeric.\n", name); + vpi_control(vpiFinish, 1); + } + + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a second (numeric) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + if (! is_numeric_obj(arg)) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's second argument must be numeric.\n", name); + vpi_control(vpiFinish, 1); + } + + /* Make sure there are no extra arguments. */ + if (vpi_scan(argv) != 0) { + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 1; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s %s takes two numeric arguments.\n", msg, name); + vpi_printf("%*s Found %u extra argument%s.\n", + (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); + } + + return 0; +} + +/* For system tasks/functions that take a single string argument. */ +PLI_INT32 sys_one_string_arg_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + + /* Check that there is an argument and that it is a string. */ + if (argv == 0) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a single string argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + if (! is_string_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's argument must be a string.\n", name); + vpi_control(vpiFinish, 1); + } + + /* Make sure there are no extra arguments. */ + if (vpi_scan(argv) != 0) { + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 1; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s %s takes a single string argument.\n", msg, name); + vpi_printf("%*s Found %u extra argument%s.\n", + (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); + } + + return 0; +} diff --git a/vpi/sys_priv.h b/vpi/sys_priv.h index 41d3bde52..82b7f2a8d 100644 --- a/vpi/sys_priv.h +++ b/vpi/sys_priv.h @@ -1,7 +1,7 @@ #ifndef __vpi_sys_priv_H #define __vpi_sys_priv_H /* - * Copyright (c) 2002 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -18,12 +18,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: sys_priv.h,v 1.8 2007/03/14 04:05:51 steve Exp $" -#endif -# include "vpi_config.h" -# include "vpi_user.h" +#include "vpi_config.h" +#include "vpi_user.h" /* * Context structure for PRNG in mt19937int.c @@ -36,9 +33,6 @@ struct context_s { extern void sgenrand(struct context_s *context, unsigned long seed); extern unsigned long genrand(struct context_s *context); - -extern int is_constant(vpiHandle obj); - extern PLI_UINT64 timerec_to_time64(const struct t_vpi_time*time); struct timeformat_info_s { @@ -50,4 +44,19 @@ struct timeformat_info_s { extern struct timeformat_info_s timeformat_info; +extern unsigned is_constant_obj(vpiHandle obj); +extern unsigned is_numeric_obj(vpiHandle obj); +extern unsigned is_string_obj(vpiHandle obj); + +extern vpiHandle sys_func_module(vpiHandle obj); + +/* + * The standard compiletf routines. + */ +extern PLI_INT32 sys_no_arg_compiletf(PLI_BYTE8 *name); +extern PLI_INT32 sys_one_numeric_arg_compiletf(PLI_BYTE8 *name); +extern PLI_INT32 sys_one_opt_numeric_arg_compiletf(PLI_BYTE8 *name); +extern PLI_INT32 sys_two_numeric_args_compiletf(PLI_BYTE8 *name); +extern PLI_INT32 sys_one_string_arg_compiletf(PLI_BYTE8 *name); + #endif diff --git a/vpi/sys_random.c b/vpi/sys_random.c index d0a3cb251..41c91d110 100644 --- a/vpi/sys_random.c +++ b/vpi/sys_random.c @@ -397,7 +397,7 @@ static PLI_INT32 sys_rand_two_args_compiletf(PLI_BYTE8 *name) break; default: vpi_printf("ERROR: %s's seed must be an integer/time" - " varible or a register.\n", name); + " variable or a register.\n", name); vpi_control(vpiFinish, 1); return 0; } @@ -449,7 +449,7 @@ static PLI_INT32 sys_rand_three_args_compiletf(PLI_BYTE8 *name) break; default: vpi_printf("ERROR: %s's seed must be an integer/time" - " varible or a register.\n", name); + " variable or a register.\n", name); vpi_control(vpiFinish, 1); return 0; } @@ -484,7 +484,7 @@ static PLI_INT32 sys_random_compiletf(PLI_BYTE8 *name) break; default: vpi_printf("ERROR: %s's seed must be an integer/time" - " varible or a register.\n", name); + " variable or a register.\n", name); vpi_control(vpiFinish, 1); return 0; } diff --git a/vpi/sys_readmem.c b/vpi/sys_readmem.c index a1a252f38..402280c28 100644 --- a/vpi/sys_readmem.c +++ b/vpi/sys_readmem.c @@ -445,7 +445,7 @@ static PLI_INT32 sys_writemem_calltf(PLI_BYTE8*name) /* Open the data file. */ file = fopen(path, "w"); if (file == 0) { - vpi_printf("%s: Unable to open %s for writeing.\n", name, path); + vpi_printf("%s: Unable to open %s for writing.\n", name, path); free(path); return 0; } diff --git a/vpi/sys_scanf.c b/vpi/sys_scanf.c index 14a84adf9..c2b9a4898 100644 --- a/vpi/sys_scanf.c +++ b/vpi/sys_scanf.c @@ -250,7 +250,7 @@ static int scan_format(vpiHandle sys, struct byte_source*src, vpiHandle argv) if (isspace(*fmtp)) { /* White space matches a string of white space in the input. The number of spaces is not - relevent, and the match may be 0 or more + relevant, and the match may be 0 or more spaces. */ while (*fmtp && isspace(*fmtp)) fmtp += 1; diff --git a/vpi/sys_table.c b/vpi/sys_table.c index 9658a469e..4560744e7 100644 --- a/vpi/sys_table.c +++ b/vpi/sys_table.c @@ -38,6 +38,7 @@ extern void sys_time_register(); extern void sys_vcd_register(); extern void sys_vcdoff_register(); extern void sys_special_register(); +extern void vams_simparam_register(); #ifdef HAVE_LIBZ #ifdef HAVE_LIBBZ2 @@ -180,5 +181,6 @@ void (*vlog_startup_routines[])() = { sys_lxt_or_vcd_register, sys_sdf_register, sys_special_register, + vams_simparam_register, 0 }; diff --git a/vpi/sys_time.c b/vpi/sys_time.c index 2a1785805..5d040f966 100644 --- a/vpi/sys_time.c +++ b/vpi/sys_time.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -16,36 +16,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: sys_time.c,v 1.12 2007/03/14 04:05:51 steve Exp $" -#endif -# include "vpi_config.h" +#include "vpi_config.h" -# include "vpi_user.h" -# include -# include -# include - -static vpiHandle module_of_function(vpiHandle obj) -{ - while (vpi_get(vpiType, obj) != vpiModule) { - obj = vpi_handle(vpiScope, obj); - assert(obj); - } - - return obj; -} - -static PLI_INT32 sys_time_sizetf(PLI_BYTE8*x) -{ - return 64; -} - -static PLI_INT32 sys_stime_sizetf(PLI_BYTE8*x) -{ - return 32; -} +#include "vpi_user.h" +#include +#include +#include +#include static PLI_INT32 sys_time_calltf(PLI_BYTE8*name) { @@ -60,7 +38,7 @@ static PLI_INT32 sys_time_calltf(PLI_BYTE8*name) call_handle = vpi_handle(vpiSysTfCall, 0); assert(call_handle); - mod = module_of_function(call_handle); + mod = sys_func_module(call_handle); now.type = vpiSimTime; vpi_get_time(0, &now); @@ -113,7 +91,7 @@ static PLI_INT32 sys_realtime_calltf(PLI_BYTE8*name) call_handle = vpi_handle(vpiSysTfCall, 0); assert(call_handle); - mod = module_of_function(call_handle); + mod = sys_func_module(call_handle); now.type = vpiSimTime; vpi_get_time(0, &now); @@ -134,82 +112,39 @@ void sys_time_register() { s_vpi_systf_data tf_data; - tf_data.type = vpiSysFunc; - tf_data.tfname = "$time"; - tf_data.calltf = sys_time_calltf; - tf_data.compiletf = 0; - tf_data.sizetf = sys_time_sizetf; - tf_data.user_data = "$time"; + tf_data.type = vpiSysFunc; + tf_data.tfname = "$time"; + tf_data.sysfunctype = vpiTimeFunc; + tf_data.calltf = sys_time_calltf; + tf_data.compiletf = sys_no_arg_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$time"; vpi_register_systf(&tf_data); - tf_data.type = vpiSysFunc; - tf_data.tfname = "$realtime"; - tf_data.calltf = sys_realtime_calltf; - tf_data.compiletf = 0; - tf_data.sizetf = 0; - tf_data.user_data = "$realtime"; + tf_data.type = vpiSysFunc; + tf_data.tfname = "$realtime"; + tf_data.sysfunctype = vpiRealFunc; + tf_data.calltf = sys_realtime_calltf; + tf_data.compiletf = sys_no_arg_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$realtime"; vpi_register_systf(&tf_data); - tf_data.type = vpiSysFunc; - tf_data.tfname = "$stime"; - tf_data.calltf = sys_time_calltf; - tf_data.compiletf = 0; - tf_data.sizetf = sys_stime_sizetf; - tf_data.user_data = "$stime"; + tf_data.type = vpiSysFunc; + tf_data.tfname = "$stime"; + tf_data.sysfunctype = vpiIntFunc; + tf_data.calltf = sys_time_calltf; + tf_data.compiletf = sys_no_arg_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$stime"; vpi_register_systf(&tf_data); - tf_data.type = vpiSysFunc; - tf_data.tfname = "$simtime"; - tf_data.calltf = sys_time_calltf; - tf_data.compiletf = 0; - tf_data.sizetf = sys_time_sizetf; - tf_data.user_data = "$simtime"; + tf_data.type = vpiSysFunc; + tf_data.tfname = "$simtime"; + tf_data.sysfunctype = vpiTimeFunc; + tf_data.calltf = sys_time_calltf; + tf_data.compiletf = sys_no_arg_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$simtime"; vpi_register_systf(&tf_data); } - -/* - * $Log: sys_time.c,v $ - * Revision 1.12 2007/03/14 04:05:51 steve - * VPI tasks take PLI_BYTE* by the standard. - * - * Revision 1.11 2006/10/30 22:45:37 steve - * Updates for Cygwin portability (pr1585922) - * - * Revision 1.10 2004/01/21 01:22:53 steve - * Give the vip directory its own configure and vpi_config.h - * - * Revision 1.9 2003/06/18 00:54:28 steve - * Account for all 64 bits in results of $time. - * - * Revision 1.8 2003/02/07 02:44:25 steve - * Properly round inter time values from $time. - * - * Revision 1.7 2003/01/28 04:41:55 steve - * Use more precise pow function to scale time by units. - * - * Revision 1.6 2003/01/27 00:14:37 steve - * Support in various contexts the $realtime - * system task. - * - * Revision 1.5 2002/12/21 00:55:58 steve - * The $time system task returns the integer time - * scaled to the local units. Change the internal - * implementation of vpiSystemTime the $time functions - * to properly account for this. Also add $simtime - * to get the simulation time. - * - * Revision 1.4 2002/08/12 01:35:05 steve - * conditional ident string using autoconfig. - * - * Revision 1.3 2002/01/11 05:20:59 steve - * Add the stime system function. - * - * Revision 1.2 2001/07/25 03:10:50 steve - * Create a config.h.in file to hold all the config - * junk, and support gcc 3.0. (Stephan Boettcher) - * - * Revision 1.1 2000/11/01 03:19:36 steve - * Add the general $time system function. - * - */ - diff --git a/vpi/sys_vcd.c b/vpi/sys_vcd.c index ffb86f06b..5d5e9d71b 100644 --- a/vpi/sys_vcd.c +++ b/vpi/sys_vcd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2003 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -16,9 +16,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: sys_vcd.c,v 1.58 2007/03/14 04:05:51 steve Exp $" -#endif # include "sys_priv.h" @@ -269,9 +266,9 @@ inline static int install_dumpvars_callback(void) if (dumpvars_status == 1) return 0; if (dumpvars_status == 2) { - vpi_mcd_printf(1, "VCD warning: $dumpvars ignored, previously" - " called at simtime %" PLI_UINT64_FMT "\n", - dumpvars_time); + vpi_printf("VCD warning: $dumpvars ignored, previously" + " called at simtime %" PLI_UINT64_FMT "\n", + dumpvars_time); return 1; } @@ -374,15 +371,16 @@ static PLI_INT32 sys_dumpall_calltf(PLI_BYTE8*name) return 0; } -static void open_dumpfile(void) +static void open_dumpfile(vpiHandle callh) { if (dump_path == 0) dump_path = strdup("dump.vcd"); dump_file = fopen(dump_path, "w"); if (dump_file == 0) { - vpi_mcd_printf(1, "VCD Error: Unable to open %s for output.\n", - dump_path); + vpi_printf("VCD Error: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Unable to open %s for output.\n", dump_path); vpi_control(vpiFinish, 1); return; } else { @@ -391,8 +389,7 @@ static void open_dumpfile(void) unsigned udx = 0; time_t walltime; - vpi_mcd_printf(1, "VCD info: dumpfile %s opened for output.\n", - dump_path); + vpi_printf("VCD info: dumpfile %s opened for output.\n", dump_path); time(&walltime); @@ -428,9 +425,9 @@ static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name) /* $dumpfile must be called before $dumpvars starts! */ if (dumpvars_status != 0) { - vpi_mcd_printf(1, "VCD warning: %s called after $dumpvars started" - ",\n using existing file (%s).\n", - name, dump_path); + vpi_printf("VCD warning: %s called after $dumpvars started,\n" + " using existing file (%s).\n", + name, dump_path); return 0; } @@ -440,8 +437,8 @@ static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name) path = strdup(value.value.str); if (dump_path) { - vpi_mcd_printf(1, "VCD warning: Overriding dump file %s with" - " %s\n", dump_path, path); + vpi_printf("VCD warning: Overriding dump file %s with %s\n", + dump_path, path); free(dump_path); } dump_path = path; @@ -502,16 +499,9 @@ static void scan_item(unsigned depth, vpiHandle item, int skip) switch (vpi_get(vpiType, item)) { - case vpiMemory: - /* don't know how to watch memories. */ - break; - - case vpiNamedEvent: - /* There is nothing in named events to dump. */ - break; - case vpiNet: type = "wire"; if(0){ case vpiIntegerVar: + case vpiMemoryWord: case vpiTimeVar: case vpiReg: type = "reg"; } @@ -635,18 +625,16 @@ static void scan_item(unsigned depth, vpiHandle item, int skip) vpi_get_str(vpiFullName, item); #if 0 - vpi_mcd_printf(1, - "VCD info:" - " scanning scope %s, %u levels\n", - fullname, depth); + vpi_printf("VCD info: scanning scope %s, %u levels\n", + fullname, depth); #endif nskip = 0 != vcd_names_search(&vcd_tab, fullname); if (!nskip) vcd_names_add(&vcd_tab, fullname); else - vpi_mcd_printf(1, "VCD warning: ignoring signals in" - " previously scanned scope %s\n", fullname); + vpi_printf("VCD warning: ignoring signals in " + "previously scanned scope %s\n", fullname); name = vpi_get_str(vpiName, item); @@ -665,12 +653,12 @@ static void scan_item(unsigned depth, vpiHandle item, int skip) break; default: - vpi_mcd_printf(1, "VCD warning: $dumpvars: Unsupported parameter " - "type (%d)\n", vpi_get(vpiType, item)); + vpi_printf("VCD warning: $dumpvars: Unsupported parameter " + "type (%s)\n", vpi_get_str(vpiType, item)); } } -static int draw_scope(vpiHandle item) +static int draw_scope(vpiHandle item, vpiHandle callh) { int depth; const char *name; @@ -679,7 +667,7 @@ static int draw_scope(vpiHandle item) vpiHandle scope = vpi_handle(vpiScope, item); if (!scope) return 0; - depth = 1 + draw_scope(scope); + depth = 1 + draw_scope(scope, callh); name = vpi_get_str(vpiName, scope); switch (vpi_get(vpiType, scope)) { @@ -689,8 +677,9 @@ static int draw_scope(vpiHandle item) case vpiNamedFork: type = "fork"; break; case vpiModule: type = "module"; break; default: - vpi_mcd_printf(1, "VCD Error: $dumpvars: Unsupported scope " - "type (%d)\n", vpi_get(vpiType, item)); + vpi_printf("VCD Error: %s line %d: $dumpvars: Unsupported scope " + "type (%d)\n", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh), vpi_get(vpiType, item)); assert(0); } @@ -708,7 +697,7 @@ static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name) unsigned depth = 0; if (dump_file == 0) { - open_dumpfile(); + open_dumpfile(callh); if (dump_file == 0) return 0; } @@ -739,23 +728,23 @@ static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name) * been included. */ switch (vpi_get(vpiType, item)) { case vpiIntegerVar: - /* What about MemoryWord? */ + case vpiMemoryWord: case vpiNet: case vpiRealVar: case vpiReg: case vpiTimeVar: scname = vpi_get_str(vpiFullName, vpi_handle(vpiScope, item)); if (vcd_names_search(&vcd_tab, scname)) { - vpi_mcd_printf(1, "VCD warning: skipping signal %s, " - "it was previously included.\n", - vpi_get_str(vpiFullName, item)); + vpi_printf("VCD warning: skipping signal %s, " + "it was previously included.\n", + vpi_get_str(vpiFullName, item)); continue; } else { add_var = 1; } } - int dep = draw_scope(item); + int dep = draw_scope(item, callh); scan_item(depth, item, 0); @@ -782,7 +771,7 @@ void sys_vcd_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpall"; tf_data.calltf = sys_dumpall_calltf; - tf_data.compiletf = sys_dumpall_compiletf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpall"; vpi_register_systf(&tf_data); @@ -790,7 +779,7 @@ void sys_vcd_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpfile"; tf_data.calltf = sys_dumpfile_calltf; - tf_data.compiletf = sys_dumpfile_compiletf; + tf_data.compiletf = sys_one_string_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpfile"; vpi_register_systf(&tf_data); @@ -798,7 +787,7 @@ void sys_vcd_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpflush"; tf_data.calltf = sys_dumpflush_calltf; - tf_data.compiletf = sys_dumpflush_compiletf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpflush"; vpi_register_systf(&tf_data); @@ -806,7 +795,7 @@ void sys_vcd_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumplimit"; tf_data.calltf = sys_dumplimit_calltf; - tf_data.compiletf = sys_dumplimit_compiletf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumplimit"; vpi_register_systf(&tf_data); @@ -814,7 +803,7 @@ void sys_vcd_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpoff"; tf_data.calltf = sys_dumpoff_calltf; - tf_data.compiletf = sys_dumpoff_compiletf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpoff"; vpi_register_systf(&tf_data); @@ -822,7 +811,7 @@ void sys_vcd_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpon"; tf_data.calltf = sys_dumpon_calltf; - tf_data.compiletf = sys_dumpon_compiletf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpon"; vpi_register_systf(&tf_data); @@ -835,4 +824,3 @@ void sys_vcd_register() tf_data.user_data = "$dumpvars"; vpi_register_systf(&tf_data); } - diff --git a/vpi/sys_vcdoff.c b/vpi/sys_vcdoff.c index 181adb51b..49e8d5c45 100644 --- a/vpi/sys_vcdoff.c +++ b/vpi/sys_vcdoff.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -16,9 +16,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: sys_vcdoff.c,v 1.5 2007/03/14 04:05:52 steve Exp $" -#endif # include "sys_priv.h" @@ -40,7 +37,7 @@ static int dump_flag = 0; -static PLI_INT32 sys_dump_calltf(PLI_BYTE8*name) +static PLI_INT32 sys_dummy_calltf(PLI_BYTE8*name) { return 0; } @@ -48,7 +45,7 @@ static PLI_INT32 sys_dump_calltf(PLI_BYTE8*name) static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name) { if (dump_flag == 0) { - vpi_mcd_printf(1, "VCD info: dumping is suppressed.\n"); + vpi_printf("VCD info: dumping is suppressed.\n"); dump_flag = 1; } @@ -63,48 +60,48 @@ void sys_vcdoff_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumpall"; - tf_data.calltf = sys_dump_calltf; - tf_data.compiletf = sys_dumpall_compiletf; + tf_data.calltf = sys_dummy_calltf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpall"; vpi_register_systf(&tf_data); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpfile"; - tf_data.calltf = sys_dump_calltf; - tf_data.compiletf = sys_dumpfile_compiletf; + tf_data.calltf = sys_dummy_calltf; + tf_data.compiletf = sys_one_string_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpfile"; vpi_register_systf(&tf_data); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpflush"; - tf_data.calltf = sys_dump_calltf; - tf_data.compiletf = sys_dumpflush_compiletf; + tf_data.calltf = sys_dummy_calltf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpflush"; vpi_register_systf(&tf_data); tf_data.type = vpiSysTask; tf_data.tfname = "$dumplimit"; - tf_data.calltf = sys_dump_calltf; - tf_data.compiletf = sys_dumplimit_compiletf; + tf_data.calltf = sys_dummy_calltf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumplimit"; vpi_register_systf(&tf_data); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpoff"; - tf_data.calltf = sys_dump_calltf; - tf_data.compiletf = sys_dumpoff_compiletf; + tf_data.calltf = sys_dummy_calltf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpoff"; vpi_register_systf(&tf_data); tf_data.type = vpiSysTask; tf_data.tfname = "$dumpon"; - tf_data.calltf = sys_dump_calltf; - tf_data.compiletf = sys_dumpon_compiletf; + tf_data.calltf = sys_dummy_calltf; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumpon"; vpi_register_systf(&tf_data); @@ -117,4 +114,3 @@ void sys_vcdoff_register() tf_data.user_data = "$dumpvars"; vpi_register_systf(&tf_data); } - diff --git a/vpi/system.sft b/vpi/system.sft index 555f7cc37..d6ff0a311 100644 --- a/vpi/system.sft +++ b/vpi/system.sft @@ -14,4 +14,6 @@ $dist_poisson vpiSysFuncInt $dist_chi_square vpiSysFuncInt $dist_t vpiSysFuncInt $dist_erlang vpiSysFuncInt -$vvp_cpu_wordsize vpiSysFuncInt + +$simparam vpiSysFuncReal +$simparam$str vpiSysFuncSized 1024 unsigned diff --git a/vpi/va_math.c b/vpi/va_math.c index a5cb115d3..a844e34ab 100644 --- a/vpi/va_math.c +++ b/vpi/va_math.c @@ -68,7 +68,7 @@ typedef struct s_single_data { static t_single_data va_single_data[]= { {"$sqrt", sqrt}, {"$ln", log}, - {"$log", log10}, /* FIXME: The $log function is replaced by the + {"$log", log10}, /* NOTE: The $log function is replaced by the $log10 function to eliminate confusion with the natural log. In C, "log" is ln and log10 is log-base-10. The $log is being left in for @@ -141,8 +141,8 @@ typedef struct { * Standard error message routine. The format string must take one * string argument (the name of the function). */ -static void va_error_message(vpiHandle callh, const char* format, - const char* name) { +static void va_error_message(vpiHandle callh, const char *format, + const char *name) { vpi_printf("%s:%d: error: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf(format, name); @@ -153,8 +153,8 @@ static void va_error_message(vpiHandle callh, const char* format, /* * Process an argument. */ -vpiHandle va_process_argument(vpiHandle callh, const char* name, - vpiHandle arg, const char* post) { +vpiHandle va_process_argument(vpiHandle callh, const char *name, + vpiHandle arg, const char *post) { PLI_INT32 type; if (arg == NULL) return 0; @@ -178,7 +178,7 @@ vpiHandle va_process_argument(vpiHandle callh, const char* name, /* * Routine to check all the single argument math functions. */ -static PLI_INT32 va_single_argument_compiletf(PLI_BYTE8* ud) +static PLI_INT32 va_single_argument_compiletf(PLI_BYTE8 *ud) { assert(ud != 0); vpiHandle callh = vpi_handle(vpiSysTfCall, 0); @@ -225,7 +225,7 @@ static PLI_INT32 va_single_argument_compiletf(PLI_BYTE8* ud) /* * Routine to implement the single argument math functions. */ -static PLI_INT32 va_single_argument_calltf(PLI_BYTE8* ud) +static PLI_INT32 va_single_argument_calltf(PLI_BYTE8 *ud) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); s_vpi_value val; @@ -249,7 +249,7 @@ static PLI_INT32 va_single_argument_calltf(PLI_BYTE8* ud) /* * Routine to check all the double argument math functions. */ -static PLI_INT32 va_double_argument_compiletf(PLI_BYTE8* ud) +static PLI_INT32 va_double_argument_compiletf(PLI_BYTE8 *ud) { assert(ud != 0); vpiHandle callh = vpi_handle(vpiSysTfCall, 0); diff --git a/vpi/vams_simparam.c b/vpi/vams_simparam.c new file mode 100644 index 000000000..90a73ced5 --- /dev/null +++ b/vpi/vams_simparam.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2008 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 +#ifdef HAVE_MALLOC_H +# include +#endif +#include +#include +#include +#include +#include +#include "sys_priv.h" + +/* Once we have real string objects replace this with a dynamic string. */ +#define MAX_STRING_RESULT 1024 + + +/* + * Check that the routines are called with the correct arguments. + */ +static PLI_INT32 simparam_compiletf(PLI_BYTE8 *name_ext) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + assert(callh != 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* We must have at least one argument. */ + if (argv == 0) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("$simparam%s requires a string argument.\n", name_ext); + vpi_control(vpiFinish, 1); + return 0; + } + + /* The first argument must be a string. */ + arg = vpi_scan(argv); + if (! is_string_obj(arg)) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("The first argument to $simparam%s must be a string.\n", + name_ext); + vpi_control(vpiFinish, 1); + } + + /* The second argument (default value) is optional. */ + arg = vpi_scan(argv); + if (arg == 0) return 0; + + /* For the string version the default must also be a string. */ + if (strcmp(name_ext, "$str") == 0) { + if (! is_string_obj(arg)) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("When provided, the second argument to $simparam%s" + "must be a string.\n", name_ext); + vpi_control(vpiFinish, 1); + } + /* For the rest the default must be numeric. */ + } else { + if (! is_numeric_obj(arg)) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("When provided, the second argument to $simparam%s" + "must be numeric.\n", name_ext); + vpi_control(vpiFinish, 1); + } + } + + /* We can have a maximum of two arguments. */ + if (vpi_scan(argv) != 0) { + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 1; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s $simparam%s takes at most two arguments.\n", + msg, name_ext); + vpi_printf("%*s Found %u extra argument%s.\n", + (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); + } + + return 0; +} + +static PLI_INT32 simparam_calltf(PLI_BYTE8 *name_ext) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + s_vpi_value val; + char *param; + unsigned have_def_val = 0; + double retval, defval = 0.0; + + /* Get the parameter we are looking for. */ + arg = vpi_scan(argv); + val.format = vpiStringVal; + vpi_get_value(arg, &val); + param = strdup(val.value.str); + + /* See if there is a default value. */ + arg = vpi_scan(argv); + if (arg != 0) { + vpi_free_object(argv); + have_def_val = 1; + val.format = vpiRealVal; + vpi_get_value(arg, &val); + defval = val.value.real; + } + + /* Now check the various things we can return. */ + if (strcmp(param, "gdev") == 0) { + retval = 0.0; /* Nothing for now. */ + } else if (strcmp(param, "gmin") == 0) { + retval = 0.0; /* Nothing for now. */ + } else if (strcmp(param, "imax") == 0) { + retval = 0.0; /* Nothing for now. */ + } else if (strcmp(param, "imelt") == 0) { + retval = 0.0; /* Nothing for now. */ + } else if (strcmp(param, "iteration") == 0) { + retval = 0.0; /* Nothing for now. */ + } else if (strcmp(param, "scale") == 0) { + retval = 0.0; /* Nothing for now. */ + } else if (strcmp(param, "shrink") == 0) { + retval = 0.0; /* Nothing for now. */ + } else if (strcmp(param, "simulatorSubversion") == 0) { + retval = 0.0; + } else if (strcmp(param, "simulatorVersion") == 0) { + retval = 0.9; + } else if (strcmp(param, "sourceScaleFactor") == 0) { + retval = 0.0; /* Nothing for now. */ + } else if (strcmp(param, "tnom") == 0) { + retval = 0.0; /* Nothing for now. */ + } else if (strcmp(param, "timeUnit") == 0) { + retval = pow(10, vpi_get(vpiTimeUnit, sys_func_module(callh))); + } else if (strcmp(param, "timePrecision") == 0) { + retval = pow(10, vpi_get(vpiTimePrecision, sys_func_module(callh))); + } else if (strcmp(param, "CPUWordSize") == 0) { + retval = 8.0*sizeof(long); + } else { + if (! have_def_val) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("$simparam%s unknown parameter name \"%s\".\n", + name_ext, param); + } + retval = defval; + } + + free(param); + + /* Return the value to the system. */ + val.format = vpiRealVal; + val.value.real = retval; + vpi_put_value(callh, &val, 0, vpiNoDelay); + + return 0; +} + +static PLI_INT32 simparam_str_calltf(PLI_BYTE8 *name_ext) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + s_vpi_value val; + char *param; + char *retval, *defval = NULL; + + /* Get the parameter we are looking for. */ + arg = vpi_scan(argv); + val.format = vpiStringVal; + vpi_get_value(arg, &val); + param = strdup(val.value.str); + + /* See if there is a default value. */ + arg = vpi_scan(argv); + if (arg != 0) { + vpi_free_object(argv); + val.format = vpiStringVal; + vpi_get_value(arg, &val); + defval = strdup(val.value.str); + } + + /* Now check the various things we can return. */ + /* For now we limit the result to 1024 characters. */ + if (strcmp(param, "analysis_name") == 0) { + retval = strdup("N/A"); /* Nothing for now. */ + } else if (strcmp(param, "analysis_type") == 0) { + retval = strdup("N/A"); /* Nothing for now. */ + } else if (strcmp(param, "cwd") == 0) { + char path [MAX_STRING_RESULT]; + char *ptr = getcwd(path, MAX_STRING_RESULT); + if (ptr == NULL) { + ptr = strcpy(path, ""); + } + retval = strdup(path); + } else if (strcmp(param, "module") == 0) { + retval = strdup(vpi_get_str(vpiDefName, sys_func_module(callh))); + } else if (strcmp(param, "instance") == 0) { + retval = strdup(vpi_get_str(vpiFullName, sys_func_module(callh))); + } else if (strcmp(param, "path") == 0) { + retval = strdup(vpi_get_str(vpiFullName, + vpi_handle(vpiScope,callh))); + } else { + if (defval == 0) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("$simparam%s unknown parameter name \"%s\".\n", + name_ext, param); + defval = strdup(""); + } + retval = defval; + } + + free(param); + + /* Return the value to the system. */ + val.format = vpiStringVal; + val.value.str = retval; + vpi_put_value(callh, &val, 0, vpiNoDelay); + free(retval); + + return 0; +} + +static PLI_INT32 simparam_str_sizetf(PLI_BYTE8 *name_ext) +{ + (void) name_ext; /* Not used! */ + + return MAX_STRING_RESULT; // 128 characters max! +} + +/* + * Register the function with Verilog. + */ +void vams_simparam_register(void) +{ + s_vpi_systf_data tf_data; + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiRealFunc; + tf_data.calltf = simparam_calltf; + tf_data.compiletf = simparam_compiletf; + tf_data.sizetf = 0; + tf_data.tfname = "$simparam"; + tf_data.user_data = ""; + vpi_register_systf(&tf_data); + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiSizedFunc; /* What should this be? */ + tf_data.calltf = simparam_str_calltf; + tf_data.compiletf = simparam_compiletf; + tf_data.sizetf = simparam_str_sizetf; /* Only 128 characters! */ + tf_data.tfname = "$simparam$str"; + tf_data.user_data = "$str"; + vpi_register_systf(&tf_data); +} diff --git a/vpi/vcd_priv.c b/vpi/vcd_priv.c index 2eef7903f..e1c370c5d 100644 --- a/vpi/vcd_priv.c +++ b/vpi/vcd_priv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -16,21 +16,19 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: vcd_priv.c,v 1.6 2004/10/04 01:10:58 steve Exp $" -#endif -# include "vpi_config.h" -# include "vcd_priv.h" -# include -# include -# include -# include +#include "vpi_config.h" +#include "vcd_priv.h" +#include +#include +#include +#include #ifdef HAVE_MALLOC_H # include #endif -# include -# include "stringheap.h" +#include +#include "stringheap.h" +#include int is_escaped_id(const char *name) { @@ -133,7 +131,7 @@ void vcd_names_sort(struct vcd_names_list_s*tab) The new signal will be listed as a $var, but no callback will be installed. This saves considerable CPU time and leads - to smalle VCD files. + to smaller VCD files. The _vpiNexusId is a private (int) property of IVL simulators. */ @@ -188,184 +186,12 @@ void set_nexus_ident(int nex, const char *id) vcd_ids[ihash(nex)] = bucket; } -/* This is used by the compiletf routines to check if an argument - * is numeric. */ -static void check_numeric_arg(vpiHandle arg, char *msg, PLI_BYTE8 *name) -{ - assert(arg); - - switch (vpi_get(vpiType, arg)) { - case vpiConstant: - case vpiParameter: - /* String constants are invalid numeric values. */ - if (vpi_get(vpiConstType, arg) == vpiStringConst) { - vpi_mcd_printf(1, msg, name); - vpi_control(vpiFinish, 1); - } - break; - - /* These have valid numeric values. */ - case vpiIntegerVar: - case vpiMemoryWord: - case vpiNet: - case vpiRealVar: - case vpiReg: - case vpiTimeVar: - break; - - default: - /* Anything else is not a numeric value. */ - vpi_mcd_printf(1, msg, name); - vpi_control(vpiFinish, 1); - break; - } -} - -/* This is used by the compiletf routines to check if an argument - * is a string value. */ -static void check_string_arg(vpiHandle arg, char *msg, PLI_BYTE8 *name) -{ - assert(arg); - PLI_INT32 ctype = 0; - - switch (vpi_get(vpiType, arg)) { - case vpiConstant: - case vpiParameter: - /* These must be a string or binary constant. */ - ctype = vpi_get(vpiConstType, arg); - if (ctype != vpiStringConst && ctype != vpiBinaryConst) { - vpi_mcd_printf(1, msg, name); - vpi_control(vpiFinish, 1); - } - break; - - /* These have valid string values. */ - case vpiIntegerVar: - case vpiMemoryWord: - case vpiNet: - case vpiReg: - case vpiTimeVar: - break; - - default: - /* Anything else is not a string. */ - vpi_mcd_printf(1, msg, name); - vpi_control(vpiFinish, 1); - break; - } -} - /* * Since the compiletf routines are all the same they are located here, - * so we only need a single copy. + * so we only need a single copy. Some are generic enough they can use + * the ones in sys_priv.c (no arg, one numeric argument, etc.). */ -/* $dumpall does not take an argument. */ -PLI_INT32 sys_dumpall_compiletf(PLI_BYTE8 *name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - - if (argv != 0) { - vpi_mcd_printf(1, "ERROR: %s does not take an argument.\n", name); - vpi_control(vpiFinish, 1); - } - - return 0; -} - -/* $dumpfile takes a single string argument. */ -PLI_INT32 sys_dumpfile_compiletf(PLI_BYTE8 *name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - - /* Check that there is an argument and that it is a string. */ - if (argv == 0) { - vpi_mcd_printf(1, "ERROR: %s requires an argument.\n", name); - vpi_control(vpiFinish, 1); - return 0; - } - check_string_arg(vpi_scan(argv), "ERROR: %s's argument must be a" - " string.\n", name); - - /* Check that there is only a single argument. */ - if (vpi_scan(argv) != 0) { - vpi_mcd_printf(1, "ERROR: %s takes a single argument.\n", name); - vpi_control(vpiFinish, 1); - return 0; - } - - return 0; -} - -/* $dumpflush does not take an argument. */ -PLI_INT32 sys_dumpflush_compiletf(PLI_BYTE8 *name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - - if (argv != 0) { - vpi_mcd_printf(1, "ERROR: %s does not take an argument.\n", name); - vpi_control(vpiFinish, 1); - } - - return 0; -} - -/* $dumplimit takes a single numeric argument. */ -PLI_INT32 sys_dumplimit_compiletf(PLI_BYTE8 *name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - - /* Check that there is an argument and that it is numeric. */ - if (argv == 0) { - vpi_mcd_printf(1, "ERROR: %s requires an argument.\n", name); - vpi_control(vpiFinish, 1); - return 0; - } - check_numeric_arg(vpi_scan(argv), "ERROR: %s's argument must be" - " numeric.\n", name); - - /* Check that there is only a single argument. */ - if (vpi_scan(argv) != 0) { - vpi_mcd_printf(1, "ERROR: %s takes a single argument.\n", name); - vpi_control(vpiFinish, 1); - return 0; - } - - return 0; -} - -/* $dumpoff does not take an argument. */ -PLI_INT32 sys_dumpoff_compiletf(PLI_BYTE8 *name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - - if (argv != 0) { - vpi_mcd_printf(1, "ERROR: %s does not take an argument.\n", name); - vpi_control(vpiFinish, 1); - } - - return 0; -} - -/* $dumpon does not take an argument. */ -PLI_INT32 sys_dumpon_compiletf(PLI_BYTE8 *name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - - if (argv != 0) { - vpi_mcd_printf(1, "ERROR: %s does not take an argument.\n", name); - vpi_control(vpiFinish, 1); - } - - return 0; -} - /* $dumpvars takes a variety of arguments. */ PLI_INT32 sys_dumpvars_compiletf(PLI_BYTE8 *name) { @@ -374,16 +200,27 @@ PLI_INT32 sys_dumpvars_compiletf(PLI_BYTE8 *name) vpiHandle arg; /* No arguments is OK, dump everything. */ - if (argv == 0) - return 0; + if (argv == 0) return 0; /* The first argument is the numeric level. */ - check_numeric_arg(vpi_scan(argv), "ERROR: %s's first argument must be" - " numeric.\n", name); + if (! is_numeric_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's argument must be numeric.\n", name); + vpi_control(vpiFinish, 1); + } /* The rest of the arguments are either a module or a variable. */ while ((arg=vpi_scan(argv)) != NULL) { switch(vpi_get(vpiType, arg)) { + case vpiMemoryWord: + if (vpi_get(vpiConstantSelect, arg) == 0) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s cannot dump a non-constant select %s.\n", name, + vpi_get_str(vpiType, arg)); + vpi_control(vpiFinish, 1); + } /* The module types. */ case vpiModule: case vpiTask: @@ -393,18 +230,18 @@ PLI_INT32 sys_dumpvars_compiletf(PLI_BYTE8 *name) /* The variable types. */ case vpiNet: case vpiReg: - case vpiMemoryWord: case vpiIntegerVar: case vpiTimeVar: case vpiRealVar: break; default: - vpi_mcd_printf(1, "ERROR: %s cannot dump a %s.\n", - name, vpi_get_str(vpiType, arg)); + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s cannot dump a %s.\n", name, + vpi_get_str(vpiType, arg)); vpi_control(vpiFinish, 1); } } return 0; } - diff --git a/vpi/vcd_priv.h b/vpi/vcd_priv.h index be39c4e88..7daa498c3 100644 --- a/vpi/vcd_priv.h +++ b/vpi/vcd_priv.h @@ -1,7 +1,7 @@ #ifndef __vcd_priv_H #define __vcd_priv_H /* - * Copyright (c) 2003 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -18,9 +18,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: vcd_priv.h,v 1.2 2003/02/13 18:13:28 steve Exp $" -#endif #include "vpi_user.h" @@ -47,12 +44,6 @@ extern const char*find_nexus_ident(int nex); extern void set_nexus_ident(int nex, const char *id); /* The compiletf routines are common for the VCD, LXT and LXT2 dumpers. */ -extern PLI_INT32 sys_dumpall_compiletf(PLI_BYTE8 *name); -extern PLI_INT32 sys_dumpfile_compiletf(PLI_BYTE8 *name); -extern PLI_INT32 sys_dumpflush_compiletf(PLI_BYTE8 *name); -extern PLI_INT32 sys_dumplimit_compiletf(PLI_BYTE8 *name); -extern PLI_INT32 sys_dumpoff_compiletf(PLI_BYTE8 *name); -extern PLI_INT32 sys_dumpon_compiletf(PLI_BYTE8 *name); extern PLI_INT32 sys_dumpvars_compiletf(PLI_BYTE8 *name); #endif diff --git a/vpi_user.h b/vpi_user.h index 0087b52b5..72b10c6c6 100644 --- a/vpi_user.h +++ b/vpi_user.h @@ -143,7 +143,7 @@ typedef struct t_vpi_value { (1) The number of delays to be retrieved ( normally this is used in vpi_get_delays (..) ) { - (1.1) Setted by "no_of_delays" field + (1.1) Set by "no_of_delays" field (1.2) For the primitive_object, the no_of_delays shall be 2 or 3 @@ -279,6 +279,7 @@ typedef struct t_vpi_delay { #define vpiNamedFork 35 #define vpiNet 36 #define vpiParameter 41 +#define vpiPartSelect 42 #define vpiPathTerm 43 #define vpiRealVar 47 #define vpiReg 48 @@ -297,6 +298,7 @@ typedef struct t_vpi_delay { #define vpiModPathIn 95 #define vpiModPathOut 96 #define vpiVariables 100 +#define vpiExpr 102 #define vpiCallback 1000 @@ -346,8 +348,8 @@ typedef struct t_vpi_delay { # define vpiSysFuncReal vpiRealFunc # define vpiSysFuncTime vpiTimeFunc # define vpiSysFuncSized vpiSizedFunc -#define vpiSigned 65 -#define vpiExpr 102 +#define vpiConstantSelect 53 +#define vpiSigned 65 /* IVL private properties */ #define _vpiNexusId 0x1000000 diff --git a/vpip/vpi_callback.c b/vpip/vpi_callback.c index e6645153a..baeb22a3f 100644 --- a/vpip/vpi_callback.c +++ b/vpip/vpi_callback.c @@ -83,7 +83,7 @@ void vpip_run_value_changes(struct __vpiSignal*sig) /* * Handle read-only synch events. This causes the callback to be * scheduled for a moment at the end of the time period. This method - * handles scheduling with itme delays. + * handles scheduling with time delays. */ static void go_readonly_synch(struct __vpiCallback*rfp) { diff --git a/vvp/Makefile.in b/vvp/Makefile.in index 39663272e..ab1e8b227 100644 --- a/vvp/Makefile.in +++ b/vvp/Makefile.in @@ -79,7 +79,7 @@ O = main.o parse.o parse_misc.o lexor.o arith.o array.o bufif.o compile.o \ concat.o \ dff.o extend.o npmos.o part.o reduce.o resolv.o sfunc.o stop.o symbols.o \ ufunc.o codes.o \ -vthread.o schedule.o statistics.o tables.o udp.o vvp_net.o \ +vthread.o schedule.o statistics.o tables.o udp.o vvp_island.o vvp_net.o \ event.o logic.o delay.o words.o $V ifeq (@WIN32@,yes) diff --git a/vvp/README.txt b/vvp/README.txt index bf24c98af..cdd51086f 100644 --- a/vvp/README.txt +++ b/vvp/README.txt @@ -345,6 +345,24 @@ The .alias statements do not create new nodes, but instead create net names that are aliases of an existing node. This handles special cases where a net has different names, possibly in different scopes. +CAST STATEMENTS: + +Sometimes nets need to be cast from a real valued net to a bit based +net or from a bit based net to a real valued net. These statements +are used to performa that operation: + +