diff --git a/PDelays.cc b/PDelays.cc index e39a57122..3fd3ce710 100644 --- a/PDelays.cc +++ b/PDelays.cc @@ -109,7 +109,7 @@ static NetExpr* make_delay_nets(Design*des, NetScope*scope, NetExpr*expr) if (dynamic_cast (expr)) return expr; - NetNet*sig = expr->synthesize(des, scope); + NetNet*sig = expr->synthesize(des, scope, expr); if (sig == 0) { cerr << expr->get_fileline() << ": error: Expression " << *expr << " is not suitable for delay expression." << endl; diff --git a/compiler.h b/compiler.h index 2caed586c..a4cc0a0fb 100644 --- a/compiler.h +++ b/compiler.h @@ -88,7 +88,6 @@ extern bool debug_elaborate; extern bool debug_elab_pexpr; extern bool debug_synth2; extern bool debug_optimizer; -extern bool debug_automatic; /* Path to a directory useful for finding subcomponents. */ extern const char*basedir; @@ -126,6 +125,11 @@ extern bool gn_verilog_ams_flag; is scalar and the net/register definition is vectored. */ extern bool gn_io_range_error_flag; +/* If this flag is true, then force re-evaluation of user functions + in a continuous assignment when any part of the expression is + re-evaluated. */ +extern bool gn_strict_ca_eval_flag; + /* The bits of these GN_KEYWORDS_* constants define non-intersecting sets of keywords. The compiler enables groups of keywords by setting lexor_keyword_mask with the OR of the bits for the keywords to be diff --git a/driver/iverilog.man b/driver/iverilog.man index 57d9e5ab1..bbf275b1c 100644 --- a/driver/iverilog.man +++ b/driver/iverilog.man @@ -97,6 +97,16 @@ reported as a error. Using \fI-gno-io-range-error\fP will produce a warning instead of a fatal error for the case of a vectored net/register and a scalar port declaration. .TP 8 +.B -gstrict-ca-eval\fI|\fP-gno-strict-ca-eval +The standard requires that if any input to a continuous assignment +expression changes value, the entire expression is re-evaluated. By +default, parts of the expression that do not depend on the changed +input value(s) are not re-evaluated. If an expression contains a call +to a function that doesn't depend solely on its input values or that +has side effects, the resulting behaviour will differ from that +required by the standard. Using \fI-gstrict-ca-eval\fP will force +standard compliant behaviour (with some loss in performance). +.TP 8 .B -I\fIincludedir\fP Append directory \fIincludedir\fP to list of directories searched for Verilog include files. The \fB-I\fP switch may be used many times diff --git a/driver/main.c b/driver/main.c index a2b15e167..892f0a723 100644 --- a/driver/main.c +++ b/driver/main.c @@ -37,7 +37,8 @@ const char NOTICE[] = ; const char HELP[] = -"Usage: iverilog [-ESvV] [-B base] [-c cmdfile|-f cmdfile] [-g1|-g2|-g2x]\n" +"Usage: iverilog [-ESvV] [-B base] [-c cmdfile|-f cmdfile]\n" +" [-g1995|-g2001|-g2005] [-g]\n" " [-D macro[=defn]] [-I includedir] [-M depfile] [-m module]\n" " [-N file] [-o filename] [-p flag=value]\n" " [-s topmodule] [-t target] [-T min|typ|max]\n" @@ -112,6 +113,7 @@ const char*gen_specify = "no-specify"; const char*gen_xtypes = "xtypes"; const char*gen_icarus = "icarus-misc"; const char*gen_io_range_error = "io-range-error"; +const char*gen_strict_ca_eval = "no-strict-ca-eval"; const char*gen_verilog_ams = "no-verilog-ams"; /* Boolean: true means use a default include dir, false means don't */ @@ -598,6 +600,12 @@ int process_generation(const char*name) else if (strcmp(name,"no-io-range-error") == 0) gen_io_range_error = "no-io-range-error"; + else if (strcmp(name,"strict-ca-eval") == 0) + gen_strict_ca_eval = "strict-ca-eval"; + + else if (strcmp(name,"no-strict-ca-eval") == 0) + gen_strict_ca_eval = "no-strict-ca-eval"; + else if (strcmp(name,"verilog-ams") == 0) gen_verilog_ams = "verilog-ams"; @@ -617,7 +625,8 @@ int process_generation(const char*name) " std-include | no-std-include\n" " xtypes | no-xtypes\n" " icarus-misc | no-icarus-misc\n" - " io-range-error | no-io-range-error\n"); + " io-range-error | no-io-range-error\n" + " strict-ca-eval | no-strict-ca-eval\n"); return 1; } @@ -887,6 +896,7 @@ int main(int argc, char **argv) fprintf(iconfig_file, "generation:%s\n", gen_specify); fprintf(iconfig_file, "generation:%s\n", gen_xtypes); fprintf(iconfig_file, "generation:%s\n", gen_io_range_error); + fprintf(iconfig_file, "generation:%s\n", gen_strict_ca_eval); fprintf(iconfig_file, "generation:%s\n", gen_verilog_ams); fprintf(iconfig_file, "generation:%s\n", gen_icarus); fprintf(iconfig_file, "warnings:%s\n", warning_flags); diff --git a/elaborate.cc b/elaborate.cc index 61a2a6ef9..1ec20a887 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -122,7 +122,7 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const rval_expr = pad_to_width(rval_expr, lval->vector_width(), *this); } - NetNet*rval = rval_expr->synthesize(des, scope); + NetNet*rval = rval_expr->synthesize(des, scope, rval_expr); if (rval == 0) { cerr << get_fileline() << ": internal error: " @@ -731,7 +731,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const ex->test_width(des, scope, 0, use_width, tmp_type, flag); NetExpr*tmp = elab_and_eval(des, scope, ex, use_width, use_width); - sig = tmp->synthesize(des, scope); + sig = tmp->synthesize(des, scope, tmp); delete tmp; } @@ -1204,7 +1204,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const << "too complicated for elaboration." << endl; continue; } - sig = tmp_expr->synthesize(des, scope); + sig = tmp_expr->synthesize(des, scope, tmp_expr); if (sig == 0) { cerr << pins[idx]->get_fileline() << ": internal error: Port expression " @@ -1674,7 +1674,7 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const "for elaboration:" << pins[idx] << endl; continue; } - NetNet*sig = expr_tmp->synthesize(des, scope); + NetNet*sig = expr_tmp->synthesize(des, scope, expr_tmp); ivl_assert(*this, sig); sig->set_line(*this); @@ -2984,7 +2984,7 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope, continue; } - NetNet*expr = tmp->synthesize(des, scope); + NetNet*expr = tmp->synthesize(des, scope, tmp); expr->set_line(*this); if (expr == 0) { expr_[idx]->dump(cerr); @@ -3754,7 +3754,7 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const // FIXME: Look for constant expressions here? // Get a net form. - condit_sig = tmp->synthesize(des, scope); + condit_sig = tmp->synthesize(des, scope, tmp); ivl_assert(*condition, condit_sig); } diff --git a/expr_synth.cc b/expr_synth.cc index 3ac8ce9f0..bf1c44879 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -31,13 +31,13 @@ static NetNet* convert_to_real_const(Design*des, NetScope*scope, NetEConst*expr) { verireal vrl(expr->value().as_double()); NetECReal rlval(vrl); - NetNet* sig = rlval.synthesize(des, scope); + NetNet* sig = rlval.synthesize(des, scope, 0); return sig; } /* Note that lsig, rsig and real_args are references. */ -static bool process_binary_args(Design*des, NetScope*scope, +static bool process_binary_args(Design*des, NetScope*scope, NetExpr*root, NetExpr*left, NetExpr*right, NetNet*&lsig, NetNet*&rsig, bool&real_args, NetExpr*obj) @@ -49,27 +49,27 @@ static bool process_binary_args(Design*des, NetScope*scope, /* Convert the arguments to real. Handle the special cases of constants, which can be converted more directly. */ if (left->expr_type() == IVL_VT_REAL) { - lsig = left->synthesize(des, scope); + lsig = left->synthesize(des, scope, root); } else if (NetEConst*tmpc = dynamic_cast (left)) { lsig = convert_to_real_const(des, scope, tmpc); } else { - NetNet*tmp = left->synthesize(des, scope); + NetNet*tmp = left->synthesize(des, scope, root); lsig = cast_to_real(des, scope, tmp); } if (right->expr_type() == IVL_VT_REAL) { - rsig = right->synthesize(des, scope); + rsig = right->synthesize(des, scope, root); } else if (NetEConst*tmpc = dynamic_cast (right)) { rsig = convert_to_real_const(des, scope, tmpc); } else { - NetNet*tmp = right->synthesize(des, scope); + NetNet*tmp = right->synthesize(des, scope, root); rsig = cast_to_real(des, scope, tmp); } } else { real_args = false; - lsig = left->synthesize(des, scope); - rsig = right->synthesize(des, scope); + lsig = left->synthesize(des, scope, root); + rsig = right->synthesize(des, scope, root); } @@ -77,7 +77,7 @@ static bool process_binary_args(Design*des, NetScope*scope, else return false; } -NetNet* NetExpr::synthesize(Design*des, NetScope*scope) +NetNet* NetExpr::synthesize(Design*des, NetScope*scope, NetExpr*root) { cerr << get_fileline() << ": internal error: cannot synthesize expression: " << *this << endl; @@ -88,13 +88,13 @@ NetNet* NetExpr::synthesize(Design*des, NetScope*scope) /* * Make an LPM_ADD_SUB device from addition operators. */ -NetNet* NetEBAdd::synthesize(Design*des, NetScope*scope) +NetNet* NetEBAdd::synthesize(Design*des, NetScope*scope, NetExpr*root) { ivl_assert(*this, (op()=='+') || (op()=='-')); NetNet *lsig=0, *rsig=0; bool real_args=false; - if (process_binary_args(des, scope, left_, right_, lsig, rsig, + if (process_binary_args(des, scope, root, left_, right_, lsig, rsig, real_args, this)) { return 0; } @@ -148,10 +148,10 @@ NetNet* NetEBAdd::synthesize(Design*des, NetScope*scope) * signals, then just connect a single gate to each bit of the vector * of the expression. */ -NetNet* NetEBBits::synthesize(Design*des, NetScope*scope) +NetNet* NetEBBits::synthesize(Design*des, NetScope*scope, NetExpr*root) { - NetNet*lsig = left_->synthesize(des, scope); - NetNet*rsig = right_->synthesize(des, scope); + NetNet*lsig = left_->synthesize(des, scope, root); + NetNet*rsig = right_->synthesize(des, scope, root); if (lsig == 0 || rsig == 0) return 0; @@ -212,13 +212,13 @@ NetNet* NetEBBits::synthesize(Design*des, NetScope*scope) return osig; } -NetNet* NetEBComp::synthesize(Design*des, NetScope*scope) +NetNet* NetEBComp::synthesize(Design*des, NetScope*scope, NetExpr*root) { NetNet *lsig=0, *rsig=0; unsigned width; bool real_args=false; - if (process_binary_args(des, scope, left_, right_, lsig, rsig, + if (process_binary_args(des, scope, root, left_, right_, lsig, rsig, real_args, this)) { return 0; } @@ -362,12 +362,12 @@ NetNet* NetEBComp::synthesize(Design*des, NetScope*scope) return osig; } -NetNet* NetEBPow::synthesize(Design*des, NetScope*scope) +NetNet* NetEBPow::synthesize(Design*des, NetScope*scope, NetExpr*root) { NetNet *lsig=0, *rsig=0; unsigned width; bool real_args=false; - if (process_binary_args(des, scope, left_, right_, lsig, rsig, + if (process_binary_args(des, scope, root, left_, right_, lsig, rsig, real_args, this)) { return 0; } @@ -397,12 +397,12 @@ NetNet* NetEBPow::synthesize(Design*des, NetScope*scope) return osig; } -NetNet* NetEBMult::synthesize(Design*des, NetScope*scope) +NetNet* NetEBMult::synthesize(Design*des, NetScope*scope, NetExpr*root) { NetNet *lsig=0, *rsig=0; unsigned width; bool real_args=false; - if (process_binary_args(des, scope, left_, right_, lsig, rsig, + if (process_binary_args(des, scope, root, left_, right_, lsig, rsig, real_args, this)) { return 0; } @@ -433,12 +433,12 @@ NetNet* NetEBMult::synthesize(Design*des, NetScope*scope) return osig; } -NetNet* NetEBDiv::synthesize(Design*des, NetScope*scope) +NetNet* NetEBDiv::synthesize(Design*des, NetScope*scope, NetExpr*root) { NetNet *lsig=0, *rsig=0; unsigned width; bool real_args=false; - if (process_binary_args(des, scope, left_, right_, lsig, rsig, + if (process_binary_args(des, scope, root, left_, right_, lsig, rsig, real_args, this)) { return 0; } @@ -507,10 +507,10 @@ NetNet* NetEBDiv::synthesize(Design*des, NetScope*scope) return osig; } -NetNet* NetEBLogic::synthesize(Design*des, NetScope*scope) +NetNet* NetEBLogic::synthesize(Design*des, NetScope*scope, NetExpr*root) { - NetNet*lsig = left_->synthesize(des, scope); - NetNet*rsig = right_->synthesize(des, scope); + NetNet*lsig = left_->synthesize(des, scope, root); + NetNet*rsig = right_->synthesize(des, scope, root); if (lsig == 0 || rsig == 0) return 0; @@ -584,11 +584,11 @@ NetNet* NetEBLogic::synthesize(Design*des, NetScope*scope) return osig; } -NetNet* NetEBShift::synthesize(Design*des, NetScope*scope) +NetNet* NetEBShift::synthesize(Design*des, NetScope*scope, NetExpr*root) { eval_expr(right_); - NetNet*lsig = left_->synthesize(des, scope); + NetNet*lsig = left_->synthesize(des, scope, root); if (lsig == 0) return 0; @@ -690,7 +690,7 @@ NetNet* NetEBShift::synthesize(Design*des, NetScope*scope) return osig; } - NetNet*rsig = right_->synthesize(des, scope); + NetNet*rsig = right_->synthesize(des, scope, root); if (rsig == 0) return 0; @@ -716,13 +716,13 @@ NetNet* NetEBShift::synthesize(Design*des, NetScope*scope) return osig; } -NetNet* NetEConcat::synthesize(Design*des, NetScope*scope) +NetNet* NetEConcat::synthesize(Design*des, NetScope*scope, NetExpr*root) { /* First, synthesize the operands. */ NetNet**tmp = new NetNet*[parms_.count()]; bool flag = true; for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) { - tmp[idx] = parms_[idx]->synthesize(des, scope); + tmp[idx] = parms_[idx]->synthesize(des, scope, root); if (tmp[idx] == 0) flag = false; } @@ -769,7 +769,7 @@ NetNet* NetEConcat::synthesize(Design*des, NetScope*scope) return osig; } -NetNet* NetEConst::synthesize(Design*des, NetScope*scope) +NetNet* NetEConst::synthesize(Design*des, NetScope*scope, NetExpr*) { perm_string path = scope->local_symbol(); unsigned width=expr_width(); @@ -791,7 +791,7 @@ NetNet* NetEConst::synthesize(Design*des, NetScope*scope) /* * Create a NetLiteral object to represent real valued constants. */ -NetNet* NetECReal::synthesize(Design*des, NetScope*scope) +NetNet* NetECReal::synthesize(Design*des, NetScope*scope, NetExpr*) { perm_string path = scope->local_symbol(); @@ -813,9 +813,9 @@ NetNet* NetECReal::synthesize(Design*des, NetScope*scope) * The bitwise unary logic operator (there is only one) is turned * into discrete gates just as easily as the binary ones above. */ -NetNet* NetEUBits::synthesize(Design*des, NetScope*scope) +NetNet* NetEUBits::synthesize(Design*des, NetScope*scope, NetExpr*root) { - NetNet*isig = expr_->synthesize(des, scope); + NetNet*isig = expr_->synthesize(des, scope, root); if (isig == 0) return 0; @@ -852,19 +852,19 @@ NetNet* NetEUBits::synthesize(Design*des, NetScope*scope) return osig; } -NetNet* NetEUnary::synthesize(Design*des, NetScope*scope) +NetNet* NetEUnary::synthesize(Design*des, NetScope*scope, NetExpr*root) { if (op_ == '+') - return expr_->synthesize(des, scope); + return expr_->synthesize(des, scope, root); if (op_ == '-') { - NetNet*sig = expr_->synthesize(des, scope); + NetNet*sig = expr_->synthesize(des, scope, root); sig = sub_net_from(des, scope, 0, sig); return sig; } if (op_ == 'm') { - NetNet*sub = expr_->synthesize(des, scope); + NetNet*sub = expr_->synthesize(des, scope, root); if (expr_->has_sign() == false) return sub; @@ -886,12 +886,12 @@ NetNet* NetEUnary::synthesize(Design*des, NetScope*scope) cerr << get_fileline() << ": iternal error: " << "NetEUnary::synthesize cannot handle op_=" << op_ << endl; des->errors += 1; - return expr_->synthesize(des, scope); + return expr_->synthesize(des, scope, root); } -NetNet* NetEUReduce::synthesize(Design*des, NetScope*scope) +NetNet* NetEUReduce::synthesize(Design*des, NetScope*scope, NetExpr*root) { - NetNet*isig = expr_->synthesize(des, scope); + NetNet*isig = expr_->synthesize(des, scope, root); if (isig == 0) return 0; @@ -961,10 +961,10 @@ NetNet* NetEUReduce::synthesize(Design*des, NetScope*scope) * - Expression elaboration already converted the offset expression into * canonical form, so we don't have to worry about that here. */ -NetNet* NetESelect::synthesize(Design *des, NetScope*scope) +NetNet* NetESelect::synthesize(Design *des, NetScope*scope, NetExpr*root) { - NetNet*sub = expr_->synthesize(des, scope); + NetNet*sub = expr_->synthesize(des, scope, root); if (sub == 0) return 0; @@ -1065,7 +1065,7 @@ NetNet* NetESelect::synthesize(Design *des, NetScope*scope) // actual part/bit select. Generate a NetPartSelect object to // do the work, and replace "sub" with the selected output. if (base_ != 0) { - off = base_->synthesize(des, scope); + off = base_->synthesize(des, scope, root); NetPartSelect*sel = new NetPartSelect(sub, off, expr_width()); sel->set_line(*this); @@ -1146,11 +1146,11 @@ NetNet* NetESelect::synthesize(Design *des, NetScope*scope) * expressions to the B and A inputs. This way, when the select input * is one, the B input, which is the true expression, is selected. */ -NetNet* NetETernary::synthesize(Design *des, NetScope*scope) +NetNet* NetETernary::synthesize(Design *des, NetScope*scope, NetExpr*root) { - NetNet*csig = cond_->synthesize(des, scope), - *tsig = true_val_->synthesize(des, scope), - *fsig = false_val_->synthesize(des, scope); + NetNet*csig = cond_->synthesize(des, scope, root), + *tsig = true_val_->synthesize(des, scope, root), + *fsig = false_val_->synthesize(des, scope, root); if (csig == 0 || tsig == 0 || fsig == 0) return 0; @@ -1209,7 +1209,7 @@ NetNet* NetETernary::synthesize(Design *des, NetScope*scope) * a bit more work needs to be done. Return a temporary that represents * the selected word. */ -NetNet* NetESignal::synthesize(Design*des, NetScope*scope) +NetNet* NetESignal::synthesize(Design*des, NetScope*scope, NetExpr*root) { if (word_ == 0) return net_; @@ -1236,7 +1236,7 @@ NetNet* NetESignal::synthesize(Design*des, NetScope*scope) mux->set_line(*this); des->add_node(mux); - NetNet*index_net = word_->synthesize(des, scope); + NetNet*index_net = word_->synthesize(des, scope, root); connect(mux->pin_Address(), index_net->pin(0)); connect(tmp->pin(0), mux->pin_Result()); @@ -1244,7 +1244,35 @@ NetNet* NetESignal::synthesize(Design*des, NetScope*scope) return tmp; } -NetNet* NetESFunc::synthesize(Design*des, NetScope*scope) +static NetEvWait* make_func_trigger(Design*des, NetScope*scope, NetExpr*root) +{ + NetEvWait*trigger = 0; + + NexusSet*nset = root->nex_input(false); + if (nset && (nset->count() > 0)) { + NetEvent*ev = new NetEvent(scope->local_symbol()); + ev->set_line(*root); + + NetEvProbe*pr = new NetEvProbe(scope, scope->local_symbol(), + ev, NetEvProbe::ANYEDGE, + nset->count()); + for (unsigned idx = 0 ; idx < nset->count() ; idx += 1) + connect(nset[0][idx], pr->pin(idx)); + + des->add_node(pr); + + scope->add_event(ev); + + trigger = new NetEvWait(0); + trigger->set_line(*root); + trigger->add_event(ev); + } + delete nset; + + return trigger; +} + +NetNet* NetESFunc::synthesize(Design*des, NetScope*scope, NetExpr*root) { const struct sfunc_return_type*def = lookup_sys_func(name_); @@ -1264,8 +1292,13 @@ NetNet* NetESFunc::synthesize(Design*des, NetScope*scope) << name_ << " returns " << def->type << endl; } + NetEvWait*trigger = 0; + if (nparms_ == 0) { + trigger = make_func_trigger(des, scope, root); + } + NetSysFunc*net = new NetSysFunc(scope, scope->local_symbol(), - def, 1+nparms_); + def, 1+nparms_, trigger); net->set_line(*this); des->add_node(net); @@ -1280,7 +1313,7 @@ NetNet* NetESFunc::synthesize(Design*des, NetScope*scope) unsigned errors = 0; for (unsigned idx = 0 ; idx < nparms_ ; idx += 1) { - NetNet*tmp = parms_[idx]->synthesize(des, scope); + NetNet*tmp = parms_[idx]->synthesize(des, scope, root); if (tmp == 0) { cerr << get_fileline() << ": error: Unable to elaborate " << "argument " << idx << " of call to " << name_ << @@ -1298,14 +1331,14 @@ NetNet* NetESFunc::synthesize(Design*des, NetScope*scope) return osig; } -NetNet* NetEUFunc::synthesize(Design*des, NetScope*scope) +NetNet* NetEUFunc::synthesize(Design*des, NetScope*scope, NetExpr*root) { svector eparms (parms_.count()); /* Synthesize the arguments. */ bool errors = false; for (unsigned idx = 0; idx < eparms.count(); idx += 1) { - NetNet*tmp = parms_[idx]->synthesize(des, scope); + NetNet*tmp = parms_[idx]->synthesize(des, scope, root); if (tmp == 0) { cerr << get_fileline() << ": error: Unable to synthesize " "port " << idx << " of call to " @@ -1318,7 +1351,17 @@ NetNet* NetEUFunc::synthesize(Design*des, NetScope*scope) } if (errors) return 0; - NetUserFunc*net = new NetUserFunc(scope_, scope_->local_symbol(), func_); + NetEvWait*trigger = 0; + if (gn_strict_ca_eval_flag) { + /* Ideally we would only do this for functions that have hidden + dependencies or side effects. Once constant functions are + implemented, we may be able to reuse some code to achieve + this. */ + trigger = make_func_trigger(des, scope, root); + } + + NetUserFunc*net = new NetUserFunc(scope_, scope_->local_symbol(), func_, + trigger); net->set_line(*this); des->add_node(net); diff --git a/ivl_target.h b/ivl_target.h index 3171472e6..5e11a5f8e 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -1024,6 +1024,10 @@ extern unsigned ivl_lpm_lineno(ivl_lpm_t net); * often the number of inputs per out, i.e., the number of inputs * per bit for a MUX. * + * ivl_lpm_trigger + * SFUNC and UFUNC devices may have a trigger that forces the + * function output to be re-evaluated. + * * SEMANTIC NOTES * * - Concatenation (IVL_LPM_CONCAT) @@ -1186,6 +1190,11 @@ extern unsigned ivl_lpm_lineno(ivl_lpm_t net); * function (i.e. "$display") that was found in the source code. The * compiler does little checking of that name. * + * The ivl_lpm_trigger function retrieves the trigger event that + * indicates when the system function needs to be re-evaluated. If + * there is no trigger event, the system function only needs to be + * re-evaluated when a change is detected on its input ports. + * * - User Function Call (IVL_LPM_UFUNC) * This device is special as it represents a call to a user defined * function (behavioral code) within a netlist. The inputs to the @@ -1204,6 +1213,11 @@ extern unsigned ivl_lpm_lineno(ivl_lpm_t net); * ports. The ivl_lpm_size function returns the number of inputs for * the device, and the ivl_lpm_data() function index argument selects * the port to retrieve. Each port is sized independently. + * + * The ivl_lpm_trigger function retrieves the trigger event that + * indicates when the user function needs to be re-evaluated. If + * there is no trigger event, the user function only needs to be + * re-evaluated when a change is detected on its input ports. */ extern const char* ivl_lpm_name(ivl_lpm_t net); /* (Obsolete) */ @@ -1213,6 +1227,7 @@ extern ivl_scope_t ivl_lpm_scope(ivl_lpm_t net); extern int ivl_lpm_signed(ivl_lpm_t net); extern ivl_lpm_type_t ivl_lpm_type(ivl_lpm_t net); extern unsigned ivl_lpm_width(ivl_lpm_t net); +extern ivl_event_t ivl_lpm_trigger(ivl_lpm_t net); /* IVL_LPM_FF */ extern ivl_nexus_t ivl_lpm_async_clr(ivl_lpm_t net); diff --git a/main.cc b/main.cc index 0b67231e6..43eddd90d 100644 --- a/main.cc +++ b/main.cc @@ -89,6 +89,7 @@ bool gn_icarus_misc_flag = true; bool gn_cadence_types_flag = true; bool gn_specify_blocks_flag = true; bool gn_io_range_error_flag = true; +bool gn_strict_ca_eval_flag = false; bool gn_verilog_ams_flag = false; map flags; @@ -125,7 +126,6 @@ bool debug_elaborate = false; bool debug_elab_pexpr = false; bool debug_synth2 = false; bool debug_optimizer = false; -bool debug_automatic = false; /* * Verbose messages enabled. @@ -246,6 +246,12 @@ static void process_generation_flag(const char*gen) } else if (strcmp(gen,"no-io-range-error") == 0) { gn_io_range_error_flag = false; + } else if (strcmp(gen,"strict-ca-eval") == 0) { + gn_strict_ca_eval_flag = true; + + } else if (strcmp(gen,"no-strict-ca-eval") == 0) { + gn_strict_ca_eval_flag = false; + } else { } } @@ -397,8 +403,6 @@ static void read_iconfig_file(const char*ipath) } else if (strcmp(cp,"optimizer") == 0) { debug_optimizer = true; cerr << "debug: Enable optimizer debug" << endl; - } else if (strcmp(cp,"automatic") == 0) { - debug_automatic = true; } else { } diff --git a/net_func.cc b/net_func.cc index 795ef8a10..eb3abacc2 100644 --- a/net_func.cc +++ b/net_func.cc @@ -29,9 +29,9 @@ * which accounts for all the inputs, plus one for the phantom output * that is the result. */ -NetUserFunc::NetUserFunc(NetScope*s, perm_string n, NetScope*d) +NetUserFunc::NetUserFunc(NetScope*s, perm_string n, NetScope*d, NetEvWait*trigger) : NetNode(s, n, d->func_def()->port_count()+1), - def_(d) + def_(d), trigger_(trigger) { pin(0).set_dir(Link::OUTPUT); @@ -128,11 +128,9 @@ bool PECallFunction::check_call_matches_definition_(Design*des, NetScope*dscope) NetSysFunc::NetSysFunc(NetScope*s, perm_string n, const struct sfunc_return_type*def, - unsigned ports) -: NetNode(s, n, ports) + unsigned ports, NetEvWait*trigger) +: NetNode(s, n, ports), def_(def), trigger_(trigger) { - def_ = def; - pin(0).set_dir(Link::OUTPUT); // Q for (unsigned idx = 1 ; idx < pin_count() ; idx += 1) { @@ -161,4 +159,3 @@ unsigned NetSysFunc::vector_width() const { return def_->wid; } - diff --git a/netlist.h b/netlist.h index 38295a788..4256f35f2 100644 --- a/netlist.h +++ b/netlist.h @@ -1439,7 +1439,7 @@ class NetReplicate : public NetNode { class NetUserFunc : public NetNode { public: - NetUserFunc(NetScope*s, perm_string n, NetScope*def); + NetUserFunc(NetScope*s, perm_string n, NetScope*def, NetEvWait*trigger); ~NetUserFunc(); ivl_variable_type_t data_type(unsigned port) const; @@ -1447,11 +1447,14 @@ class NetUserFunc : public NetNode { const NetScope* def() const; + const NetEvWait* trigger() const { return trigger_; } + virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: NetScope*def_; + NetEvWait*trigger_; }; /* @@ -1463,18 +1466,21 @@ class NetSysFunc : public NetNode { public: NetSysFunc(NetScope*s, perm_string n, const struct sfunc_return_type*def, - unsigned ports); + unsigned ports, NetEvWait*trigger); ~NetSysFunc(); ivl_variable_type_t data_type() const; unsigned vector_width() const; const char* func_name() const; + const NetEvWait* trigger() const { return trigger_; } + virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*) const; private: const struct sfunc_return_type*def_; + NetEvWait*trigger_; }; class NetTran : public NetNode, public IslandBranch { @@ -1598,12 +1604,14 @@ class NetExpr : public LineInfo { // // des, scope: The context where this work is done // + // root: The root expression of which this expression is a part. + // // rise/fall/decay: Attach these delays to the driver for the // expression output. // // drive0/drive1: Attach these strengths tp the driver for // the expression output. - virtual NetNet*synthesize(Design*des, NetScope*scope); + virtual NetNet*synthesize(Design*des, NetScope*scope, NetExpr*root); protected: @@ -1641,7 +1649,7 @@ class NetEConst : public NetExpr { virtual void dump(ostream&) const; virtual NetEConst* dup_expr() const; - virtual NetNet*synthesize(Design*, NetScope*scope); + virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*); virtual NexusSet* nex_input(bool rem_out = true); private: @@ -1694,7 +1702,7 @@ class NetECReal : public NetExpr { virtual void dump(ostream&) const; virtual NetECReal* dup_expr() const; - virtual NetNet*synthesize(Design*, NetScope*scope); + virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*); virtual NexusSet* nex_input(bool rem_out = true); private: @@ -2999,7 +3007,7 @@ class NetEUFunc : public NetExpr { virtual void expr_scan(struct expr_scan_t*) const; virtual NetEUFunc*dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); - virtual NetNet* synthesize(Design*des, NetScope*scope); + virtual NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); private: NetScope*scope_; @@ -3242,7 +3250,7 @@ class NetEBAdd : public NetEBinary { virtual bool set_width(unsigned w, bool last_chance); virtual NetEBAdd* dup_expr() const; virtual NetExpr* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*, NetScope*scope); + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: NetECReal* eval_tree_real_(); @@ -3264,7 +3272,7 @@ class NetEBDiv : public NetEBinary { virtual bool set_width(unsigned w, bool last_chance); virtual NetEBDiv* dup_expr() const; virtual NetExpr* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*, NetScope*scope); + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); }; /* @@ -3291,7 +3299,7 @@ class NetEBBits : public NetEBinary { virtual NetEBBits* dup_expr() const; virtual NetEConst* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*, NetScope*scope); + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); }; /* @@ -3322,7 +3330,7 @@ class NetEBComp : public NetEBinary { virtual NetEBComp* dup_expr() const; virtual NetEConst* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*, NetScope*scope); + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: NetEConst* must_be_leeq_(NetExpr*le, const verinum&rv, bool eq_flag); @@ -3354,7 +3362,7 @@ class NetEBLogic : public NetEBinary { virtual bool set_width(unsigned w, bool last_chance =false); virtual NetEBLogic* dup_expr() const; virtual NetEConst* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*, NetScope*scope); + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: }; @@ -3392,7 +3400,7 @@ class NetEBMult : public NetEBinary { virtual bool set_width(unsigned w, bool last_chance); virtual NetEBMult* dup_expr() const; virtual NetExpr* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*, NetScope*scope); + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3414,7 +3422,7 @@ class NetEBPow : public NetEBinary { virtual bool set_width(unsigned w, bool last_chance); virtual NetEBPow* dup_expr() const; virtual NetExpr* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*, NetScope*scope); + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3446,7 +3454,7 @@ class NetEBShift : public NetEBinary { virtual NetEBShift* dup_expr() const; virtual NetEConst* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*, NetScope*scope); + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: }; @@ -3480,7 +3488,7 @@ class NetEConcat : public NetExpr { virtual bool set_width(unsigned w, bool last_chance =false); virtual NetEConcat* dup_expr() const; virtual NetEConst* eval_tree(int prune_to_width = -1); - virtual NetNet*synthesize(Design*, NetScope*scope); + virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -3557,7 +3565,7 @@ class NetESelect : public NetExpr { virtual void expr_scan(struct expr_scan_t*) const; virtual NetEConst* eval_tree(int prune_to_width = -1); virtual NetESelect* dup_expr() const; - virtual NetNet*synthesize(Design*des, NetScope*scope); + virtual NetNet*synthesize(Design*des, NetScope*scope, NetExpr*root); virtual void dump(ostream&) const; private: @@ -3637,7 +3645,7 @@ class NetESFunc : public NetExpr { virtual void expr_scan(struct expr_scan_t*) const; virtual NetESFunc*dup_expr() const; - virtual NetNet*synthesize(Design*, NetScope*scope); + virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); private: const char* name_; @@ -3674,7 +3682,7 @@ class NetETernary : public NetExpr { virtual NexusSet* nex_input(bool rem_out = true); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; - virtual NetNet*synthesize(Design*, NetScope*scope); + virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); public: static bool test_operand_compat(ivl_variable_type_t tru, ivl_variable_type_t fal); @@ -3714,7 +3722,7 @@ class NetEUnary : public NetExpr { virtual NetEUnary* dup_expr() const; virtual NetExpr* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*, NetScope*scope); + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); @@ -3735,7 +3743,7 @@ class NetEUBits : public NetEUnary { NetEUBits(char op, NetExpr*ex); ~NetEUBits(); - virtual NetNet* synthesize(Design*, NetScope*scope); + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual NetEUBits* dup_expr() const; virtual NetExpr* eval_tree(int prune_to_width = -1); @@ -3749,7 +3757,7 @@ class NetEUReduce : public NetEUnary { ~NetEUReduce(); virtual bool set_width(unsigned w, bool last_chance); - virtual NetNet* synthesize(Design*, NetScope*scope); + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual NetEUReduce* dup_expr() const; virtual NetEConst* eval_tree(int prune_to_width = -1); virtual ivl_variable_type_t expr_type() const; @@ -3776,7 +3784,7 @@ class NetESignal : public NetExpr { virtual bool set_width(unsigned, bool last_chance); virtual NetESignal* dup_expr() const; - NetNet* synthesize(Design*des, NetScope*scope); + NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); NexusSet* nex_input(bool rem_out = true); // This is the expression for selecting an array word, if this diff --git a/pform.cc b/pform.cc index 2737b0ea0..521b699c6 100644 --- a/pform.cc +++ b/pform.cc @@ -110,12 +110,12 @@ PTask* pform_push_task_scope(char*name, bool is_auto) PTask*task; if (pform_cur_generate) { task = new PTask(task_name, pform_cur_generate->lexical_scope, - is_auto || debug_automatic); + is_auto); pform_cur_generate->tasks[task->pscope_name()] = task; pform_cur_generate->lexical_scope = task; } else { task = new PTask(task_name, lexical_scope, - is_auto || debug_automatic); + is_auto); pform_cur_module->tasks[task->pscope_name()] = task; lexical_scope = task; } @@ -130,12 +130,12 @@ PFunction* pform_push_function_scope(char*name, bool is_auto) PFunction*func; if (pform_cur_generate) { func = new PFunction(func_name, pform_cur_generate->lexical_scope, - is_auto || debug_automatic); + is_auto); pform_cur_generate->funcs[func->pscope_name()] = func; pform_cur_generate->lexical_scope = func; } else { func = new PFunction(func_name, lexical_scope, - is_auto || debug_automatic); + is_auto); pform_cur_module->funcs[func->pscope_name()] = func; lexical_scope = func; } diff --git a/syn-rules.y b/syn-rules.y index 80dbad7c4..dc06414a9 100644 --- a/syn-rules.y +++ b/syn-rules.y @@ -141,7 +141,7 @@ static void make_DFF_CE(Design*des, NetProcTop*top, NetEvWait*wclk, NetEvProbe*pclk = eclk->probe(0); NetESignal*d = dynamic_cast (asn->rval()); - NetNet*ce = cexp? cexp->synthesize(des, top->scope()) : 0; + NetNet*ce = cexp? cexp->synthesize(des, top->scope(), cexp) : 0; if (d == 0) { cerr << asn->get_fileline() << ": internal error: " diff --git a/synth.cc b/synth.cc index 6f2012290..25a963b61 100644 --- a/synth.cc +++ b/synth.cc @@ -53,7 +53,7 @@ int do_expr::assign(NetAssign*stmt) if (dynamic_cast(stmt->rval())) return 0; - NetNet*tmp = stmt->rval()->synthesize(des_, scope_); + NetNet*tmp = stmt->rval()->synthesize(des_, scope_, stmt->rval()); if (tmp == 0) return 0; @@ -68,7 +68,7 @@ int do_expr::assign_nb(NetAssignNB*stmt) if (dynamic_cast(stmt->rval())) return 0; - NetNet*tmp = stmt->rval()->synthesize(des_, scope_); + NetNet*tmp = stmt->rval()->synthesize(des_, scope_, stmt->rval()); if (tmp == 0) return 0; @@ -82,7 +82,7 @@ int do_expr::condit(NetCondit*stmt) { /* synthesize the condition expression, if necessary. */ if (! dynamic_cast(stmt->expr())) { - NetNet*tmp = stmt->expr()->synthesize(des_, scope_); + NetNet*tmp = stmt->expr()->synthesize(des_, scope_, stmt->expr()); if (tmp) { NetESignal*tmpe = new NetESignal(tmp); diff --git a/synth2.cc b/synth2.cc index dbd843759..d99e8e54b 100644 --- a/synth2.cc +++ b/synth2.cc @@ -60,7 +60,7 @@ bool NetProc::synth_sync(Design*des, NetScope*scope, NetFF*ff, bool NetAssignBase::synth_async(Design*des, NetScope*scope, const NetBus&nex_map, NetBus&nex_out) { - NetNet*rsig = rval_->synthesize(des, scope); + NetNet*rsig = rval_->synthesize(des, scope, rval_); assert(rsig); NetNet*lsig = lval_->sig(); @@ -155,7 +155,7 @@ bool NetCase::synth_async(Design*des, NetScope*scope, const NetBus&nex_map, NetBus&nex_out) { /* Synthesize the select expression. */ - NetNet*esig = expr_->synthesize(des, scope); + NetNet*esig = expr_->synthesize(des, scope, expr_); unsigned sel_width = esig->vector_width(); assert(sel_width > 0); @@ -978,4 +978,3 @@ void synth2(Design*des) synth2_f synth_obj; des->functor(&synth_obj); } - diff --git a/t-dll-api.cc b/t-dll-api.cc index 60ece7ebf..207203f4b 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -1284,6 +1284,20 @@ extern "C" unsigned ivl_lpm_width(ivl_lpm_t net) return net->width; } +extern "C" ivl_event_t ivl_lpm_trigger(ivl_lpm_t net) +{ + assert(net); + switch (net->type) { + case IVL_LPM_SFUNC: + return net->u_.sfunc.trigger; + case IVL_LPM_UFUNC: + return net->u_.ufunc.trigger; + default: + assert(0); + return 0; + } +} + extern "C" ivl_expr_t ivl_lval_mux(ivl_lval_t net) { assert(net); diff --git a/t-dll.cc b/t-dll.cc index 9b343778d..c72eb939f 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -1239,6 +1239,40 @@ void dll_target::net_case_cmp(const NetCaseCmp*net) scope_add_lpm(obj->scope, obj); } +ivl_event_t dll_target::make_lpm_trigger(const NetEvWait*net) +{ + ivl_event_t trigger = 0; + if (net) { + const NetEvent*ev = net->event(0); + + /* Locate the event by name. */ + ivl_scope_t ev_scope = lookup_scope_(ev->scope()); + + assert(ev_scope); + assert(ev_scope->nevent_ > 0); + for (unsigned idx = 0; idx < ev_scope->nevent_; idx += 1) { + const char*ename = + ivl_event_basename(ev_scope->event_[idx]); + if (strcmp(ev->name(), ename) == 0) { + trigger = ev_scope->event_[idx]; + break; + } + } + + /* Connect up the probe pins. This wasn't done during the + ::event method because the signals weren't scanned yet. */ + assert(ev->nprobe() == 1); + const NetEvProbe*pr = ev->probe(0); + for (unsigned bit = 0; bit < pr->pin_count(); bit += 1) { + ivl_nexus_t nex = (ivl_nexus_t) + pr->pin(bit).nexus()->t_cookie(); + assert(nex); + trigger->pins[bit] = nex; + } + } + return trigger; +} + bool dll_target::net_sysfunction(const NetSysFunc*net) { unsigned idx; @@ -1276,6 +1310,9 @@ bool dll_target::net_sysfunction(const NetSysFunc*net) IVL_DR_HiZ, IVL_DR_HiZ); } + /* Save information about the trigger event if it exists. */ + obj->u_.sfunc.trigger = make_lpm_trigger(net->trigger()); + make_lpm_delays_(obj, net); scope_add_lpm(obj->scope, obj); @@ -1326,6 +1363,9 @@ bool dll_target::net_function(const NetUserFunc*net) nexus_lpm_add(obj->u_.ufunc.pins[idx], obj, idx, drive, drive); } + /* Save information about the trigger event if it exists. */ + obj->u_.ufunc.trigger = make_lpm_trigger(net->trigger()); + make_lpm_delays_(obj, net); /* All done. Add this LPM to the scope. */ diff --git a/t-dll.h b/t-dll.h index 2c60c9529..f831b5d33 100644 --- a/t-dll.h +++ b/t-dll.h @@ -169,6 +169,8 @@ struct dll_target : public target_t, public expr_scan_t { void make_scope_parameters(ivl_scope_t scope, const NetScope*net); void make_scope_param_expr(ivl_parameter_t cur_par, NetExpr*etmp); + ivl_event_t make_lpm_trigger(const NetEvWait*ev); + static ivl_expr_t expr_from_value_(const verinum&that); }; @@ -377,12 +379,14 @@ struct ivl_lpm_s { const char* fun_name; unsigned ports; ivl_nexus_t*pins; + ivl_event_t trigger; } sfunc; struct ivl_lpm_ufunc_s { ivl_scope_t def; unsigned ports; ivl_nexus_t*pins; + ivl_event_t trigger; } ufunc; } u_; }; diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 64ab70e3d..e7633c7dc 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -1537,9 +1537,15 @@ static void draw_lpm_sfunc(ivl_lpm_t net) const char*dly = draw_lpm_output_delay(net); - fprintf(vvp_out, "L_%p%s .sfunc %u %u \"%s\"", net, dly, - ivl_file_table_index(ivl_lpm_file(net)), ivl_lpm_lineno(net), - ivl_lpm_string(net)); + if (ivl_lpm_trigger(net)) + fprintf(vvp_out, "L_%p%s .sfunc/e %u %u \"%s\", E_%p", net, dly, + ivl_file_table_index(ivl_lpm_file(net)), + ivl_lpm_lineno(net), ivl_lpm_string(net), + ivl_lpm_trigger(net)); + else + fprintf(vvp_out, "L_%p%s .sfunc %u %u \"%s\"", net, dly, + ivl_file_table_index(ivl_lpm_file(net)), + ivl_lpm_lineno(net), ivl_lpm_string(net)); /* Print the function type descriptor string. */ fprintf(vvp_out, ", \""); @@ -1565,9 +1571,14 @@ static void draw_lpm_ufunc(ivl_lpm_t net) const char*dly = draw_lpm_output_delay(net); - fprintf(vvp_out, "L_%p%s .ufunc TD_%s, %u", net, dly, - vvp_mangle_id(ivl_scope_name(def)), - ivl_lpm_width(net)); + if (ivl_lpm_trigger(net)) + fprintf(vvp_out, "L_%p%s .ufunc/e TD_%s, %u, E_%p", net, dly, + vvp_mangle_id(ivl_scope_name(def)), + ivl_lpm_width(net), ivl_lpm_trigger(net)); + else + fprintf(vvp_out, "L_%p%s .ufunc TD_%s, %u", net, dly, + vvp_mangle_id(ivl_scope_name(def)), + ivl_lpm_width(net)); /* Print all the net signals that connect to the input of the function. */ diff --git a/vvp/README.txt b/vvp/README.txt index 4781f6814..8c209b6a7 100644 --- a/vvp/README.txt +++ b/vvp/README.txt @@ -654,9 +654,17 @@ input to port 1 is the amount to shift. STRUCTURAL FUNCTION CALLS: -The .ufunc statement defines a call to a user defined function. +The .ufunc statements define a call to a user defined function. -