diff --git a/PExpr.h b/PExpr.h index 337305e6b..774ed62a5 100644 --- a/PExpr.h +++ b/PExpr.h @@ -45,6 +45,11 @@ class PExpr : public LineInfo { public: enum width_mode_t { SIZED, EXPAND, LOSSLESS, UNSIZED }; + // Flag values that can be passed to elaborate_expr. + static const unsigned NO_FLAGS = 0x0; + static const unsigned NEED_CONST = 0x1; + static const unsigned SYS_TASK_ARG = 0x2; + PExpr(); virtual ~PExpr(); @@ -122,7 +127,7 @@ class PExpr : public LineInfo { // be incomplete. virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; // This method elaborates the expression as gates, but // restricted for use as l-values of continuous assignments. @@ -195,7 +200,7 @@ class PEConcat : public PExpr { virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_force) const; @@ -261,7 +266,7 @@ class PEFNumber : public PExpr { width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; virtual void dump(ostream&) const; @@ -301,7 +306,7 @@ class PEIdent : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; // Elaborate the PEIdent as a port to a module. This method // only applies to Ident expressions. @@ -330,7 +335,7 @@ class PEIdent : public PExpr { // invalid bits (xz) in either expression, then the defined // flag is set to *false*. bool calculate_parts_(Design*, NetScope*, long&msb, long&lsb, bool&defined) const; - NetExpr* calculate_up_do_base_(Design*, NetScope*) const; + NetExpr* calculate_up_do_base_(Design*, NetScope*, bool need_const) const; bool calculate_param_range_(Design*, NetScope*, const NetExpr*msb_ex, long&msb, const NetExpr*lsb_ex, long&lsb, @@ -352,7 +357,8 @@ class PEIdent : public PExpr { NetScope*found, const NetExpr*par_msb, const NetExpr*par_lsb, - unsigned expr_wid) const; + unsigned expr_wid, + unsigned flags) const; NetExpr*elaborate_expr_param_part_(Design*des, NetScope*scope, const NetExpr*par, @@ -365,25 +371,27 @@ class PEIdent : public PExpr { const NetExpr*par, NetScope*found, const NetExpr*par_msb, - const NetExpr*par_lsb) const; + const NetExpr*par_lsb, + bool need_const) const; NetExpr*elaborate_expr_param_idx_do_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found, const NetExpr*par_msb, - const NetExpr*par_lsb) const; + const NetExpr*par_lsb, + bool need_const) const; NetExpr*elaborate_expr_net(Design*des, NetScope*scope, NetNet*net, NetScope*found, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; NetExpr*elaborate_expr_net_word_(Design*des, NetScope*scope, NetNet*net, NetScope*found, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; NetExpr*elaborate_expr_net_part_(Design*des, NetScope*scope, NetESignal*net, @@ -392,15 +400,18 @@ class PEIdent : public PExpr { NetExpr*elaborate_expr_net_idx_up_(Design*des, NetScope*scope, NetESignal*net, - NetScope*found) const; + NetScope*found, + bool need_const) const; NetExpr*elaborate_expr_net_idx_do_(Design*des, NetScope*scope, NetESignal*net, - NetScope*found) const; + NetScope*found, + bool need_const) const; NetExpr*elaborate_expr_net_bit_(Design*des, NetScope*scope, NetESignal*net, - NetScope*found) const; + NetScope*found, + bool need_const) const; private: NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, @@ -423,7 +434,7 @@ class PENumber : public PExpr { width_mode_t&mode); virtual NetEConst*elaborate_expr(Design*des, NetScope*, - unsigned expr_wid, bool) const; + unsigned expr_wid, unsigned) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_force) const; @@ -456,7 +467,7 @@ class PEString : public PExpr { width_mode_t&mode); virtual NetEConst*elaborate_expr(Design*des, NetScope*, - unsigned expr_wid, bool) const; + unsigned expr_wid, unsigned) const; verinum* eval_const(Design*, NetScope*) const; private: @@ -480,7 +491,7 @@ class PEUnary : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; private: @@ -508,7 +519,7 @@ class PEBinary : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; protected: @@ -546,7 +557,7 @@ class PEBComp : public PEBinary { width_mode_t&mode); NetExpr* elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool sys_task_arg) const; + unsigned expr_wid, unsigned flags) const; private: unsigned l_width_; @@ -566,7 +577,7 @@ class PEBLogic : public PEBinary { width_mode_t&mode); NetExpr* elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool sys_task_arg) const; + unsigned expr_wid, unsigned flags) const; }; /* @@ -589,7 +600,7 @@ class PEBLeftWidth : public PEBinary { virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; }; class PEBPower : public PEBLeftWidth { @@ -633,12 +644,13 @@ class PETernary : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; private: NetExpr* elab_and_eval_alternative_(Design*des, NetScope*scope, - PExpr*expr, unsigned expr_wid) const; + PExpr*expr, unsigned expr_wid, + unsigned flags) const; private: PExpr*expr_; @@ -672,7 +684,7 @@ class PECallFunction : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); @@ -686,7 +698,8 @@ class PECallFunction : public PExpr { NetExpr* cast_to_width_(NetExpr*expr, unsigned wid) const; NetExpr* elaborate_sfunc_(Design*des, NetScope*scope, - unsigned expr_wid) const; + unsigned expr_wid, + unsigned flags) const; NetExpr* elaborate_access_func_(Design*des, NetScope*scope, ivl_nature_t, unsigned expr_wid) const; unsigned test_width_sfunc_(Design*des, NetScope*scope, @@ -705,7 +718,7 @@ class PEVoid : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; }; #endif diff --git a/PGate.h b/PGate.h index e45f95ec3..3a4b9ced4 100644 --- a/PGate.h +++ b/PGate.h @@ -1,7 +1,7 @@ #ifndef __PGate_H #define __PGate_H /* - * Copyright (c) 1998-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2011 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 @@ -238,6 +238,9 @@ class PGModule : public PGate { friend class delayed_elaborate_scope_mod_instances; void elaborate_mod_(Design*, Module*mod, NetScope*scope) const; void elaborate_udp_(Design*, PUdp *udp, NetScope*scope) const; + unsigned calculate_instance_count_(Design*, NetScope*, + long&high, long&low, + perm_string name) const; void elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const; void elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*sc) const; bool elaborate_sig_mod_(Design*des, NetScope*scope, Module*mod) const; diff --git a/config.h.in b/config.h.in index cce688618..3fd7953c9 100644 --- a/config.h.in +++ b/config.h.in @@ -42,6 +42,7 @@ # undef HAVE_LIBREADLINE # undef HAVE_LIBZ # undef HAVE_LIBBZ2 +# undef HAVE_LROUND # undef HAVE_SYS_WAIT_H # undef WORDS_BIGENDIAN diff --git a/driver/Makefile.in b/driver/Makefile.in index 346ce4ab5..f31d76037 100644 --- a/driver/Makefile.in +++ b/driver/Makefile.in @@ -38,6 +38,8 @@ CC = @CC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ +LEX = @LEX@ +YACC = @YACC@ MAN = @MAN@ PS2PDF = @PS2PDF@ @@ -78,10 +80,10 @@ iverilog@EXEEXT@: $O $(CC) $(LDFLAGS) $O -o iverilog@EXEEXT@ @EXTRALIBS@ cflexor.c: cflexor.lex - flex -s -Pcf -ocflexor.c $(srcdir)/cflexor.lex + $(LEX) -s -Pcf -ocflexor.c $(srcdir)/cflexor.lex cfparse.h cfparse.c: cfparse.y - bison --verbose -t -d -o cfparse.c --name-prefix=cf $(srcdir)/cfparse.y + $(YACC) --verbose -t -d -o cfparse.c --name-prefix=cf $(srcdir)/cfparse.y %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o diff --git a/dup_expr.cc b/dup_expr.cc index d541161e9..cccdff8c6 100644 --- a/dup_expr.cc +++ b/dup_expr.cc @@ -239,7 +239,8 @@ NetEUFunc* NetEUFunc::dup_expr() const tmp_parms[idx] = parms_[idx]->dup_expr(); } - tmp = new NetEUFunc(scope_, func_, result_sig_->dup_expr(), tmp_parms); + tmp = new NetEUFunc(scope_, func_, result_sig_->dup_expr(), tmp_parms, + need_const_); ivl_assert(*this, tmp); tmp->set_line(*this); diff --git a/elab_expr.cc b/elab_expr.cc index 24a61469b..3a5ee321a 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -82,7 +82,7 @@ static NetBranch* find_existing_implicit_branch(NetNet*sig, NetNet*gnd) NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_variable_type_t lv_type, unsigned lv_width, - PExpr*expr) + PExpr*expr, bool need_const) { int context_wid = -1; switch (lv_type) { @@ -99,7 +99,7 @@ NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, break; } - return elab_and_eval(des, scope, expr, context_wid); + return elab_and_eval(des, scope, expr, context_wid, need_const); } /* @@ -127,7 +127,7 @@ unsigned PExpr::test_width(Design*des, NetScope*, width_mode_t&) return 1; } -NetExpr* PExpr::elaborate_expr(Design*des, NetScope*, unsigned, bool) const +NetExpr* PExpr::elaborate_expr(Design*des, NetScope*, unsigned, unsigned) const { cerr << get_fileline() << ": internal error: I do not know how to" << " elaborate this expression. " << endl; @@ -224,8 +224,10 @@ unsigned PEBinary::test_width(Design*des, NetScope*scope, width_mode_t&mode) * types. */ NetExpr* PEBinary::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + ivl_assert(*this, left_); ivl_assert(*this, right_); @@ -249,8 +251,8 @@ NetExpr* PEBinary::elaborate_expr(Design*des, NetScope*scope, left_->cast_signed(signed_flag_); } - NetExpr*lp = left_->elaborate_expr(des, scope, l_width, false); - NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); + NetExpr*lp = left_->elaborate_expr(des, scope, l_width, flags); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, flags); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; @@ -505,8 +507,10 @@ unsigned PEBComp::test_width(Design*des, NetScope*scope, width_mode_t&) } NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + ivl_assert(*this, left_); ivl_assert(*this, right_); @@ -517,8 +521,8 @@ NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, if (type_is_vectorable(right_->expr_type()) && !right_->has_sign()) left_->cast_signed(false); - NetExpr*lp = left_->elaborate_expr(des, scope, l_width_, false); - NetExpr*rp = right_->elaborate_expr(des, scope, r_width_, false); + NetExpr*lp = left_->elaborate_expr(des, scope, l_width_, flags); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width_, flags); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; @@ -569,13 +573,14 @@ unsigned PEBLogic::test_width(Design*, NetScope*, width_mode_t&) } NetExpr*PEBLogic::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { ivl_assert(*this, left_); ivl_assert(*this, right_); - NetExpr*lp = elab_and_eval(des, scope, left_, -1); - NetExpr*rp = elab_and_eval(des, scope, right_, -1); + bool need_const = NEED_CONST & flags; + NetExpr*lp = elab_and_eval(des, scope, left_, -1, need_const); + NetExpr*rp = elab_and_eval(des, scope, right_, -1, need_const); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; @@ -620,13 +625,13 @@ unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode) long r_val = LONG_MAX; if (r_width < sizeof(long)*8) { r_val = (1L << r_width) - 1L; - if (right_->has_sign()) + if ((op_ == 'p') && right_->has_sign()) r_val >>= 1; } // If the right operand is constant, we can use the // actual value. - NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, NO_FLAGS); if (rp) { eval_expr(rp, r_width); } else { @@ -707,8 +712,10 @@ unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode) } NetExpr*PEBLeftWidth::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + ivl_assert(*this, left_); // The left operand is always context determined, so propagate @@ -717,8 +724,8 @@ NetExpr*PEBLeftWidth::elaborate_expr(Design*des, NetScope*scope, unsigned r_width = right_->expr_width(); - NetExpr*lp = left_->elaborate_expr(des, scope, expr_wid, false); - NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); + NetExpr*lp = left_->elaborate_expr(des, scope, expr_wid, flags); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, flags); if (lp == 0 || rp == 0) { delete lp; delete rp; @@ -1074,7 +1081,8 @@ NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, unsigned wid) const * known function names. */ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, - unsigned expr_wid) const + unsigned expr_wid, + unsigned flags) const { perm_string name = peek_tail_name(path_); @@ -1090,7 +1098,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, } PExpr*expr = parms_[0]; - NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, true); + NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, flags); return cast_to_width_(sub, expr_wid); } @@ -1162,13 +1170,21 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, expression as much as possible, and use the reduced expression if one is created. */ + bool need_const = NEED_CONST & flags; + + unsigned parm_errors = 0; unsigned missing_parms = 0; for (unsigned idx = 0 ; idx < nparms ; idx += 1) { PExpr*expr = parms_[idx]; if (expr) { - NetExpr*tmp = elab_sys_task_arg(des, scope, name, idx, expr); - fun->parm(idx, tmp); - + NetExpr*tmp = elab_sys_task_arg(des, scope, name, idx, + expr, need_const); + if (tmp) { + fun->parm(idx, tmp); + } else { + parm_errors += 1; + fun->parm(idx, 0); + } } else { missing_parms += 1; fun->parm(idx, 0); @@ -1183,6 +1199,9 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, des->errors += 1; } + if (missing_parms || parm_errors) + return 0; + NetExpr*tmp = pad_to_width(fun, expr_wid, *this); tmp->cast_signed(signed_flag_); @@ -1250,10 +1269,12 @@ NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope, } NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + if (peek_tail_name(path_)[0] == '$') - return elaborate_sfunc_(des, scope, expr_wid); + return elaborate_sfunc_(des, scope, expr_wid, flags); NetFuncDef*def = des->find_function(scope, path_); if (def == 0) { @@ -1265,16 +1286,9 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, return elaborate_access_func_(des, scope, access_nature, expr_wid); - // We do not currently support constant user function and - // this is where things fail because of that, though we - // don't know for sure so we need to display both messages. - if (need_constant_expr || is_param_expr) { - cerr << get_fileline() << ": sorry: constant user " - "functions are not currently supported: " - << path_ << "()." << endl << " or" << endl; - } - cerr << get_fileline() << ": error: No function " << path_ - << " in this context (" << scope_path(scope) << ")." << endl; + cerr << get_fileline() << ": error: No function named `" << path_ + << "' found in this context (" << scope_path(scope) << ")." + << endl; des->errors += 1; return 0; } @@ -1283,6 +1297,29 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, NetScope*dscope = def->scope(); ivl_assert(*this, dscope); + bool need_const = NEED_CONST & flags; + + // It is possible to get here before the called function has been + // fully elaborated. If this is the case, elaborate it now. This + // ensures we know whether or not it is a constant function. + if (dscope->elab_stage() < 3) { + dscope->need_const_func(need_const); + const PFunction*pfunc = dscope->func_pform(); + ivl_assert(*this, pfunc); + pfunc->elaborate(des, dscope); + } + + if (dscope->parent() != scope->parent() || !dscope->is_const_func()) { + if (scope->need_const_func()) { + cerr << get_fileline() << ": error: A function invoked by " + "a constant function must be a constant function " + "local to the current module." << endl; + des->errors += 1; + return 0; + } + scope->is_const_func(false); + } + if (! check_call_matches_definition_(des, dscope)) return 0; @@ -1297,6 +1334,7 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, of the function being called. The scope of the called function is elaborated when the definition is elaborated. */ + unsigned parm_errors = 0; unsigned missing_parms = 0; for (unsigned idx = 0 ; idx < parms.count() ; idx += 1) { PExpr*tmp = parms_[idx]; @@ -1304,7 +1342,11 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, parms[idx] = elaborate_rval_expr(des, scope, def->port(idx)->data_type(), (unsigned)def->port(idx)->vector_width(), - tmp); + tmp, need_const); + if (parms[idx] == 0) { + parm_errors += 1; + continue; + } if (NetEEvent*evt = dynamic_cast (parms[idx])) { cerr << evt->get_fileline() << ": error: An event '" << evt->event()->name() << "' can not be a user " @@ -1332,6 +1374,28 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, des->errors += 1; } + if (need_const && !dscope->is_const_func()) { + + // If this is the first time the function has been called in + // a constant context, force the function to be re-elaborated. + // This will generate the necessary error messages to allow + // the user to diagnose the fault. + if (!dscope->need_const_func()) { + dscope->set_elab_stage(2); + dscope->need_const_func(true); + const PFunction*pfunc = dscope->func_pform(); + ivl_assert(*this, pfunc); + pfunc->elaborate(des, dscope); + } + + cerr << get_fileline() << ": error: `" << dscope->basename() + << "' is not a constant function." << endl; + des->errors += 1; + return 0; + } + + if (missing_parms || parm_errors) + return 0; /* Look for the return value signal for the called function. This return value is a magic signal in the scope @@ -1343,7 +1407,7 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, if (NetNet*res = dscope->find_signal(dscope->basename())) { NetESignal*eres = new NetESignal(res); - NetEUFunc*func = new NetEUFunc(scope, dscope, eres, parms); + NetEUFunc*func = new NetEUFunc(scope, dscope, eres, parms, need_const); func->set_line(*this); NetExpr*tmp = pad_to_width(func, expr_wid, *this); @@ -1372,9 +1436,7 @@ unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&) /* If there is a repeat expression, then evaluate the constant value and set the repeat count. */ if (repeat_ && (scope != tested_scope_)) { - need_constant_expr = true; - NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1); - need_constant_expr = false; + NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1, true); if (tmp == 0) return 0; if (tmp->expr_type() == IVL_VT_REAL) { @@ -1426,8 +1488,10 @@ unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&) static int concat_depth = 0; NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + concat_depth += 1; if (debug_elaborate) { @@ -1459,7 +1523,7 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, assert(parms_[idx]); unsigned wid = parms_[idx]->expr_width(); - NetExpr*ex = parms_[idx]->elaborate_expr(des, scope, wid, false); + NetExpr*ex = parms_[idx]->elaborate_expr(des, scope, wid, flags); if (ex == 0) continue; ex->set_line(*parms_[idx]); @@ -1542,7 +1606,7 @@ unsigned PEFNumber::test_width(Design*, NetScope*, width_mode_t&) return expr_width_; } -NetExpr* PEFNumber::elaborate_expr(Design*, NetScope*, unsigned, bool) const +NetExpr* PEFNumber::elaborate_expr(Design*, NetScope*, unsigned, unsigned) const { NetECReal*tmp = new NetECReal(*value_); tmp->set_line(*this); @@ -1569,9 +1633,7 @@ bool PEIdent::calculate_parts_(Design*des, NetScope*scope, two bit select expressions, and both must be constant. Evaluate them and pass the results back to the caller. */ - need_constant_expr = true; - NetExpr*lsb_ex = elab_and_eval(des, scope, index_tail.lsb, -1); - need_constant_expr = false; + NetExpr*lsb_ex = elab_and_eval(des, scope, index_tail.lsb, -1, true); NetEConst*lsb_c = dynamic_cast(lsb_ex); if (lsb_c == 0) { cerr << index_tail.lsb->get_fileline() << ": error: " @@ -1589,9 +1651,7 @@ bool PEIdent::calculate_parts_(Design*des, NetScope*scope, lsb = lsb_c->value().as_long(); } - need_constant_expr = true; - NetExpr*msb_ex = elab_and_eval(des, scope, index_tail.msb, -1); - need_constant_expr = false; + NetExpr*msb_ex = elab_and_eval(des, scope, index_tail.msb, -1, true); NetEConst*msb_c = dynamic_cast(msb_ex); if (msb_c == 0) { cerr << index_tail.msb->get_fileline() << ": error: " @@ -1628,9 +1688,7 @@ bool PEIdent::calculate_up_do_width_(Design*des, NetScope*scope, /* Calculate the width expression (in the lsb_ position) first. If the expression is not constant, error but guess 1 so we can keep going and find more errors. */ - need_constant_expr = true; - NetExpr*wid_ex = elab_and_eval(des, scope, index_tail.lsb, -1); - need_constant_expr = false; + NetExpr*wid_ex = elab_and_eval(des, scope, index_tail.lsb, -1, true); NetEConst*wid_c = dynamic_cast(wid_ex); if (wid_c == 0) { @@ -1651,7 +1709,8 @@ bool PEIdent::calculate_up_do_width_(Design*des, NetScope*scope, * 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 +NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope, + bool need_const) const { const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); @@ -1660,7 +1719,7 @@ NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope) const 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); + NetExpr*tmp = elab_and_eval(des, scope, index_tail.msb, -1, need_const); return tmp; } @@ -1831,7 +1890,7 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) * The signal name may be escaped, but that affects nothing here. */ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool sys_task_arg) const + unsigned expr_wid, unsigned flags) const { assert(scope); @@ -1841,12 +1900,22 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, const NetExpr*ex1, *ex2; - if (is_param_expr && path_.size() > 1) { - cerr << get_fileline() << ": error: parameter r-value expression " - "does not support hierarchical references `" << path_ - << "`." << endl; - des->errors += 1; - return 0; + if (path_.size() > 1) { + if (NEED_CONST & flags) { + cerr << get_fileline() << ": error: A hierarchical reference" + " (`" << path_ << "') is not allowed in a constant" + " expression." << endl; + des->errors += 1; + return 0; + } + if (scope->need_const_func()) { + cerr << get_fileline() << ": error: A hierarchical reference" + " (`" << path_ << "') is not allowed in a constant" + " function." << endl; + des->errors += 1; + return 0; + } + scope->is_const_func(false); } NetScope*found_in = symbol_search(this, des, scope, path_, @@ -1857,7 +1926,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, // the parameter value. if (par != 0) { NetExpr*tmp = elaborate_expr_param_(des, scope, par, found_in, - ex1, ex2, expr_wid); + ex1, ex2, expr_wid, flags); if (!tmp) return 0; @@ -1867,20 +1936,29 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, return tmp; } - // If this is a parameter expression, no other identifiers are valid. - if (is_param_expr) { - cerr << get_fileline() << ": error: identifier `" - << path_ << "` is not a parameter in " - << scope_path(scope) << "." << endl; - des->errors += 1; - return 0; - } - // If the identifier names a signal (a register or wire) // then create a NetESignal node to handle it. if (net != 0) { + if (NEED_CONST & flags) { + cerr << get_fileline() << ": error: A reference to a wire " + "or reg (`" << path_ << "') is not allowed in " + "a constant expression." << endl; + des->errors += 1; + return 0; + } + if (net->scope() != scope) { + if (scope->need_const_func()) { + cerr << get_fileline() << ": error: A reference to a " + "non-local wire or reg (`" << path_ << "') is " + "not allowed in a constant function." << endl; + des->errors += 1; + return 0; + } + scope->is_const_func(false); + } + NetExpr*tmp = elaborate_expr_net(des, scope, net, found_in, - expr_wid, sys_task_arg); + expr_wid, flags); if (!tmp) return 0; @@ -1890,9 +1968,27 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, return tmp; } - // If the identifier is a named event. - // is a variable reference. + // If the identifier is a named event + // then create a NetEEvent node to handle it. if (eve != 0) { + if (NEED_CONST & flags) { + cerr << get_fileline() << ": error: A reference to a named " + "event (`" << path_ << "') is not allowed in a " + "constant expression." << endl; + des->errors += 1; + return 0; + } + if (eve->scope() != scope) { + if (scope->need_const_func()) { + cerr << get_fileline() << ": error: A reference to a " + "non-local named event (`" << path_ << "') is " + "not allowed in a constant function." << endl; + des->errors += 1; + return 0; + } + scope->is_const_func(false); + } + NetEEvent*tmp = new NetEEvent(eve); tmp->set_line(*this); return tmp; @@ -1987,7 +2083,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, } NetExpr*expr = elaborate_expr_net(des, scope, net, found_in, - expr_wid, false); + expr_wid, NO_FLAGS); NetESFunc*sys_expr = 0; if (method_name == "name") { @@ -2007,7 +2103,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, << " attached to " << use_path << "." << endl; des->errors += 1; return elaborate_expr_net(des, scope, net, found_in, - expr_wid, false); + expr_wid, NO_FLAGS); } sys_expr->set_line(*this); @@ -2023,10 +2119,17 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, // are not scopes. If this is not a system task argument, then // it cannot be a scope name, so give up. - if (! sys_task_arg) { + if ( !(SYS_TASK_ARG & flags) ) { // I cannot interpret this identifier. Error message. - cerr << get_fileline() << ": error: Unable to bind wire/reg/memory " - "`" << path_ << "' in `" << scope_path(scope) << "'"<< endl; + cerr << get_fileline() << ": error: Unable to bind " + << (NEED_CONST & flags ? "parameter" : "wire/reg/memory") + << " `" << path_ << "' in `" << scope_path(scope) << "'" + << endl; + if (scope->need_const_func()) { + cerr << get_fileline() << ": : `" << scope->basename() + << "' is being used as a constant function, so may " + "only reference local variables." << endl; + } des->errors += 1; return 0; } @@ -2063,7 +2166,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, << nsc->basename() << " path=" << path_ << endl; - if (! sys_task_arg) { + if ( !(SYS_TASK_ARG & flags) ) { cerr << get_fileline() << ": error: Scope name " << nsc->basename() << " not allowed here." << endl; des->errors += 1; @@ -2248,7 +2351,8 @@ NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, const NetExpr*par, NetScope*, const NetExpr*par_msb, - const NetExpr*par_lsb) const + const NetExpr*par_lsb, + bool need_const) const { const NetEConst*par_ex = dynamic_cast (par); ivl_assert(*this, par_ex); @@ -2258,7 +2362,7 @@ NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, par_lsb, par_lsv, par_ex->value().len())) return 0; - NetExpr*base = calculate_up_do_base_(des, scope); + NetExpr*base = calculate_up_do_base_(des, scope, need_const); if (base == 0) return 0; unsigned long wid = 0; @@ -2327,7 +2431,8 @@ NetExpr* PEIdent::elaborate_expr_param_idx_do_(Design*des, NetScope*scope, const NetExpr*par, NetScope*, const NetExpr*par_msb, - const NetExpr*par_lsb) const + const NetExpr*par_lsb, + bool need_const) const { const NetEConst*par_ex = dynamic_cast (par); ivl_assert(*this, par_ex); @@ -2337,7 +2442,7 @@ NetExpr* PEIdent::elaborate_expr_param_idx_do_(Design*des, NetScope*scope, par_lsb, par_lsv, par_ex->value().len())) return 0; - NetExpr*base = calculate_up_do_base_(des, scope); + NetExpr*base = calculate_up_do_base_(des, scope, need_const); if (base == 0) return 0; unsigned long wid = 0; @@ -2414,8 +2519,10 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, - unsigned expr_wid) const + unsigned expr_wid, unsigned flags) const { + bool need_const = NEED_CONST & flags; + 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()) @@ -2440,11 +2547,11 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, if (use_sel == index_component_t::SEL_IDX_UP) return elaborate_expr_param_idx_up_(des, scope, par, found_in, - par_msb, par_lsb); + par_msb, par_lsb, need_const); if (use_sel == index_component_t::SEL_IDX_DO) return elaborate_expr_param_idx_do_(des, scope, par, found_in, - par_msb, par_lsb); + par_msb, par_lsb, need_const); // NOTE TO SELF (continued): The code below should be // rewritten in the above format, as I get to it. @@ -2469,7 +2576,8 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, /* Handle the case where a parameter has a bit select attached to it. Generate a NetESelect object to select the bit as desired. */ - NetExpr*mtmp = elab_and_eval(des, scope, index_tail.msb, -1); + NetExpr*mtmp = elab_and_eval(des, scope, index_tail.msb, -1, + need_const); /* Let's first try to get constant values for both the parameter and the bit select. If they are @@ -2622,11 +2730,13 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, NetNet*net, NetScope*found_in, unsigned expr_wid, - bool sys_task_arg) const + unsigned flags) const { + bool need_const = NEED_CONST & flags; + const name_component_t&name_tail = path_.back(); - if (name_tail.index.empty() && !sys_task_arg) { + if (name_tail.index.empty() && !(SYS_TASK_ARG & flags)) { cerr << get_fileline() << ": error: Array " << path() << " Needs an array index here." << endl; des->errors += 1; @@ -2647,10 +2757,12 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, ivl_assert(*this, !index_front.lsb); } - NetExpr*word_index = index_front.sel == index_component_t::SEL_NONE - ? 0 - : elab_and_eval(des, scope, index_front.msb, -1); - if (word_index == 0 && !sys_task_arg) + NetExpr*word_index = 0; + if (index_front.sel != index_component_t::SEL_NONE) + word_index = elab_and_eval(des, scope, index_front.msb, -1, + need_const); + + if (word_index == 0 && !(SYS_TASK_ARG & flags)) return 0; if (NetEConst*word_addr = dynamic_cast(word_index)) { @@ -2711,16 +2823,20 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, } if (word_sel == index_component_t::SEL_PART) - return elaborate_expr_net_part_(des, scope, res, found_in, expr_wid); + return elaborate_expr_net_part_(des, scope, res, found_in, + expr_wid); if (word_sel == index_component_t::SEL_IDX_UP) - return elaborate_expr_net_idx_up_(des, scope, res, found_in); + return elaborate_expr_net_idx_up_(des, scope, res, found_in, + need_const); if (word_sel == index_component_t::SEL_IDX_DO) - return elaborate_expr_net_idx_do_(des, scope, res, found_in); + return elaborate_expr_net_idx_do_(des, scope, res, found_in, + need_const); if (word_sel == index_component_t::SEL_BIT) - return elaborate_expr_net_bit_(des, scope, res, found_in); + return elaborate_expr_net_bit_(des, scope, res, found_in, + need_const); ivl_assert(*this, word_sel == index_component_t::SEL_NONE); @@ -2837,9 +2953,10 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, * Part select indexed up, i.e. net[ +: ] */ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, - NetESignal*net, NetScope*) const + NetESignal*net, NetScope*, + bool need_const) const { - NetExpr*base = calculate_up_do_base_(des, scope); + NetExpr*base = calculate_up_do_base_(des, scope, need_const); unsigned long wid = 0; calculate_up_do_width_(des, scope, wid); @@ -2925,9 +3042,10 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, * Part select indexed down, i.e. net[ -: ] */ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, - NetESignal*net, NetScope*)const + NetESignal*net, NetScope*, + bool need_const) const { - NetExpr*base = calculate_up_do_base_(des, scope); + NetExpr*base = calculate_up_do_base_(des, scope, need_const); unsigned long wid = 0; calculate_up_do_width_(des, scope, wid); @@ -3009,7 +3127,8 @@ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, } NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, - NetESignal*net, NetScope*) const + NetESignal*net, NetScope*, + bool need_const) const { const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); @@ -3018,7 +3137,7 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, ivl_assert(*this, index_tail.msb != 0); ivl_assert(*this, index_tail.lsb == 0); - NetExpr*ex = elab_and_eval(des, scope, index_tail.msb, -1); + NetExpr*ex = elab_and_eval(des, scope, index_tail.msb, -1, need_const); // If the bit select is constant, then treat it similar // to the part select, so that I save the effort of @@ -3111,11 +3230,13 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, NetNet*net, NetScope*found_in, unsigned expr_wid, - bool sys_task_arg) const + unsigned flags) const { if (net->array_dimensions() > 0) return elaborate_expr_net_word_(des, scope, net, found_in, - expr_wid, sys_task_arg); + expr_wid, flags); + + bool need_const = NEED_CONST & flags; NetESignal*node = new NetESignal(net); node->set_line(*this); @@ -3143,13 +3264,16 @@ NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, expr_wid); if (use_sel == index_component_t::SEL_IDX_UP) - return elaborate_expr_net_idx_up_(des, scope, node, found_in); + return elaborate_expr_net_idx_up_(des, scope, node, found_in, + need_const); if (use_sel == index_component_t::SEL_IDX_DO) - return elaborate_expr_net_idx_do_(des, scope, node, found_in); + return elaborate_expr_net_idx_do_(des, scope, node, found_in, + need_const); if (use_sel == index_component_t::SEL_BIT) - return elaborate_expr_net_bit_(des, scope, node, found_in); + return elaborate_expr_net_bit_(des, scope, node, found_in, + need_const); // It's not anything else, so this must be a simple identifier // expression with no part or bit select. Return the signal @@ -3173,7 +3297,7 @@ unsigned PENumber::test_width(Design*, NetScope*, width_mode_t&mode) } NetEConst* PENumber::elaborate_expr(Design*, NetScope*, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned) const { assert(value_); verinum val = *value_; @@ -3198,7 +3322,7 @@ unsigned PEString::test_width(Design*, NetScope*, width_mode_t&) } NetEConst* PEString::elaborate_expr(Design*, NetScope*, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned) const { verinum val(value()); val = pad_to_width(val, expr_wid); @@ -3295,15 +3419,17 @@ bool NetETernary::test_operand_compat(ivl_variable_type_t l, * methods. If any elaboration fails, then give up and return 0. */ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { - assert(expr_); - assert(tru_); - assert(fal_); + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + + ivl_assert(*this, expr_); + ivl_assert(*this, tru_); + ivl_assert(*this, fal_); // Elaborate and evaluate the condition expression. Note that // it is always self-determined. - NetExpr*con = elab_and_eval(des, scope, expr_, -1); + NetExpr*con = elab_and_eval(des, scope, expr_, -1, NEED_CONST & flags); if (con == 0) return 0; @@ -3326,7 +3452,8 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, "elaborate TRUE clause of ternary." << endl; - return elab_and_eval_alternative_(des, scope, tru_, expr_wid); + return elab_and_eval_alternative_(des, scope, tru_, + expr_wid, flags); } // Condition is constant FALSE, so we only need the @@ -3337,20 +3464,21 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, "elaborate FALSE clause of ternary." << endl; - return elab_and_eval_alternative_(des, scope, fal_, expr_wid); + return elab_and_eval_alternative_(des, scope, fal_, + expr_wid, flags); } // X and Z conditions need to blend both results, so we // can't short-circuit. } - NetExpr*tru = elab_and_eval_alternative_(des, scope, tru_, expr_wid); + NetExpr*tru = elab_and_eval_alternative_(des, scope, tru_, expr_wid, flags); if (tru == 0) { delete con; return 0; } - NetExpr*fal = elab_and_eval_alternative_(des, scope, fal_, expr_wid); + NetExpr*fal = elab_and_eval_alternative_(des, scope, fal_, expr_wid, flags); if (fal == 0) { delete con; delete tru; @@ -3378,7 +3506,8 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, * self-determined. */ NetExpr* PETernary::elab_and_eval_alternative_(Design*des, NetScope*scope, - PExpr*expr, unsigned expr_wid) const + PExpr*expr, unsigned expr_wid, + unsigned flags) const { int context_wid = expr_wid; if (type_is_vectorable(expr->expr_type()) && !type_is_vectorable(expr_type_)) { @@ -3387,7 +3516,7 @@ NetExpr* PETernary::elab_and_eval_alternative_(Design*des, NetScope*scope, } else { expr->cast_signed(signed_flag_); } - NetExpr*tmp = expr->elaborate_expr(des, scope, expr_wid, false); + NetExpr*tmp = expr->elaborate_expr(des, scope, expr_wid, flags); if (tmp == 0) return 0; eval_expr(tmp, context_wid); @@ -3436,8 +3565,10 @@ unsigned PEUnary::test_width(Design*des, NetScope*scope, width_mode_t&mode) NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + unsigned sub_width = expr_wid; switch (op_) { // Reduction operators and ! always have a self determined width. @@ -3457,7 +3588,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, expr_->cast_signed(signed_flag_); break; } - NetExpr*ip = expr_->elaborate_expr(des, scope, sub_width, false); + NetExpr*ip = expr_->elaborate_expr(des, scope, sub_width, flags); if (ip == 0) return 0; ivl_assert(*expr_, expr_type_ != IVL_VT_NO_TYPE); @@ -3604,7 +3735,7 @@ NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, unsigned expr_wid) const return tmp; } -NetExpr* PEVoid::elaborate_expr(Design*, NetScope*, unsigned, bool) const +NetExpr* PEVoid::elaborate_expr(Design*, NetScope*, unsigned, unsigned) const { return 0; } diff --git a/elab_net.cc b/elab_net.cc index 9ff968c39..2a7d75400 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -222,9 +222,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, case index_component_t::SEL_IDX_DO: case index_component_t::SEL_IDX_UP: { - need_constant_expr = true; - NetExpr*tmp_ex = elab_and_eval(des, scope, index_tail.msb, -1); - need_constant_expr = false; + NetExpr*tmp_ex = elab_and_eval(des, scope, index_tail.msb, -1, true); NetEConst*tmp = dynamic_cast(tmp_ex); if (!tmp) { cerr << get_fileline() << ": error: indexed part select of " @@ -480,9 +478,7 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, } ivl_assert(*this, index_head.sel == index_component_t::SEL_BIT); - need_constant_expr = true; - NetExpr*tmp_ex = elab_and_eval(des, scope, index_head.msb, -1); - need_constant_expr = false; + NetExpr*tmp_ex = elab_and_eval(des, scope, index_head.msb, -1, true); NetEConst*tmp = dynamic_cast(tmp_ex); if (!tmp) { cerr << get_fileline() << ": error: array " << sig->name() diff --git a/elab_scope.cc b/elab_scope.cc index 4bf184661..984a93af9 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -554,9 +554,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) // The initial value for the genvar does not need (nor can it // use) the genvar itself, so we can evaluate this expression // the same way any other parameter value is evaluated. - need_constant_expr = true; - NetExpr*init_ex = elab_and_eval(des, container, loop_init, -1); - need_constant_expr = false; + NetExpr*init_ex = elab_and_eval(des, container, loop_init, -1, true); NetEConst*init = dynamic_cast (init_ex); if (init == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar" @@ -620,9 +618,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) cerr << get_fileline() << ": debug: genvar init = " << genvar << endl; container->genvar_tmp = loop_index; container->genvar_tmp_val = genvar; - need_constant_expr = true; - NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1); - need_constant_expr = false; + NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1, true); NetEConst*test = dynamic_cast(test_ex); if (test == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar" @@ -668,9 +664,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) elaborate_subscope_(des, scope); // Calculate the step for the loop variable. - need_constant_expr = true; - NetExpr*step_ex = elab_and_eval(des, container, loop_step, -1); - need_constant_expr = false; + NetExpr*step_ex = elab_and_eval(des, container, loop_step, -1, true); NetEConst*step = dynamic_cast(step_ex); if (step == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar" @@ -700,9 +694,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else_flag) { - need_constant_expr = true; - NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1); - need_constant_expr = false; + NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1, true); NetEConst*test = dynamic_cast (test_ex); if (test == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar" @@ -792,9 +784,7 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else bool PGenerate::generate_scope_case_(Design*des, NetScope*container) { - need_constant_expr = true; - NetExpr*case_value_ex = elab_and_eval(des, container, loop_test, -1); - need_constant_expr = false; + NetExpr*case_value_ex = elab_and_eval(des, container, loop_test, -1, true); NetEConst*case_value_co = dynamic_cast(case_value_ex); if (case_value_co == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar case" @@ -824,9 +814,9 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container) bool match_flag = false; for (unsigned idx = 0 ; idx < item->item_test.size() && !match_flag ; idx +=1 ) { - need_constant_expr = true; - NetExpr*item_value_ex = elab_and_eval(des, container, item->item_test[idx], -1); - need_constant_expr = false; + NetExpr*item_value_ex = elab_and_eval(des, container, + item->item_test[idx], + -1, true); NetEConst*item_value_co = dynamic_cast(item_value_ex); if (item_value_co == 0) { cerr << get_fileline() << ": error: Cannot evaluate " @@ -1196,10 +1186,8 @@ void PGModule::elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const */ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*sc) const { - need_constant_expr = true; - NetExpr*mse = msb_ ? elab_and_eval(des, sc, msb_, -1) : 0; - NetExpr*lse = lsb_ ? elab_and_eval(des, sc, lsb_, -1) : 0; - need_constant_expr = false; + NetExpr*mse = msb_ ? elab_and_eval(des, sc, msb_, -1, true) : 0; + NetExpr*lse = lsb_ ? elab_and_eval(des, sc, lsb_, -1, true) : 0; NetEConst*msb = dynamic_cast (mse); NetEConst*lsb = dynamic_cast (lse); @@ -1374,6 +1362,14 @@ void PFunction::elaborate_scope(Design*des, NetScope*scope) const { assert(scope->type() == NetScope::FUNC); + // Save a reference to the pform representation of the function + // in case we need to perform early elaboration. + scope->set_func_pform(this); + + // Assume the function is a constant function until we + // find otherwise. + scope->is_const_func(true); + // Scan the parameters in the function, and store the information // needed to evaluate the parameter expressions. diff --git a/elab_sig.cc b/elab_sig.cc index 35312e442..15013c9b8 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -35,14 +35,6 @@ # include "util.h" # include "ivl_assert.h" -/* - * Set the following to true when you need to process an expression - * that is being done in a constant context. This allows the - * elaboration to explicitly say we do not currently support constant - * user functions when the function is not found. - */ -bool need_constant_expr = false; - static bool get_const_argument(NetExpr*exp, verinum&res) { switch (exp->expr_type()) { @@ -445,6 +437,11 @@ bool PGenerate::elaborate_sig_(Design*des, NetScope*scope) const */ void PFunction::elaborate_sig(Design*des, NetScope*scope) const { + if (scope->elab_stage() > 1) + return; + + scope->set_elab_stage(2); + perm_string fname = scope->basename(); assert(scope->type() == NetScope::FUNC); @@ -472,14 +469,14 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const if (return_type_.range) { ivl_assert(*this, return_type_.range->size() == 2); - need_constant_expr = true; NetExpr*me = elab_and_eval(des, scope, - return_type_.range->at(0), -1); + return_type_.range->at(0), -1, + true); assert(me); NetExpr*le = elab_and_eval(des, scope, - return_type_.range->at(1), -1); + return_type_.range->at(1), -1, + true); assert(le); - need_constant_expr = false; long mnum = 0, lnum = 0; if ( ! get_const_argument(me, mnum) ) { @@ -545,14 +542,14 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const ivl_assert(*this, return_type_.range != 0); long use_wid; { - need_constant_expr = true; NetExpr*me = elab_and_eval(des, scope, - (*return_type_.range)[0], -1); + (*return_type_.range)[0], -1, + true); assert(me); NetExpr*le = elab_and_eval(des, scope, - (*return_type_.range)[1], -1); + (*return_type_.range)[1], -1, + true); assert(le); - need_constant_expr = false; long mnum = 0, lnum = 0; if ( ! get_const_argument(me, mnum) ) { @@ -848,10 +845,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const bool bad_lsb = false, bad_msb = false; /* If they exist get the port definition MSB and LSB */ if (port_set_ && port_msb_ != 0) { - /* We do not currently support constant user function. */ - need_constant_expr = true; - NetExpr*texpr = elab_and_eval(des, scope, port_msb_, -1); - need_constant_expr = false; + NetExpr*texpr = elab_and_eval(des, scope, port_msb_, -1, true); if (! eval_as_long(pmsb, texpr)) { cerr << port_msb_->get_fileline() << ": error: " @@ -865,10 +859,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const delete texpr; - /* We do not currently support constant user function. */ - need_constant_expr = true; - texpr = elab_and_eval(des, scope, port_lsb_, -1); - need_constant_expr = false; + texpr = elab_and_eval(des, scope, port_lsb_, -1, true); if (! eval_as_long(plsb, texpr)) { cerr << port_lsb_->get_fileline() << ": error: " @@ -892,10 +883,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const /* If they exist get the net/etc. definition MSB and LSB */ if (net_set_ && net_msb_ != 0 && !bad_msb && !bad_lsb) { - /* We do not currently support constant user function. */ - need_constant_expr = true; - NetExpr*texpr = elab_and_eval(des, scope, net_msb_, -1); - need_constant_expr = false; + NetExpr*texpr = elab_and_eval(des, scope, net_msb_, -1, true); if (! eval_as_long(nmsb, texpr)) { cerr << net_msb_->get_fileline() << ": error: " @@ -909,10 +897,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const delete texpr; - /* We do not currently support constant user function. */ - need_constant_expr = true; - texpr = elab_and_eval(des, scope, net_lsb_, -1); - need_constant_expr = false; + texpr = elab_and_eval(des, scope, net_lsb_, -1, true); if (! eval_as_long(nlsb, texpr)) { cerr << net_lsb_->get_fileline() << ": error: " @@ -1003,10 +988,8 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const if (lidx_ || ridx_) { assert(lidx_ && ridx_); - need_constant_expr = true; - NetExpr*lexp = elab_and_eval(des, scope, lidx_, -1); - NetExpr*rexp = elab_and_eval(des, scope, ridx_, -1); - need_constant_expr = false; + NetExpr*lexp = elab_and_eval(des, scope, lidx_, -1, true); + NetExpr*rexp = elab_and_eval(des, scope, ridx_, -1, true); if ((lexp == 0) || (rexp == 0)) { cerr << get_fileline() << ": internal error: There is " diff --git a/elaborate.cc b/elaborate.cc index a1e0e5a49..d09f7a072 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -227,10 +227,8 @@ unsigned PGBuiltin::calculate_array_count_(Design*des, NetScope*scope, gates, then I am expected to make more than one gate. Figure out how many are desired. */ if (msb_) { - need_constant_expr = true; - NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1); - NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1); - need_constant_expr = false; + NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1, true); + NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1, true); NetEConst*msb_con = dynamic_cast(msb_exp); NetEConst*lsb_con = dynamic_cast(lsb_exp); @@ -265,8 +263,8 @@ unsigned PGBuiltin::calculate_array_count_(Design*des, NetScope*scope, if (debug_elaborate) { cerr << get_fileline() << ": debug: PGBuiltin: Make array " - << "[" << high << ":" << low << "]" - << " of " << count << " gates for " << get_name() << endl; + << "[" << high << ":" << low << "]" << " of " + << count << " gates for " << get_name() << endl; } } @@ -686,8 +684,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const unsigned instance_width = 1; perm_string name = get_name(); - if (name == "") - name = scope->local_symbol(); + if (name == "") name = scope->local_symbol(); /* Calculate the array bounds and instance count for the gate, as described in the Verilog source. If there is none, then @@ -695,8 +692,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const long low=0, high=0; unsigned array_count = calculate_array_count_(des, scope, high, low); - if (array_count == 0) - return; + if (array_count == 0) return; unsigned output_count = calculate_output_count_(); @@ -1773,6 +1769,62 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const } +unsigned PGModule::calculate_instance_count_(Design*des, NetScope*scope, + long&high, long&low, + perm_string name) const +{ + unsigned count = 1; + high = 0; + low = 0; + + /* If the Verilog source has a range specification for the UDP, then + * I am expected to make more than one gate. Figure out how many are + * desired. */ + if (msb_) { + NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1, true); + NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1, true); + + NetEConst*msb_con = dynamic_cast(msb_exp); + NetEConst*lsb_con = dynamic_cast(lsb_exp); + + if (msb_con == 0) { + cerr << get_fileline() << ": error: Unable to evaluate " + "expression " << *msb_ << endl; + des->errors += 1; + return 0; + } + + if (lsb_con == 0) { + cerr << get_fileline() << ": error: Unable to evaluate " + "expression " << *lsb_ << endl; + des->errors += 1; + return 0; + } + + verinum msb = msb_con->value(); + verinum lsb = lsb_con->value(); + + delete msb_exp; + delete lsb_exp; + + if (msb.as_long() > lsb.as_long()) + count = msb.as_long() - lsb.as_long() + 1; + else + count = lsb.as_long() - msb.as_long() + 1; + + low = lsb.as_long(); + high = msb.as_long(); + + if (debug_elaborate) { + cerr << get_fileline() << ": debug: PGModule: Make range " + << "[" << high << ":" << low << "]" << " of " + << count << " UDPs for " << name << endl; + } + } + + return count; +} + /* * From a UDP definition in the source, make a NetUDP * object. Elaborate the pin expressions as netlists, then connect @@ -1803,6 +1855,19 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const } } + long low = 0, high = 0; + unsigned inst_count = calculate_instance_count_(des, scope, high, low, + my_name); + if (inst_count == 0) return; + + if (inst_count != 1) { + cerr << get_fileline() << ": sorry: UDPs with a range (" + << my_name << " [" << high << ":" << low << "]) are " + << "not supported." << endl; + des->errors += 1; + return; + } + assert(udp); NetUDP*net = new NetUDP(scope, my_name, udp->ports.count(), udp); net->set_line(*this); @@ -2057,9 +2122,8 @@ NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, { ivl_assert(*this, rval_); - need_constant_expr = is_constant_; - NetExpr*rv = elaborate_rval_expr(des, scope, lv_type, lv_width, rval()); - need_constant_expr = false; + NetExpr*rv = elaborate_rval_expr(des, scope, lv_type, lv_width, rval(), + is_constant_); if (!is_constant_ || !rv) return rv; @@ -3376,7 +3440,8 @@ NetProc* PEventStatement::elaborate_wait(Design*des, NetScope*scope, PExpr::width_mode_t mode; pe->test_width(des, scope, mode); - NetExpr*expr = pe->elaborate_expr(des, scope, pe->expr_width(), false); + NetExpr*expr = pe->elaborate_expr(des, scope, pe->expr_width(), + PExpr::NO_FLAGS); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate" " wait condition expression." << endl; @@ -3736,6 +3801,11 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const void PFunction::elaborate(Design*des, NetScope*scope) const { + if (scope->elab_stage() > 2) + return; + + scope->set_elab_stage(3); + NetFuncDef*def = scope->func_def(); if (def == 0) { cerr << get_fileline() << ": internal error: " @@ -3751,6 +3821,7 @@ void PFunction::elaborate(Design*des, NetScope*scope) const if (st == 0) { cerr << statement_->get_fileline() << ": error: Unable to elaborate " "statement in function " << scope->basename() << "." << endl; + scope->is_const_func(true); // error recovery des->errors += 1; return; } @@ -4204,9 +4275,8 @@ bool Module::elaborate(Design*des, NetScope*scope) const for (specparam_it_t cur = specparams.begin() ; cur != specparams.end() ; ++ cur ) { - need_constant_expr = true; - NetExpr*val = elab_and_eval(des, scope, (*cur).second, -1); - need_constant_expr = false; + NetExpr*val = elab_and_eval(des, scope, (*cur).second, -1, + true); NetScope::spec_val_t value; if (NetECReal*val_cr = dynamic_cast (val)) { diff --git a/eval_tree.cc b/eval_tree.cc index 5fc9831c2..6bc35b4c0 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -1946,12 +1946,29 @@ NetExpr* NetESFunc::eval_tree() NetExpr* NetEUFunc::eval_tree() { - if (need_constant_expr) { - cerr << get_fileline() << ": sorry: constant user " - "functions are not currently supported: " - << func_->basename() << "()." << endl; + // If we know the function cannot be evaluated as a constant, + // give up now. + if (!func()->is_const_func()) + return 0; + // Variables inside static functions can be accessed from outside + // the function, so we can't be sure they are constant unless the + // function was called in a constant context. + if (!func()->is_auto() && !need_const_) + return 0; + + // Run through the input parameters to check they are constants. + for (unsigned idx = 0; idx < parm_count(); idx += 1) { + if (dynamic_cast (parm(idx))) + continue; + if (dynamic_cast (parm(idx))) + continue; + return 0; } + if (need_const_) { + cerr << get_fileline() << ": sorry: Constant user functions are " + "not yet supported." << endl; + } return 0; } diff --git a/ivlpp/Makefile.in b/ivlpp/Makefile.in index f65f1114f..62a799e98 100644 --- a/ivlpp/Makefile.in +++ b/ivlpp/Makefile.in @@ -36,6 +36,7 @@ CC = @CC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ +LEX = @LEX@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. @@ -69,7 +70,7 @@ ivlpp@EXEEXT@: $O $(CC) $(LDFLAGS) $O -o ivlpp@EXEEXT@ @EXTRALIBS@ lexor.c: lexor.lex - flex -olexor.c $(srcdir)/lexor.lex + $(LEX) -olexor.c $(srcdir)/lexor.lex install: all installdirs $(libdir)/ivl$(suffix)/ivlpp@EXEEXT@ diff --git a/net_design.cc b/net_design.cc index 31296aa63..4b4ba5692 100644 --- a/net_design.cc +++ b/net_design.cc @@ -33,6 +33,7 @@ # include "compiler.h" # include "netmisc.h" # include "PExpr.h" +# include "PTask.h" # include # include "ivl_assert.h" @@ -332,7 +333,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) /* Evaluate the msb expression, if it is present. */ PExpr*msb_expr = (*cur).second.msb_expr; if (msb_expr) { - (*cur).second.msb = elab_and_eval(des, this, msb_expr, -1); + (*cur).second.msb = elab_and_eval(des, this, msb_expr, -1, true); if (! eval_as_long(msb, (*cur).second.msb)) { cerr << (*cur).second.val->get_fileline() << ": error: Unable to evaluate msb expression " @@ -348,7 +349,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) /* Evaluate the lsb expression, if it is present. */ PExpr*lsb_expr = (*cur).second.lsb_expr; if (lsb_expr) { - (*cur).second.lsb = elab_and_eval(des, this, lsb_expr, -1); + (*cur).second.lsb = elab_and_eval(des, this, lsb_expr, -1, true); if (! eval_as_long(lsb, (*cur).second.lsb)) { cerr << (*cur).second.val->get_fileline() << ": error: Unable to evaluate lsb expression " @@ -369,7 +370,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) if (range_flag) lv_width = (msb >= lsb) ? 1 + msb - lsb : 1 + lsb - msb; - NetExpr*expr = elab_and_eval(des, val_scope, val_expr, lv_width); + NetExpr*expr = elab_and_eval(des, val_scope, val_expr, lv_width, true); if (! expr) return; @@ -488,7 +489,7 @@ void NetScope::evaluate_parameter_real_(Design*des, param_ref_t cur) PExpr*val_expr = (*cur).second.val_expr; NetScope*val_scope = (*cur).second.val_scope; - NetExpr*expr = elab_and_eval(des, val_scope, val_expr, -1); + NetExpr*expr = elab_and_eval(des, val_scope, val_expr, -1, true); if (! expr) return; @@ -631,14 +632,6 @@ void NetScope::evaluate_parameter_(Design*des, param_ref_t cur) cur->second.val_expr = 0; } -/* - * Set the following to true when evaluating the parameter expressions. - * This causes PEIdent::elaborate_expr() to report an error if an - * identifier in an expression is anything other than a non-hierarchical - * parameter name. - */ -bool is_param_expr = false; - void NetScope::evaluate_parameters(Design*des) { for (map::const_iterator cur = children_.begin() @@ -649,13 +642,11 @@ void NetScope::evaluate_parameters(Design*des) cerr << ":0" << ": debug: " << "Evaluate parameters in " << scope_path(this) << endl; - is_param_expr = true; for (param_ref_t cur = parameters.begin() ; cur != parameters.end() ; ++ cur) { evaluate_parameter_(des, cur); } - is_param_expr = false; } void Design::residual_defparams() @@ -730,9 +721,18 @@ NetFuncDef* Design::find_function(NetScope*scope, const pform_name_t&name) std::list eval_path = eval_scope_path(this, scope, name); NetScope*func = find_scope(scope, eval_path, NetScope::FUNC); - if (func && (func->type() == NetScope::FUNC)) + if (func && (func->type() == NetScope::FUNC)) { + // If a function is used in a parameter definition or in + // a signal declaration, it is possible to get here before + // the function's signals have been elaborated. If this is + // the case, elaborate them now. + if (func->elab_stage() < 2) { + const PFunction*pfunc = func->func_pform(); + assert(pfunc); + pfunc->elaborate_sig(this, func); + } return func->func_def(); - + } return 0; } diff --git a/net_scope.cc b/net_scope.cc index 50f2d646f..2509cbc61 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -43,6 +43,8 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t) { events_ = 0; lcounter_ = 0; + need_const_func_ = false; + is_const_func_ = false; is_auto_ = false; is_cell_ = false; @@ -72,6 +74,8 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t) default: /* BEGIN_END and FORK_JOIN, do nothing */ break; } + func_pform_ = 0; + elab_stage_ = 1; lineno_ = 0; def_lineno_ = 0; genvar_tmp_val = 0; diff --git a/netlist.cc b/netlist.cc index 9adca5dcc..d59be9650 100644 --- a/netlist.cc +++ b/netlist.cc @@ -1988,8 +1988,8 @@ const NetExpr* NetSTask::parm(unsigned idx) const } NetEUFunc::NetEUFunc(NetScope*scope, NetScope*def, NetESignal*res, - svector&p) -: scope_(scope), func_(def), result_sig_(res), parms_(p) + svector&p, bool nc) +: scope_(scope), func_(def), result_sig_(res), parms_(p), need_const_(nc) { expr_width(result_sig_->expr_width()); } diff --git a/netlist.h b/netlist.h index 042be10ae..2a9a3e06f 100644 --- a/netlist.h +++ b/netlist.h @@ -73,6 +73,7 @@ class NetTaskDef; class NetEvTrig; class NetEvWait; class PExpr; +class PFunction; class netenum_t; struct target; @@ -790,6 +791,29 @@ class NetScope : public Attrib { unsigned get_def_lineno() const { return def_lineno_; }; bool in_func() const; + + /* Provide a link back to the pform to allow early elaboration of + constant functions. */ + void set_func_pform(const PFunction*pfunc) { func_pform_ = pfunc; }; + const PFunction*func_pform() const { return func_pform_; }; + + /* Allow tracking of elaboration stages. The three stages are: + 1 - scope elaboration + 2 - signal elaboration + 3 - statement elaboration + This is only used for functions, to support early elaboration. + */ + void set_elab_stage(unsigned stage) { elab_stage_ = stage; }; + unsigned elab_stage() const { return elab_stage_; }; + + /* Is this a function called in a constant expression. */ + void need_const_func(bool need_const) { need_const_func_ = need_const; }; + bool need_const_func() const { return need_const_func_; }; + + /* Is this a constant function. */ + void is_const_func(bool is_const) { is_const_func_ = is_const; }; + bool is_const_func() const { return is_const_func_; }; + /* Is the task or function automatic. */ void is_auto(bool is_auto__) { is_auto_ = is_auto__; }; bool is_auto() const { return is_auto_; }; @@ -904,7 +928,6 @@ class NetScope : public Attrib { param_ref_t find_parameter(perm_string name); - struct spec_val_t { ivl_variable_type_t type; union { @@ -954,6 +977,8 @@ class NetScope : public Attrib { NetTaskDef*task_; NetFuncDef*func_; }; + const PFunction*func_pform_; + unsigned elab_stage_; // Enumerations. The enum_sets_ is a list of all the // enumerations present in this scope. The enum_names_ is a @@ -966,7 +991,7 @@ class NetScope : public Attrib { map children_; unsigned lcounter_; - bool is_auto_, is_cell_; + bool need_const_func_, is_const_func_, is_auto_, is_cell_; }; /* @@ -3095,7 +3120,7 @@ class NetTaskDef { class NetEUFunc : public NetExpr { public: - NetEUFunc(NetScope*, NetScope*, NetESignal*, svector&); + NetEUFunc(NetScope*, NetScope*, NetESignal*, svector&, bool); ~NetEUFunc(); const NetESignal*result_sig() const; @@ -3119,6 +3144,7 @@ class NetEUFunc : public NetExpr { NetScope*func_; NetESignal*result_sig_; svector parms_; + bool need_const_; private: // not implemented NetEUFunc(const NetEUFunc&); diff --git a/netmisc.cc b/netmisc.cc index 42657c7bc..6bb216e85 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -470,7 +470,8 @@ static const char*width_mode_name(PExpr::width_mode_t mode) } } -NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width) +NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, + int context_width, bool need_const) { PExpr::width_mode_t mode = PExpr::SIZED; if ((context_width == -2) && !gn_strict_expr_width_flag) @@ -510,7 +511,11 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width) } } - NetExpr*tmp = pe->elaborate_expr(des, scope, expr_width, false); + unsigned flags = PExpr::NO_FLAGS; + if (need_const) + flags |= PExpr::NEED_CONST; + + NetExpr*tmp = pe->elaborate_expr(des, scope, expr_width, flags); if (tmp == 0) return 0; eval_expr(tmp, context_width); @@ -524,7 +529,7 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width) } NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, - unsigned arg_idx, PExpr*pe) + unsigned arg_idx, PExpr*pe, bool need_const) { PExpr::width_mode_t mode = PExpr::SIZED; pe->test_width(des, scope, mode); @@ -539,7 +544,11 @@ NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, << ", mode=" << width_mode_name(mode) << endl; } - NetExpr*tmp = pe->elaborate_expr(des, scope, pe->expr_width(), true); + unsigned flags = PExpr::SYS_TASK_ARG; + if (need_const) + flags |= PExpr::NEED_CONST; + + NetExpr*tmp = pe->elaborate_expr(des, scope, pe->expr_width(), flags); if (tmp == 0) return 0; eval_expr(tmp, -1); diff --git a/netmisc.h b/netmisc.h index f056c933f..20d72461f 100644 --- a/netmisc.h +++ b/netmisc.h @@ -134,17 +134,6 @@ extern NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid); */ extern unsigned count_lval_width(const class NetAssign_*first); -/* - * This is temporarily used to indicate that a user function elaboration - * fail is likely the result of missing constant user function support. - */ -extern bool need_constant_expr; - -/* - * This is used to indicate that we are evaluating a parameter expression. - */ -extern bool is_param_expr; - /* * This function elaborates an expression, and tries to evaluate it * right away. If the expression can be evaluated, this returns a @@ -160,14 +149,16 @@ extern bool is_param_expr; class PExpr; extern NetExpr* elab_and_eval(Design*des, NetScope*scope, - PExpr*pe, int context_width); + PExpr*pe, int context_width, + bool need_const =false); /* * This function is a variant of elab_and_eval that elaborates and * evaluates the arguments of a system task. */ -extern NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, - unsigned arg_idx, PExpr*pe); +extern NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, + perm_string name, unsigned arg_idx, + PExpr*pe, bool need_const =false); /* * This function elaborates an expression as if it is for the r-value * of an assignment, The lv_type and lv_width are the type and width @@ -176,7 +167,8 @@ extern NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, */ extern NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_variable_type_t lv_type, - unsigned lv_width, PExpr*expr); + unsigned lv_width, PExpr*expr, + bool need_const =false); /* * This procedure evaluates an expression and if the evaluation is diff --git a/t-dll-api.cc b/t-dll-api.cc index 206943f1a..179a42a62 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -1424,6 +1424,36 @@ extern "C" unsigned ivl_lpm_size(ivl_lpm_t net) return net->u_.repeat.count; case IVL_LPM_CONCAT: return net->u_.concat.inputs; + case IVL_LPM_ABS: + case IVL_LPM_CAST_INT: + case IVL_LPM_CAST_INT2: + case IVL_LPM_CAST_REAL: + 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_SIGN_EXT: + case IVL_LPM_FF: + return 1; + case IVL_LPM_ADD: + 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_DIVIDE: + case IVL_LPM_MOD: + case IVL_LPM_MULT: + case IVL_LPM_POW: + case IVL_LPM_SUB: + case IVL_LPM_SHIFTL: + case IVL_LPM_SHIFTR: + case IVL_LPM_PART_VP: + case IVL_LPM_PART_PV: + return 2; default: assert(0); return 0; diff --git a/tgt-vhdl/expr.cc b/tgt-vhdl/expr.cc index 2b8322683..555a50f43 100644 --- a/tgt-vhdl/expr.cc +++ b/tgt-vhdl/expr.cc @@ -320,6 +320,19 @@ static vhdl_expr *translate_binary(ivl_expr_t e) int rwidth = rhs->get_type()->get_width(); int result_width = ivl_expr_width(e); + // There's a funny corner-case where both the LHS and RHS are constant + // single bit numbers and the VHDL compiler can't decide between the + // std_ulogic and bit overloads of various operators + const bool lnumber = ivl_expr_type(ivl_expr_oper1(e)) == IVL_EX_NUMBER; + const bool rnumber = ivl_expr_type(ivl_expr_oper2(e)) == IVL_EX_NUMBER; + if (lwidth == 1 && rwidth == 1 && lnumber && rnumber) { + // It's sufficient to qualify only one side + vhdl_fcall *lqual = new vhdl_fcall("std_logic'", lhs->get_type()); + lqual->add_expr(lhs); + + lhs = lqual; + } + // For === and !== we need to compare std_logic_vectors // rather than signeds vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR, result_width-1, 0); diff --git a/tgt-vhdl/lpm.cc b/tgt-vhdl/lpm.cc index c68d6bebe..7153ec06b 100644 --- a/tgt-vhdl/lpm.cc +++ b/tgt-vhdl/lpm.cc @@ -41,26 +41,6 @@ static vhdl_expr *part_select_base(vhdl_scope *scope, ivl_lpm_t lpm) return off->cast(&integer); } -static vhdl_expr *concat_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm) -{ - vhdl_type *result_type = - vhdl_type::type_for(ivl_lpm_width(lpm), ivl_lpm_signed(lpm) != 0); - vhdl_binop_expr *expr = - new vhdl_binop_expr(VHDL_BINOP_CONCAT, result_type); - - for (int i = ivl_lpm_size(lpm) - 1; i >= 0; i--) { - vhdl_expr *e = readable_ref(scope, ivl_lpm_data(lpm, i)); - if (NULL == e) { - delete expr; - return NULL; - } - - expr->add_expr(e); - } - - return expr; -} - static vhdl_expr *binop_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op) { unsigned out_width = ivl_lpm_width(lpm); @@ -68,14 +48,21 @@ static vhdl_expr *binop_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop vhdl_type::type_for(out_width, ivl_lpm_signed(lpm) != 0); vhdl_binop_expr *expr = new vhdl_binop_expr(op, result_type); - for (int i = 0; i < 2; i++) { + for (unsigned i = 0; i < ivl_lpm_size(lpm); i++) { vhdl_expr *e = readable_ref(scope, ivl_lpm_data(lpm, i)); - if (NULL == e) { - delete expr; + if (NULL == e) return NULL; - } - expr->add_expr(e->cast(result_type)); + // It's possible that the inputs are a mixture of signed and unsigned + // in which case we must cast them to the output type + e = e->cast(vhdl_type::type_for(e->get_type()->get_width(), + ivl_lpm_signed(lpm) != 0)); + + // Bit of a hack: the LPM inputs are in the wrong order for concatenation + if (op == VHDL_BINOP_CONCAT) + expr->add_expr_front(e); + else + expr->add_expr(e); } if (op == VHDL_BINOP_MULT) { @@ -256,7 +243,7 @@ static vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm) case IVL_LPM_MOD: return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_MOD); case IVL_LPM_CONCAT: - return concat_lpm_to_expr(scope, lpm); + return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_CONCAT); case IVL_LPM_CMP_GE: return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_GEQ); case IVL_LPM_CMP_GT: @@ -284,7 +271,7 @@ static vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm) case IVL_LPM_RE_XOR: return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XOR, false); case IVL_LPM_RE_XNOR: - return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XOR, true); + return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XNOR, true); case IVL_LPM_SIGN_EXT: return sign_extend_lpm_to_expr(scope, lpm); case IVL_LPM_ARRAY: diff --git a/tgt-vhdl/scope.cc b/tgt-vhdl/scope.cc index 56d313933..190b18590 100644 --- a/tgt-vhdl/scope.cc +++ b/tgt-vhdl/scope.cc @@ -239,7 +239,16 @@ void draw_nexus(ivl_nexus_t nexus) vhdl_type *type = vhdl_type::type_for(lpm_temp_width, ivl_lpm_signed(lpm) != 0); ostringstream ss; - ss << "LPM" << ivl_lpm_basename(lpm); + ss << "LPM"; + if (nexus == ivl_lpm_q(lpm)) + ss << "_q"; + else { + for (unsigned d = 0; d < ivl_lpm_size(lpm); d++) { + if (nexus == ivl_lpm_data(lpm, d)) + ss << "_d" << d; + } + } + ss << ivl_lpm_basename(lpm); if (!vhdl_scope->have_declared(ss.str())) vhdl_scope->add_decl(new vhdl_signal_decl(ss.str().c_str(), type)); diff --git a/tgt-vhdl/support.cc b/tgt-vhdl/support.cc index 6f16ba762..3a06efea7 100644 --- a/tgt-vhdl/support.cc +++ b/tgt-vhdl/support.cc @@ -144,7 +144,7 @@ void support_function::emit(std::ostream &of, int level) const emit_reduction(of, level, "and", '1'); break; case SF_REDUCE_XOR: - emit_reduction(of, level, "xnor", '0'); + emit_reduction(of, level, "xor", '0'); break; case SF_REDUCE_XNOR: emit_reduction(of, level, "xnor", '0'); diff --git a/tgt-vhdl/vhdl_syntax.cc b/tgt-vhdl/vhdl_syntax.cc index 4f231e1ee..747717e24 100644 --- a/tgt-vhdl/vhdl_syntax.cc +++ b/tgt-vhdl/vhdl_syntax.cc @@ -940,6 +940,11 @@ void vhdl_binop_expr::add_expr(vhdl_expr *e) operands_.push_back(e); } +void vhdl_binop_expr::add_expr_front(vhdl_expr *e) +{ + operands_.push_front(e); +} + void vhdl_binop_expr::find_vars(vhdl_var_set_t& read) { for (list::const_iterator it = operands_.begin(); diff --git a/tgt-vhdl/vhdl_syntax.hh b/tgt-vhdl/vhdl_syntax.hh index 32745b097..b591d5ebf 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -126,6 +126,7 @@ public: ~vhdl_binop_expr(); void add_expr(vhdl_expr *e); + void add_expr_front(vhdl_expr *e); void emit(std::ostream &of, int level) const; void find_vars(vhdl_var_set_t& read); private: diff --git a/verinum.cc b/verinum.cc index 0c4629e4a..143c0cac9 100644 --- a/verinum.cc +++ b/verinum.cc @@ -1089,15 +1089,17 @@ verinum pow(const verinum&left, const verinum&right) long pow_count = right.as_long(); // We need positive and negative one in a few places. - verinum one (verinum::V0, left.len(), left.has_len()); + unsigned len = left.len(); + // Positive one must be at least two bits wide! + verinum one (verinum::V0, (len<2) ? 2 : len, left.has_len()); one.has_sign(left.has_sign()); one.set(0, verinum::V1); - verinum m_one (verinum::V1, left.len(), left.has_len()); + verinum m_one (verinum::V1, len, left.has_len()); m_one.has_sign(true); // If either the right or left values are undefined we return 'bx. if (!right.is_defined() || !left.is_defined()) { - result = verinum(verinum::Vx, left.len(), left.has_len()); + result = verinum(verinum::Vx, len, left.has_len()); result.has_sign(left.has_sign()); // If the right value is zero we need to set the result to 1. } else if (pow_count == 0) { @@ -1105,7 +1107,7 @@ verinum pow(const verinum&left, const verinum&right) } else if (pow_count < 0) { // 0 ** is 'bx. if (left.is_zero()) { - result = verinum(verinum::Vx, left.len(), left.has_len()); + result = verinum(verinum::Vx, len, left.has_len()); result.has_sign(left.has_sign()); // 1 ** is 1. } else if (left == one) { @@ -1119,7 +1121,7 @@ verinum pow(const verinum&left, const verinum&right) } // Everything else is 0. } else { - result = verinum(verinum::V0, left.len(), left.has_len()); + result = verinum(verinum::V0, len, left.has_len()); result.has_sign(left.has_sign()); } } diff --git a/vhdlpp/architec.h b/vhdlpp/architec.h index e184cb1ea..0dd208b32 100644 --- a/vhdlpp/architec.h +++ b/vhdlpp/architec.h @@ -50,7 +50,7 @@ class Architecture : public Scope, public LineInfo { virtual int elaborate(Entity*ent, Architecture*arc); virtual int emit(ostream&out, Entity*ent, Architecture*arc); - virtual void dump(ostream&out) const; + virtual void dump(ostream&out, int indent = 0) const; private: @@ -77,7 +77,7 @@ class Architecture : public Scope, public LineInfo { int emit(ostream&out, Entity*entity); // The dump method writes a debug display to the given output. - void dump(ostream&out, perm_string of_entity) const; + void dump(ostream&out, perm_string of_entity, int indent = 0) const; private: perm_string name_; @@ -100,7 +100,7 @@ class SignalAssignment : public Architecture::Statement { ~SignalAssignment(); virtual int emit(ostream&out, Entity*entity, Architecture*arc); - virtual void dump(ostream&out) const; + virtual void dump(ostream&out, int ident =0) const; private: ExpName*lval_; @@ -116,7 +116,7 @@ class ComponentInstantiation : public Architecture::Statement { virtual int elaborate(Entity*ent, Architecture*arc); virtual int emit(ostream&out, Entity*entity, Architecture*arc); - virtual void dump(ostream&out) const; + virtual void dump(ostream&out, int indent =0) const; private: perm_string iname_; diff --git a/vhdlpp/debug.cc b/vhdlpp/debug.cc index f787ad10f..934c1494a 100644 --- a/vhdlpp/debug.cc +++ b/vhdlpp/debug.cc @@ -55,16 +55,16 @@ void dump_design_entities(const char*path) } } -void ComponentBase::dump_ports(ostream&out) const +void ComponentBase::dump_ports(ostream&out, int indent) const { if (ports_.size() == 0) { - out << " No ports" << endl; + out << setw(indent) << "" << "No ports" << endl; } else { - out << " PORTS:" << endl; + out << setw(indent) << "" << "PORTS:" << endl; for (vector::const_iterator cur = ports_.begin() ; cur != ports_.end() ; ++cur) { InterfacePort*item = *cur; - out << setw(6) << "" << item->name + out << setw(indent+2) << "" << item->name << " : " << item->mode << ", type="; if (item->type) @@ -87,68 +87,68 @@ void Scope::dump_scope(ostream&out) const } } -void Entity::dump(ostream&out) const +void Entity::dump(ostream&out, int indent) const { - out << "entity " << get_name() + out << setw(indent) << "" << "entity " << get_name() << " file=" << get_fileline() << endl; - dump_ports(out); + dump_ports(out, indent+2); for (map::const_iterator cur = arch_.begin() ; cur != arch_.end() ; ++cur) { - cur->second->dump(out, get_name()); + cur->second->dump(out, get_name(), indent); } } -void Architecture::dump(ostream&out, perm_string of_entity) const +void Architecture::dump(ostream&out, perm_string of_entity, int indent) const { - out << "architecture " << name_ + out << setw(indent) << "" << "architecture " << name_ << " of entity " << of_entity << " file=" << get_fileline() << endl; // Dump signal declarations for (map::const_iterator cur = signals_.begin() ; cur != signals_.end() ; ++cur) { - cur->second->dump(out); + cur->second->dump(out, indent+3); } dump_scope(out); for (list::const_iterator cur = statements_.begin() ; cur != statements_.end() ; ++cur) { - (*cur)->dump(out); + (*cur)->dump(out, indent+3); } } -void Architecture::Statement::dump(ostream&out) const +void Architecture::Statement::dump(ostream&out, int indent) const { - out << " Architecture::Statement at file=" << get_fileline() << endl; + out << setw(indent) << "" << "Architecture::Statement at file=" << get_fileline() << endl; } -void Signal::dump(ostream&out) const +void Signal::dump(ostream&out, int indent) const { - out << " signal " << name_ << " is " << *type_ << endl; + out << setw(indent) << "" << "signal " << name_ << " is " << *type_ << endl; } -void SignalAssignment::dump(ostream&out) const +void SignalAssignment::dump(ostream&out, int indent) const { - out << " SignalAssignment file=" << get_fileline() << endl; - lval_->dump(out, 4); - out << " <= ..." << endl; + out << setw(indent) << "" << "SignalAssignment file=" << get_fileline() << endl; + lval_->dump(out, indent+1); + out << setw(indent+2) << "" << "<= ..." << endl; for (list::const_iterator cur = rval_.begin() ; cur != rval_.end() ; ++cur) { - (*cur)->dump(out, 5); + (*cur)->dump(out, indent+2); } } -void ComponentInstantiation::dump(ostream&out) const +void ComponentInstantiation::dump(ostream&out, int indent) const { - out << " Component Instantiation file=" << get_fileline() << endl; + out << setw(indent) << "" << "Component Instantiation file=" << get_fileline() << endl; for (map::const_iterator cur = port_map_.begin() ; cur != port_map_.end() ; ++cur) { - out << " " << cur->first << " => ..." << endl; - cur->second->dump(out, 10); + out << setw(indent+2) <<""<< cur->first << " => ..." << endl; + cur->second->dump(out, indent+6); } } diff --git a/vhdlpp/entity.h b/vhdlpp/entity.h index 44f91e815..c56f10d84 100644 --- a/vhdlpp/entity.h +++ b/vhdlpp/entity.h @@ -63,7 +63,7 @@ class ComponentBase : public LineInfo { void set_interface(std::list*ports); public: - void dump_ports(ostream&out) const; + void dump_ports(ostream&out, int indent = 0) const; protected: // This is really only used by the Entity derived class. @@ -94,7 +94,7 @@ class Entity : public ComponentBase { int elaborate(); int emit(ostream&out); - void dump(ostream&out) const; + void dump(ostream&out, int indent = 0) const; private: std::maparch_; diff --git a/vhdlpp/expression.h b/vhdlpp/expression.h index 009525589..cf5ae5fdd 100644 --- a/vhdlpp/expression.h +++ b/vhdlpp/expression.h @@ -56,7 +56,7 @@ class Expression : public LineInfo { virtual bool is_primary(void) const; // Debug dump of the expression. - virtual void dump(ostream&out, int indent) const =0; + virtual void dump(ostream&out, int indent = 0) const =0; private: @@ -73,7 +73,7 @@ class ExpUnary : public Expression { protected: int emit_operand1(ostream&out, Entity*ent, Architecture*arc); - void dump_operand1(ostream&out, int indent) const; + void dump_operand1(ostream&out, int indent = 0) const; private: Expression*operand1_; @@ -94,7 +94,7 @@ class ExpBinary : public Expression { int emit_operand1(ostream&out, Entity*ent, Architecture*arc); int emit_operand2(ostream&out, Entity*ent, Architecture*arc); - void dump_operands(ostream&out, int indent) const; + void dump_operands(ostream&out, int indent = 0) const; private: Expression*operand1_; @@ -111,7 +111,7 @@ class ExpArithmetic : public ExpBinary { ~ExpArithmetic(); int emit(ostream&out, Entity*ent, Architecture*arc); - void dump(ostream&out, int indent) const; + void dump(ostream&out, int indent = 0) const; private: fun_t fun_; @@ -126,7 +126,7 @@ class ExpInteger : public Expression { int emit(ostream&out, Entity*ent, Architecture*arc); bool is_primary(void) const; bool evaluate(int64_t&val) const; - void dump(ostream&out, int indent) const; + void dump(ostream&out, int indent = 0) const; private: int64_t value_; @@ -142,7 +142,7 @@ class ExpLogical : public ExpBinary { ~ExpLogical(); int emit(ostream&out, Entity*ent, Architecture*arc); - void dump(ostream&out, int indent) const; + void dump(ostream&out, int indent = 0) const; private: fun_t fun_; @@ -162,7 +162,7 @@ class ExpName : public Expression { public: // Base methods int emit(ostream&out, Entity*ent, Architecture*arc); bool is_primary(void) const; - void dump(ostream&out, int indent) const; + void dump(ostream&out, int indent = 0) const; const char* name() const; private: @@ -177,7 +177,7 @@ class ExpUAbs : public ExpUnary { ~ExpUAbs(); int emit(ostream&out, Entity*ent, Architecture*arc); - void dump(ostream&out, int indent) const; + void dump(ostream&out, int indent = 0) const; }; class ExpUNot : public ExpUnary { @@ -187,7 +187,7 @@ class ExpUNot : public ExpUnary { ~ExpUNot(); int emit(ostream&out, Entity*ent, Architecture*arc); - void dump(ostream&out, int indent) const; + void dump(ostream&out, int indent = 0) const; }; #endif diff --git a/vhdlpp/vsignal.h b/vhdlpp/vsignal.h index 32109e500..0a604db6f 100644 --- a/vhdlpp/vsignal.h +++ b/vhdlpp/vsignal.h @@ -34,7 +34,7 @@ class Signal : public LineInfo { int emit(ostream&out, Entity*ent, Architecture*arc); - void dump(ostream&out) const; + void dump(ostream&out, int indent = 0) const; private: perm_string name_; diff --git a/vpi/Makefile.in b/vpi/Makefile.in index 166c8169e..31a6e54c7 100644 --- a/vpi/Makefile.in +++ b/vpi/Makefile.in @@ -53,10 +53,10 @@ LDFLAGS = @LDFLAGS@ # Object files for system.vpi 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 sys_priv.o sdf_lexor.o \ - sdf_parse.o stringheap.o vams_simparam.o + sys_finish.o sys_icarus.o sys_plusargs.o sys_queue.o sys_random.o \ + sys_random_mti.o sys_readmem.o sys_readmem_lex.o sys_scanf.o sys_sdf.o \ + sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o mt19937int.o sys_priv.o \ + sdf_lexor.o sdf_parse.o stringheap.o vams_simparam.o OPP = vcd_priv2.o ifeq (@HAVE_LIBZ@,yes) @@ -118,12 +118,12 @@ system.vpi: $O $(OPP) ../vvp/libvpi.a $(CXX) @shared@ -o $@ $O $(OPP) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) sys_readmem_lex.c: sys_readmem_lex.lex - flex -t -Preadmem $(srcdir)/sys_readmem_lex.lex > sys_readmem_lex.c + $(LEX) -t -Preadmem $(srcdir)/sys_readmem_lex.lex > sys_readmem_lex.c sdf_lexor.o: sdf_lexor.c sdf_parse.h sdf_lexor.c: sdf_lexor.lex - flex -t -Psdf $(srcdir)/sdf_lexor.lex > sdf_lexor.c + $(LEX) -t -Psdf $(srcdir)/sdf_lexor.lex > sdf_lexor.c sdf_parse.c sdf_parse.h: $(srcdir)/sdf_parse.y $(YACC) --verbose -d -p sdf -o sdf_parse.c $(srcdir)/sdf_parse.y diff --git a/vpi/sys_icarus.c b/vpi/sys_icarus.c index 5d6d61f12..58e290dc6 100644 --- a/vpi/sys_icarus.c +++ b/vpi/sys_icarus.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2010 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2008-2011 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 @@ -190,31 +190,6 @@ void sys_special_register(void) res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); - tf_data.tfname = "$q_initialize"; - tf_data.user_data = "$q_initialize"; - res = vpi_register_systf(&tf_data); - vpip_make_systf_system_defined(res); - - tf_data.tfname = "$q_add"; - tf_data.user_data = "$q_add"; - res = vpi_register_systf(&tf_data); - vpip_make_systf_system_defined(res); - - tf_data.tfname = "$q_remove"; - tf_data.user_data = "$q_remove"; - res = vpi_register_systf(&tf_data); - vpip_make_systf_system_defined(res); - - tf_data.tfname = "$q_full"; - tf_data.user_data = "$q_full"; - res = vpi_register_systf(&tf_data); - vpip_make_systf_system_defined(res); - - tf_data.tfname = "$q_exam"; - tf_data.user_data = "$q_exam"; - res = vpi_register_systf(&tf_data); - vpip_make_systf_system_defined(res); - tf_data.tfname = "$dumpports"; tf_data.user_data = "$dumpports"; res = vpi_register_systf(&tf_data); diff --git a/vpi/sys_queue.c b/vpi/sys_queue.c new file mode 100644 index 000000000..44f21f9f8 --- /dev/null +++ b/vpi/sys_queue.c @@ -0,0 +1,1357 @@ +/* + * Copyright (C) 2011 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 "sys_priv.h" +#include +#include +#include + +/* + * The two queue types. + */ +#define IVL_QUEUE_FIFO 1 +#define IVL_QUEUE_LIFO 2 + +/* + * The statistical codes that can be passed to $q_exam(). + */ +#define IVL_QUEUE_LENGTH 1 +#define IVL_QUEUE_MEAN 2 +#define IVL_QUEUE_MAX_LENGTH 3 +#define IVL_QUEUE_SHORTEST 4 +#define IVL_QUEUE_LONGEST 5 +#define IVL_QUEUE_AVERAGE 6 + +/* + * All the values that can be returned by the queue tasks/function. + */ +#define IVL_QUEUE_OK 0 +#define IVL_QUEUE_FULL 1 +#define IVL_QUEUE_UNDEFINED_ID 2 +#define IVL_QUEUE_EMPTY 3 +#define IVL_QUEUE_UNSUPPORTED_TYPE 4 +#define IVL_QUEUE_INVALID_LENGTH 5 +#define IVL_QUEUE_DUPLICATE_ID 6 +#define IVL_QUEUE_OUT_OF_MEMORY 7 +/* Icarus specific status codes. */ +#define IVL_QUEUE_UNDEFINED_STAT_CODE 8 +#define IVL_QUEUE_VALUE_OVERFLOWED 9 +#define IVL_QUEUE_NO_STATISTICS 10 + +/* + * The data structure used for an individual queue element. It hold four + * state result for the jobs and inform fields along with the time that + * the element was added in base time units. + */ +typedef struct t_ivl_queue_elem { + uint64_t time; + s_vpi_vecval job; + s_vpi_vecval inform; +} s_ivl_queue_elem, *p_ivl_queue_elem; + +/* + * This structure is used to represent a specific queue. The time + * information is in base simulation units. + */ +typedef struct t_ivl_queue_base { + uint64_t shortest_wait_time; + uint64_t first_add_time; + uint64_t latest_add_time; + uint64_t number_of_adds; +// HERE: Still need information for the average wait time statistic. + p_ivl_queue_elem queue; + PLI_INT32 id; + PLI_INT32 length; + PLI_INT32 type; + PLI_INT32 head; + PLI_INT32 elems; + PLI_INT32 max_len; + PLI_INT32 have_statistics; +} s_ivl_queue_base, *p_ivl_queue_base; + +/* + * For now we keep the queues in a vector since there are likely not too many + * of them. We may need something more efficient later. + */ +static p_ivl_queue_base base = NULL; +static int64_t base_len = 0; + +/* + * This routine is called at the end of simulation to free the queue memory. + */ +static PLI_INT32 cleanup_queue(p_cb_data cause) +{ + PLI_INT32 idx; + (void) cause; /* Unused argument. */ + for (idx = 0; idx < base_len; idx += 1) free(base[idx].queue); + free(base); + base = NULL; + base_len = 0; + return 0; +} + +/* + * Add a new queue to the list, return 1 if there is not enough memory, + * otherwise return 0. + */ +static unsigned create_queue(PLI_INT32 id, PLI_INT32 type, PLI_INT32 length) +{ + p_ivl_queue_base new_base; + p_ivl_queue_elem queue; + + /* Allocate space for the new queue base. */ + base_len += 1; + new_base = (p_ivl_queue_base) realloc(base, + base_len*sizeof(s_ivl_queue_base)); + + /* If we ran out of memory then fix the length and return a fail. */ + if (new_base == NULL) { + base_len -= 1; + return 1; + } + base = new_base; + + /* Allocate space for the queue elements. */ + queue = (p_ivl_queue_elem) malloc(length*sizeof(s_ivl_queue_elem)); + + /* If we ran out of memory then fix the length and return a fail. */ + if (queue == NULL) { + base_len -= 1; + return 1; + } + + /* The memory was allocated so configure it. */ + base[base_len-1].queue = queue; + base[base_len-1].id = id; + base[base_len-1].length = length; + base[base_len-1].type = type; + base[base_len-1].head = 0; + base[base_len-1].elems = 0; + base[base_len-1].max_len = 0; + base[base_len-1].shortest_wait_time = UINT64_MAX; + base[base_len-1].first_add_time = 0U; + base[base_len-1].latest_add_time = 0U; + base[base_len-1].number_of_adds = 0U; + base[base_len-1].have_statistics = 0; + return 0; +} + +/* + * Check to see if the given queue is full. + */ +static unsigned is_queue_full(int64_t idx) +{ + if (base[idx].elems >= base[idx].length) return 1; + + return 0; +} + +/* + * Add the job and inform to the queue. Return 1 if the queue is full, + * otherwise return 0. + */ +static unsigned add_to_queue(int64_t idx, p_vpi_vecval job, + p_vpi_vecval inform) +{ + PLI_INT32 length = base[idx].length; + PLI_INT32 type = base[idx].type; + PLI_INT32 head = base[idx].head; + PLI_INT32 elems = base[idx].elems; + PLI_INT32 loc; + s_vpi_time cur_time; + uint64_t time; + + assert(elems <= length); + + /* If the queue is full we can't add anything. */ + if (elems == length) return 1; + + /* Increment the number of element since one will be added.*/ + base[idx].elems += 1; + + /* Save the job and inform to the queue. */ + if (type == IVL_QUEUE_LIFO) { + assert(head == 0); /* For a LIFO head must always be zero. */ + loc = elems; + } else { + assert(type == IVL_QUEUE_FIFO); + loc = head + elems; + if (loc >= length) loc -= length; + } + base[idx].queue[loc].job.aval = job->aval; + base[idx].queue[loc].job.bval = job->bval; + base[idx].queue[loc].inform.aval = inform->aval; + base[idx].queue[loc].inform.bval = inform->bval; + + /* Save the current time with this entry for the statistics. */ + cur_time.type = vpiSimTime; + vpi_get_time(NULL, &cur_time); + time = cur_time.high; + time <<= 32; + time |= cur_time.low; + base[idx].queue[loc].time = time; + + /* Increment the maximum length if needed. */ + if (base[idx].max_len == elems) base[idx].max_len += 1; + + /* Update the inter-arrivial statistics. */ + assert(base[idx].number_of_adds < UINT64_MAX); + base[idx].number_of_adds += 1; + if (base[idx].number_of_adds == 1) base[idx].first_add_time = time; + base[idx].latest_add_time = time; + + return 0; +} + +/* + * Get the job and inform values from the queue. Return 1 if the queue is + * empty, otherwise return 0. + */ +static unsigned remove_from_queue(int64_t idx, p_vpi_vecval job, + p_vpi_vecval inform) +{ + PLI_INT32 type = base[idx].type; + PLI_INT32 head = base[idx].head; + PLI_INT32 elems = base[idx].elems - 1; + PLI_INT32 loc; + s_vpi_time cur_time; + uint64_t time; + + assert(elems >= -1); + + /* If the queue is empty we can't remove anything. */ + if (elems < 0) return 1; + + /* Decrement the number of element in the queue structure since one + * will be removed.*/ + base[idx].elems -= 1; + + /* Remove the job and inform from the queue. */ + if (type == IVL_QUEUE_LIFO) { + assert(head == 0); /* For a LIFO head must always be zero. */ + loc = elems; + } else { + assert(type == IVL_QUEUE_FIFO); + loc = head; + if (head + 1 == base[idx].length) base[idx].head = 0; + else base[idx].head += 1; + } + job->aval = base[idx].queue[loc].job.aval; + job->bval = base[idx].queue[loc].job.bval; + inform->aval = base[idx].queue[loc].inform.aval; + inform->bval = base[idx].queue[loc].inform.bval; + + /* Get the current simulation time. */ + cur_time.type = vpiSimTime; + vpi_get_time(NULL, &cur_time); + time = cur_time.high; + time <<= 32; + time |= cur_time.low; + + /* Set the shortest wait time if needed. */ + assert(time >= base[idx].queue[loc].time); + time -= base[idx].queue[loc].time; + if (time < base[idx].shortest_wait_time) { + base[idx].shortest_wait_time = time; + } + base[idx].have_statistics = 1; + + return 0; +} + +/* + * Return the current queue length. + */ +static PLI_INT32 get_current_queue_length(int64_t idx) +{ + return base[idx].elems; +} + +/* + * Return the maximum queue length. + */ +static PLI_INT32 get_maximum_queue_length(int64_t idx) +{ + return base[idx].max_len; +} + +/* + * Return the longest wait time in the queue in base simulation units. + * Make sure to check that there are elements in the queue before calling + * this routine. The caller will need to scale the time as appropriate. + */ +static uint64_t get_longest_queue_time(int64_t idx) +{ + s_vpi_time cur_time; + uint64_t time; + + /* Get the current simulation time. */ + cur_time.type = vpiSimTime; + vpi_get_time(NULL, &cur_time); + time = cur_time.high; + time <<= 32; + time |= cur_time.low; + + /* Subtract the element with the longest time (the head) from the + * current time. */ + time -= base[idx].queue[base[idx].head].time; + + return time; +} + +/* + * Check to see if there are statistics. + */ +static unsigned have_statistics(int64_t idx) +{ + return (base[idx].have_statistics != 0); +} + +/* + * Check to see if we have inter-arrival statistics. + */ +static unsigned have_interarrival_statistic(int64_t idx) +{ + return (base[idx].number_of_adds >= 2U); +} + +/* + * Return the mean inter-arrival time for the queue. This is just the + * latest add time minus the first add time divided be the number of time + * deltas (the number of adds - 1). + */ +static uint64_t get_mean_interarrival_time(int64_t idx) +{ + return ((base[idx].latest_add_time - base[idx].first_add_time) / + (base[idx].number_of_adds - 1U)); +} + +/* + * Return the shortest amount of time an element has waited in the queue. + */ +static uint64_t get_shortest_wait_time(int64_t idx) +{ + return base[idx].shortest_wait_time; +} + +/* + * Return the average wait time in the queue. + */ +#if 0 +static uint64_t get_average_wait_time(int64_t idx) +{ +// HERE: Need to save the information and calculate the average wait time. + return 0; +} +#endif + +/* + * Check to see if the given id already exists. Return the index for the + * queue if it exists, otherwise return -1. + */ +static int64_t get_id_index(PLI_INT32 id) +{ + int64_t idx; + + for (idx = 0; idx < base_len; idx += 1) { + if (id == base[idx].id) return idx; + } + + return -1; +} + +/* + * Check to see if the given value is bit based and has 32 or fewer bits. + */ +static unsigned is_32_or_smaller_obj(vpiHandle obj) +{ + PLI_INT32 const_type; + unsigned rtn = 0; + + assert(obj); + + switch(vpi_get(vpiType, obj)) { + case vpiConstant: + case vpiParameter: + const_type = vpi_get(vpiConstType, obj); + if ((const_type != vpiRealConst) && + (const_type != vpiStringConst)) rtn = 1; + break; + + /* These can have valid 32 bit or smaller numeric values. */ + case vpiIntegerVar: + case vpiMemoryWord: + case vpiNet: + case vpiPartSelect: + case vpiReg: + rtn = 1; + break; + } + + /* The object must be 32 bits or smaller. */ + if (vpi_get(vpiSize, obj) > 32) rtn = 0; + + return rtn; +} + +/* + * Check to see if the argument is a variable that is exactly 32 bits in size. + */ +static void check_var_arg_32(vpiHandle arg, vpiHandle callh, + const char *name, const char *desc) +{ + assert(arg); + + switch (vpi_get(vpiType, arg)) { + case vpiMemoryWord: + case vpiPartSelect: + case vpiReg: // Check that we have exactly 32 bits. + if (vpi_get(vpiSize, arg) != 32) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s (variable) argument must be 32 bits.\n", + name, desc); + vpi_control(vpiFinish, 1); + } + case vpiIntegerVar: + break; + default: + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s argument must be a 32 bit variable.\n", + name, desc); + vpi_control(vpiFinish, 1); + } +} + +/* + * Check to see if the argument is a variable of atleast 32 bits. + */ +static void check_var_arg_large(vpiHandle arg, vpiHandle callh, + const char *name, const char *desc) +{ + assert(arg); + + switch (vpi_get(vpiType, arg)) { + case vpiMemoryWord: + case vpiPartSelect: + case vpiReg: // Check that we have at least 32 bits. + if (vpi_get(vpiSize, arg) < 32) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s (variable) argument must have at least " + "32 bits.\n", name, desc); + vpi_control(vpiFinish, 1); + } + case vpiIntegerVar: + case vpiTimeVar: + break; + default: + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s argument must be a variable.\n", name, desc); + vpi_control(vpiFinish, 1); + } +} + +/* + * Check that the given number of arguments are numeric. + */ +static unsigned check_numeric_args(vpiHandle argv, unsigned count, + vpiHandle callh, const char *name) +{ + unsigned idx; + + /* Check that the first count arguments are numeric. Currently + * only three are needed/supported. */ + for (idx = 0; idx < count; idx += 1) { + char *loc; + vpiHandle arg = vpi_scan(argv); + + /* Get the name for this argument. */ + switch (idx) { + case 0: loc = "first"; break; + case 1: loc = "second"; break; + case 2: loc = "third"; break; + default: assert(0); + } + + /* Check that there actually is an argument. */ + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a %s (<= 32 bit numeric) argument.\n", + name, loc); + vpi_control(vpiFinish, 1); + return 1; + } + + /* Check that it is no more than 32 bits. */ + if (! is_32_or_smaller_obj(arg)) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s argument must be numeric (<= 32 bits).\n", + name, loc); + vpi_control(vpiFinish, 1); + } + } + + return 0; +} + +/* + * Check to see if the given argument is valid (does not have any X/Z bits). + * Return zero if it is valid and a positive value if it is invalid. + */ +static unsigned get_valid_32(vpiHandle arg, PLI_INT32 *value) +{ + PLI_INT32 size, mask; + s_vpi_value val; + + size = vpi_get(vpiSize, arg); + /* The compiletf routine should have already verified that this is + * <= 32 bits. */ + assert((size <= 32) && (size > 0)); + + /* Create a mask so that we only check the appropriate bits. */ + mask = UINT32_MAX >> (32 - size); + + /* Get the value and return the possible integer value in the value + * variable. Return the b-value bits to indicate if the value is + * undefined (has X/Z bit). */ + val.format = vpiVectorVal; + vpi_get_value(arg, &val); + + *value = val.value.vector->aval & mask; + /* If the argument is signed and less than 32 bit we need to sign + * extend the value. */ + if (vpi_get(vpiSigned, arg) && (size < 32)) { + if ((*value) & (1 << (size - 1))) *value |= ~mask; + } + return (val.value.vector->bval & mask); +} + +static void get_four_state(vpiHandle arg, p_vpi_vecval vec) +{ + PLI_INT32 size, mask; + s_vpi_value val; + + size = vpi_get(vpiSize, arg); + /* The compiletf routine should have already verified that this is + * <= 32 bits. */ + assert((size <= 32) && (size > 0)); + + /* Create a mask so that we only use the appropriate bits. */ + mask = UINT32_MAX >> (32 - size); + + /* Get the bits for the argument and save them in the return value. */ + val.format = vpiVectorVal; + vpi_get_value(arg, &val); + vec->aval = val.value.vector->aval & mask; + vec->bval = val.value.vector->bval & mask; + + /* If the argument is signed and less than 32 bit we need to sign + * extend the value. */ + if (vpi_get(vpiSigned, arg) && (size < 32)) { + if (vec->aval & (1 << (size - 1))) vec->aval |= ~mask; + if (vec->bval & (1 << (size - 1))) vec->bval |= ~mask; + } +} + +/* + * Fill the passed variable with x. + */ +static void fill_variable_with_x(vpiHandle var) +{ + s_vpi_value val; + PLI_INT32 words = ((vpi_get(vpiSize, var) - 1) / 32) + 1; + PLI_INT32 idx; + p_vpi_vecval val_ptr = (p_vpi_vecval) malloc(words*sizeof(s_vpi_vecval)); + + assert(val_ptr); + + /* Fill the vector with X. */ + for (idx = 0; idx < words; idx += 1) { + val_ptr[idx].aval = 0xffffffff; + val_ptr[idx].bval = 0xffffffff; + } + + /* Put the vector to the variable. */ + val.format = vpiVectorVal; + val.value.vector = val_ptr; + vpi_put_value(var, &val, 0, vpiNoDelay); + free(val_ptr); +} + +/* + * Fill the passed variable with the passed value if it fits. If it doesn't + * fit then set all bits to one and return that the value is too big instead + * of the normal OK. The value is a time and needs to be scaled to the + * calling module's timescale. + */ +static PLI_INT32 fill_variable_with_scaled_time(vpiHandle var, uint64_t time) +{ + s_vpi_value val; + PLI_INT32 size = vpi_get(vpiSize, var); + PLI_INT32 is_signed = vpi_get(vpiSigned, var); + PLI_INT32 words = ((size - 1) / 32) + 1; + uint64_t max_val = 0; + uint64_t scale = 1; + uint64_t frac; + PLI_INT32 rtn, idx, units, prec; + p_vpi_vecval val_ptr = (p_vpi_vecval) malloc(words*sizeof(s_vpi_vecval)); + + assert(val_ptr); + assert(size >= 32); + assert(words > 0); + + /* Scale the variable to match the calling module's timescale. */ + prec = vpi_get(vpiTimePrecision, 0); + units = vpi_get(vpiTimeUnit, vpi_handle(vpiModule, var)); + assert(units >= prec); + while (units > prec) { + scale *= 10; + units -= 1; + } + frac = time % scale; + time /= scale; + if ((scale > 1) && (frac >= scale/2)) time += 1; + + /* Find the maximum value + 1 that can be put into the variable. */ + if (size < 64) { + max_val = 1; + max_val <<= (size - is_signed); + } + + /* If the time is too big to fit then return the maximum positive + * value and that the value overflowed. Otherwise, return the time + * and OK. */ + if (max_val && (time >= max_val)) { + /* For a single word only the MSB is cleared if signed. */ + if (words == 1) { + if (is_signed) { + val_ptr[0].aval = 0x7fffffff; + } else { + val_ptr[0].aval = 0xffffffff; + } + val_ptr[0].bval = 0x00000000; + /* For two words the lower word is filled with 1 and the top + * word has a size dependet fill if signed. */ + } else { + assert(words == 2); + val_ptr[0].aval = 0xffffffff; + val_ptr[0].bval = 0x00000000; + if (is_signed) { + val_ptr[1].aval = ~(UINT32_MAX >> (size - 32)); + } else { + val_ptr[1].aval = 0xffffffff; + } + val_ptr[1].bval = 0x00000000; + } + rtn = IVL_QUEUE_VALUE_OVERFLOWED; + } else { + /* Fill the vector with 0. */ + for (idx = 0; idx < words; idx += 1) { + val_ptr[idx].aval = 0x00000000; + val_ptr[idx].bval = 0x00000000; + } + /* Add the time to the vector. */ + switch (words) { + default: + val_ptr[1].aval = (time >> 32) & 0xffffffff; + case 1: + val_ptr[0].aval = time & 0xffffffff; + } + rtn = IVL_QUEUE_OK; + } + + /* Put the vector to the variable. */ + val.format = vpiVectorVal; + val.value.vector = val_ptr; + vpi_put_value(var, &val, 0, vpiNoDelay); + free(val_ptr); + + return rtn; +} + +/* + * Check that the given $q_initialize() call has valid arguments. + */ +static PLI_INT32 sys_q_initialize_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires four arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* Check that the first three arguments (the id, type and maximum + * length) are numeric. */ + if (check_numeric_args(argv, 3, callh, name)) return 0; + + /* The fourth argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a fourth (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "fourth"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "four arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_initialize(). + */ +static PLI_INT32 sys_q_initialize_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle status; + PLI_INT32 id, type, length; + s_vpi_value val; + unsigned invalid_id, invalid_type, invalid_length; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the queue type. */ + invalid_type = get_valid_32(vpi_scan(argv), &type); + + /* Get the queue maximum length. */ + invalid_length = get_valid_32(vpi_scan(argv), &length); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* If the id is invalid then return. */ + if (invalid_id) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Verify that the type is valid. */ + if (invalid_type || ((type != IVL_QUEUE_FIFO) && + (type != IVL_QUEUE_LIFO))) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNSUPPORTED_TYPE; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Verify that the queue length is greater than zero. */ + if (invalid_length || (length <= 0)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_INVALID_LENGTH; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Check that this is not a duplicate queue id. */ + if (get_id_index(id) >= 0) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_DUPLICATE_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Create the queue and fail if we do not have enough memory. */ + if (create_queue(id, type, length)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OUT_OF_MEMORY; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* The queue was initialized correctly so return OK. */ + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OK; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Check that the given $q_add() call has valid arguments. + */ +static PLI_INT32 sys_q_add_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires four arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* Check that the first three arguments (the id, job and information) + * are numeric. */ + if (check_numeric_args(argv, 3, callh, name)) return 0; + + /* The fourth argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a fourth (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "fourth"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "four arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_add(). + */ +static PLI_INT32 sys_q_add_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle status; + PLI_INT32 id; + int64_t idx; + s_vpi_vecval job, inform; + s_vpi_value val; + unsigned invalid_id; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the job. */ + get_four_state(vpi_scan(argv), &job); + + /* Get the value. */ + get_four_state(vpi_scan(argv), &inform); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* Verify that the id is valid. */ + idx = get_id_index(id); + if (invalid_id || (idx < 0)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Add the data to the queue if it is not already full. */ + if (add_to_queue(idx, &job, &inform)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_FULL; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* The data was added to the queue so return OK. */ + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OK; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Check that the given $q_remove() call has valid arguments. + */ +static PLI_INT32 sys_q_remove_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires four arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* The first argument (the id) must be numeric. */ + if (! is_32_or_smaller_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's first argument must be numeric (<= 32 bits).\n", + name); + vpi_control(vpiFinish, 1); + } + + /* The second argument (the job id) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a second (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the job id argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "second"); + + /* The third argument (the information id) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a third (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the information id argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "third"); + + /* The fourth argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a fourth (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "fourth"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "four arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_remove(). + */ +static PLI_INT32 sys_q_remove_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle job, inform, status; + PLI_INT32 id, idx; + s_vpi_vecval job_val, inform_val; + s_vpi_value val; + unsigned invalid_id; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the job variable. */ + job = vpi_scan(argv); + + /* Get the inform variable. */ + inform = vpi_scan(argv); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* Verify that the id is valid. */ + idx = get_id_index(id); + if (invalid_id || (idx < 0)) { + fill_variable_with_x(job); + fill_variable_with_x(inform); + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Remove the data from the queue if it is not already empty. */ + if (remove_from_queue(idx, &job_val, &inform_val)) { + fill_variable_with_x(job); + fill_variable_with_x(inform); + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_EMPTY; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + val.format = vpiVectorVal; + val.value.vector = &job_val; + vpi_put_value(job, &val, 0, vpiNoDelay); + val.format = vpiVectorVal; + val.value.vector = &inform_val; + vpi_put_value(inform, &val, 0, vpiNoDelay); + + /* The data was added to the queue so return OK. */ + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OK; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Check that the given $q_full() call has valid arguments. + */ +static PLI_INT32 sys_q_full_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%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; + } + + /* The first argument (the id) must be numeric. */ + if (! is_32_or_smaller_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's first argument must be numeric (<= 32 bits).\n", + name); + vpi_control(vpiFinish, 1); + } + + /* The second argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a second (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "second"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "two arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_full(). + */ +static PLI_INT32 sys_q_full_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle status; + PLI_INT32 id, idx; + s_vpi_value val; + unsigned invalid_id; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* Verify that the id is valid. */ + idx = get_id_index(id); + if (invalid_id || (idx < 0)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + val.format = vpiIntVal; + val.value.integer = 2; /* An error value. */ + vpi_put_value(callh, &val, 0, vpiNoDelay); + return 0; + } + + /* Get the queue state and return it. */ + val.format = vpiIntVal; + if (is_queue_full(idx)) val.value.integer = 1; + else val.value.integer = 0; + vpi_put_value(callh, &val, 0, vpiNoDelay); + + /* The queue state was passed back so return OK. */ + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OK; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Check that the given $q_exam() call has valid arguments. + */ +static PLI_INT32 sys_q_exam_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires four arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* Check that the first two arguments (the id and code) are numeric. */ + if (check_numeric_args(argv, 2, callh, name)) return 0; + + /* The third argument (the value) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a third (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the value argument is a variable with at least + * 32 bits. */ + check_var_arg_large(arg, callh, name, "third"); + + /* The fourth argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a fourth (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "fourth"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "two arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_exam(). + */ +static PLI_INT32 sys_q_exam_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle value, status; + PLI_INT32 id, code, idx, rtn; + s_vpi_value val; + unsigned invalid_id, invalid_code; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the code. */ + invalid_code = get_valid_32(vpi_scan(argv), &code); + + /* Get the value variable. */ + value = vpi_scan(argv); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* Verify that the id is valid. */ + idx = get_id_index(id); + if (invalid_id || (idx < 0)) { + fill_variable_with_x(value); + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Verify that the code is valid. */ + if (invalid_code || (code <= 0) || (code > 6)) { + fill_variable_with_x(value); + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_STAT_CODE; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + rtn = IVL_QUEUE_OK; + + /* Calculate the requested queue information. */ + switch (code) { + /* The current queue length. */ + case IVL_QUEUE_LENGTH: + val.format = vpiIntVal; + val.value.integer = get_current_queue_length(idx); + vpi_put_value(value, &val, 0, vpiNoDelay); + break; + /* The mean inter-arrival time. */ + case IVL_QUEUE_MEAN: + if (have_interarrival_statistic(idx) == 0) { + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } else { + uint64_t time = get_mean_interarrival_time(idx); + rtn = fill_variable_with_scaled_time(value, time); + } + break; + /* The maximum queue length. */ + case IVL_QUEUE_MAX_LENGTH: + val.format = vpiIntVal; + val.value.integer = get_maximum_queue_length(idx); + vpi_put_value(value, &val, 0, vpiNoDelay); + break; + /* The shortest queue wait time ever. */ + case IVL_QUEUE_SHORTEST: + if (have_statistics(idx) == 0) { + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } else { + uint64_t time = get_shortest_wait_time(idx); + rtn = fill_variable_with_scaled_time(value, time); + } + break; + /* The longest wait time for elements still in the queue. */ + case IVL_QUEUE_LONGEST: + if (get_current_queue_length(idx) == 0) { + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } else { + uint64_t time = get_longest_queue_time(idx); + rtn = fill_variable_with_scaled_time(value, time); + } + break; + /* The average queue wait time. */ + case IVL_QUEUE_AVERAGE: + if (have_statistics(idx) == 0) { + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } else { +// HERE: For now this is not supported. The get routine always returns 0. +#if 0 + uint64_t time = get_average_wait_time(idx); + rtn = fill_variable_with_scaled_time(value, time); +#endif + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } + break; + default: + assert(0); + } + + /* The queue information was passed back so now return the status. */ + val.format = vpiIntVal; + val.value.integer = rtn; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Routine to register the system tasks/functions provided in this file. + */ +void sys_queue_register() +{ + s_vpi_systf_data tf_data; + s_cb_data cb; + vpiHandle res; + + tf_data.type = vpiSysTask; + tf_data.tfname = "$q_initialize"; + tf_data.calltf = sys_q_initialize_calltf; + tf_data.compiletf = sys_q_initialize_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$q_initialize"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysTask; + tf_data.tfname = "$q_add"; + tf_data.calltf = sys_q_add_calltf; + tf_data.compiletf = sys_q_add_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$q_add"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysTask; + tf_data.tfname = "$q_remove"; + tf_data.calltf = sys_q_remove_calltf; + tf_data.compiletf = sys_q_remove_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$q_remove"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiSysFuncInt; + tf_data.tfname = "$q_full"; + tf_data.calltf = sys_q_full_calltf; + tf_data.compiletf = sys_q_full_compiletf; + tf_data.sizetf = 0; /* Not needed for a vpiSysFuncInt. */ + tf_data.user_data = "$q_full"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysTask; + tf_data.tfname = "$q_exam"; + tf_data.calltf = sys_q_exam_calltf; + tf_data.compiletf = sys_q_exam_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$q_exam"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + /* Create a callback to clear all the queue memory when the + * simulator finishes. */ + cb.time = NULL; + cb.reason = cbEndOfSimulation; + cb.cb_rtn = cleanup_queue; + cb.user_data = 0x0; + cb.obj = 0x0; + + vpi_register_cb(&cb); +} diff --git a/vpi/sys_table.c b/vpi/sys_table.c index 637319e0f..9116d34c6 100644 --- a/vpi/sys_table.c +++ b/vpi/sys_table.c @@ -29,6 +29,7 @@ extern void sys_finish_register(); extern void sys_deposit_register(); extern void sys_display_register(); extern void sys_plusargs_register(); +extern void sys_queue_register(); extern void sys_random_register(); extern void sys_random_mti_register(); extern void sys_readmem_register(); @@ -198,6 +199,7 @@ void (*vlog_startup_routines[])() = { sys_deposit_register, sys_display_register, sys_plusargs_register, + sys_queue_register, sys_random_register, sys_random_mti_register, sys_readmem_register, diff --git a/vpi/system.sft b/vpi/system.sft index f0eb86f6a..b772dc85c 100644 --- a/vpi/system.sft +++ b/vpi/system.sft @@ -15,6 +15,7 @@ $dist_chi_square vpiSysFuncInt $dist_t vpiSysFuncInt $dist_erlang vpiSysFuncInt $clog2 vpiSysFuncInt +$q_full vpiSysFuncInt $abstime vpiSysFuncReal $simparam vpiSysFuncReal diff --git a/vvp/arith.cc b/vvp/arith.cc index 842e58d75..7ab6f3c80 100644 --- a/vvp/arith.cc +++ b/vvp/arith.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 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 @@ -460,11 +460,14 @@ void vvp_arith_pow::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, return; } - double ad, bd; + double ad, bd, resd; vector4_to_value(op_a_, ad, true); vector4_to_value(op_b_, bd, true); + /* 2**-1 and -2**-1 are defined to be zero. */ + if ((bd == -1) && (fabs(ad) == 2.0)) resd = 0.0; + else resd = pow(ad, bd); - res4 = vvp_vector4_t(wid_, pow(ad, bd)); + res4 = vvp_vector4_t(wid_, resd); } else { vvp_vector2_t a2 (op_a_); vvp_vector2_t b2 (op_b_); diff --git a/vvp/vpi_scope.cc b/vvp/vpi_scope.cc index e3449679c..45afce324 100644 --- a/vvp/vpi_scope.cc +++ b/vvp/vpi_scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -81,6 +81,7 @@ static void delete_sub_scopes(struct __vpiScope *scope) case vpiShortIntVar: case vpiIntVar: case vpiByteVar: + case vpiBitVar: signal_delete((scope->intern)[idx]); break; case vpiParameter: diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 73938264a..e4c0b2b22 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -4050,10 +4050,13 @@ bool of_POW_S(vthread_t thr, vvp_code_t cp) } /* Calculate the result using the double pow() function. */ - double xd, yd; + double xd, yd, resd; vector4_to_value(xv, xd, true); vector4_to_value(yv, yd, true); - vvp_vector4_t res = vvp_vector4_t(wid, pow(xd, yd)); + /* 2**-1 and -2**-1 are defined to be zero. */ + if ((yd == -1.0) && (fabs(xd) == 2.0)) resd = 0.0; + else resd = pow(xd, yd); + vvp_vector4_t res = vvp_vector4_t(wid, resd); /* Copy the result. */ for (unsigned jdx = 0; jdx < wid; jdx += 1)