From 2bbd077dc90887028ca0770c32b4a8dc0a34010f Mon Sep 17 00:00:00 2001 From: Cary R Date: Wed, 27 Dec 2017 14:41:43 -0800 Subject: [PATCH] Add some synthesis checks for the always_comb/ff/latch blocks --- elaborate.cc | 70 ++++-- expr_synth.cc | 3 +- net_event.cc | 1 + net_nex_input.cc | 190 ++++++++++------ net_nex_output.cc | 51 +++-- netlist.cc | 564 ++++++++++++++++++++++++++++++++++++++++++++-- netlist.h | 44 +++- 7 files changed, 785 insertions(+), 138 deletions(-) diff --git a/elaborate.cc b/elaborate.cc index e378e842a..f450a42ff 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -2906,11 +2906,12 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const if (nscope == 0) nscope = scope; - // Handle the special case that the block contains only one - // statement. There is no need to keep the block node. Also, - // don't elide named blocks, because they might be referenced - // elsewhere. - if ((list_.size() == 1) && (pscope_name() == 0)) { + // Handle the special case that the sequential block contains + // only one statement. There is no need to keep the block node. + // Also, don't elide named blocks, because they might be + // referenced elsewhere. + if ((type == NetBlock::SEQU) && (list_.size() == 1) && + (pscope_name() == 0)) { assert(list_[0]); NetProc*tmp = list_[0]->elaborate(des, nscope); return tmp; @@ -4190,6 +4191,7 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope, NetEvent*ev = new NetEvent(scope->local_symbol()); ev->set_line(*this); + ev->local_flag(true); unsigned expr_count = 0; NetEvWait*wa = new NetEvWait(enet); @@ -4470,6 +4472,8 @@ NetProc* PEventStatement::elaborate_wait(Design*des, NetScope*scope, /* Create an event wait and an otherwise unreferenced event variable to force a perpetual wait. */ NetEvent*wait_event = new NetEvent(scope->local_symbol()); + wait_event->set_line(*this); + wait_event->local_flag(true); scope->add_event(wait_event); NetEvWait*wait = new NetEvWait(0); @@ -4490,6 +4494,8 @@ NetProc* PEventStatement::elaborate_wait(Design*des, NetScope*scope, eval_expr(expr); NetEvent*wait_event = new NetEvent(scope->local_symbol()); + wait_event->set_line(*this); + wait_event->local_flag(true); scope->add_event(wait_event); NetEvWait*wait = new NetEvWait(0 /* noop */); @@ -6110,10 +6116,10 @@ class later_defparams : public elaborator_work_item_t { bool Design::check_proc_delay() const { - bool result_flag = true; + bool result = false; for (const NetProcTop*pr = procs_ ; pr ; pr = pr->next_) { - /* If this is an always block and we have no or zero delay then + /* If this is an always process and we have no or zero delay then * a runtime infinite loop will happen. If we possibly have some * delay then print a warning that an infinite loop is possible. */ @@ -6128,7 +6134,7 @@ bool Design::check_proc_delay() const << " statement does not have any delay." << endl; cerr << pr->get_fileline() << ": : A runtime" << " infinite loop will occur." << endl; - result_flag = false; + result = true; } else if (dly_type == POSSIBLE_DELAY && warn_inf_loop) { cerr << pr->get_fileline() << ": warning: always" @@ -6137,20 +6143,20 @@ bool Design::check_proc_delay() const << " infinite loop may be possible." << endl; } - // The always_comb/ff/latch blocks have special delay - // rules that need to be checked. + // The always_comb/ff/latch processes also have special + // delay rules that need to be checked. if ((pr->type() == IVL_PR_ALWAYS_COMB) || (pr->type() == IVL_PR_ALWAYS_FF) || (pr->type() == IVL_PR_ALWAYS_LATCH)) { const NetEvWait *wait = dynamic_cast (pr->statement()); if (! wait) { - // The always_comb/latch have an event wait - // added automatically by the compiler. + // The always_comb/latch processes have an event + // control added automatically by the compiler. assert(pr->type() == IVL_PR_ALWAYS_FF); cerr << pr->get_fileline() << ": error: the " << "first statement of an always_ff must " << "be an event control." << endl; - result_flag = false; + result = true; } else if (wait->statement()->delay_type(true) != NO_DELAY) { cerr << pr->get_fileline() << ": error: there " << "must "; @@ -6165,7 +6171,7 @@ bool Design::check_proc_delay() const << "process."; } cerr << endl; - result_flag = false; + result = true; } } } @@ -6180,12 +6186,30 @@ bool Design::check_proc_delay() const if (dly_type != NO_DELAY && dly_type != ZERO_DELAY) { cerr << pr->get_fileline() << ": error: final" << " statement contains a delay." << endl; - result_flag = false; + result = true; } } } - return result_flag; + return result; +} + +/* + * Check to see if the always_* processes only contain synthesizable + * constructs. + */ +bool Design::check_proc_synth() const +{ + bool result = false; + for (const NetProcTop*pr = procs_ ; pr ; pr = pr->next_) { + if ((pr->type() == IVL_PR_ALWAYS_COMB) || + (pr->type() == IVL_PR_ALWAYS_FF) || + (pr->type() == IVL_PR_ALWAYS_LATCH)) { + result |= pr->statement()->check_synth(pr->type(), + pr->scope()); + } + } + return result; } /* @@ -6589,10 +6613,11 @@ Design* elaborate(listroots) // Now that everything is fully elaborated verify that we do // not have an always block with no delay (an infinite loop), // or a final block with a delay. - if (des->check_proc_delay() == false) { - delete des; - des = 0; - } + bool has_failure = des->check_proc_delay(); + + // Check to see if the always_comb/ff/latch processes only have + // synthesizable constructs + has_failure |= des->check_proc_synth(); if (debug_elaborate) { cerr << "" << ": debug: " @@ -6600,5 +6625,10 @@ Design* elaborate(listroots) << des->find_root_scopes().size() << " root scopes " << endl; } + if (has_failure) { + delete des; + des = 0; + } + return des; } diff --git a/expr_synth.cc b/expr_synth.cc index 6f8ca29b9..5c28e1adb 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2017 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -1358,6 +1358,7 @@ static NetEvWait* make_func_trigger(Design*des, NetScope*scope, NetExpr*root) if (nset && (nset->size() > 0)) { NetEvent*ev = new NetEvent(scope->local_symbol()); ev->set_line(*root); + ev->local_flag(true); NetEvProbe*pr = new NetEvProbe(scope, scope->local_symbol(), ev, NetEvProbe::ANYEDGE, diff --git a/net_event.cc b/net_event.cc index 8c66ff1bf..2b6eb8d35 100644 --- a/net_event.cc +++ b/net_event.cc @@ -28,6 +28,7 @@ NetEvent::NetEvent(perm_string n) : name_(n) { + local_flag_ = false; scope_ = 0; snext_ = 0; probes_ = 0; diff --git a/net_nex_input.cc b/net_nex_input.cc index f11ffad7e..38e7e148a 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -32,7 +32,7 @@ NexusSet* NetExpr::nex_input(bool, bool) const cerr << get_fileline() << ": internal error: nex_input not implemented: " << *this << endl; - return 0; + return new NexusSet; } NexusSet* NetProc::nex_input(bool, bool) const @@ -40,7 +40,7 @@ NexusSet* NetProc::nex_input(bool, bool) const cerr << get_fileline() << ": internal error: NetProc::nex_input not implemented" << endl; - return 0; + return new NexusSet; } NexusSet* NetEArrayPattern::nex_input(bool rem_out, bool search_funcs) const @@ -69,12 +69,12 @@ NexusSet* NetEBinary::nex_input(bool rem_out, bool search_funcs) const NexusSet* NetEConcat::nex_input(bool rem_out, bool search_funcs) const { - if (parms_[0] == NULL) return NULL; + if (parms_[0] == NULL) return new NexusSet; NexusSet*result = parms_[0]->nex_input(rem_out, search_funcs); for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { if (parms_[idx] == NULL) { delete result; - return NULL; + return new NexusSet; } NexusSet*tmp = parms_[idx]->nex_input(rem_out, search_funcs); result->add(*tmp); @@ -140,10 +140,6 @@ NexusSet* NetESelect::nex_input(bool rem_out, bool search_funcs) const { NexusSet*result = base_? base_->nex_input(rem_out, search_funcs) : new NexusSet(); NexusSet*tmp = expr_->nex_input(rem_out, search_funcs); - if (tmp == NULL) { - delete result; - return NULL; - } result->add(*tmp); delete tmp; /* See the comment for NetESignal below. */ @@ -159,19 +155,18 @@ NexusSet* NetESelect::nex_input(bool rem_out, bool search_funcs) const */ NexusSet* NetESFunc::nex_input(bool rem_out, bool search_funcs) const { - if (parms_.empty()) - return new NexusSet; + NexusSet*result = new NexusSet; - NexusSet*result; - if (parms_[0]) result = parms_[0]->nex_input(rem_out, search_funcs); - else result = new NexusSet; - for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { + if (parms_.empty()) return result; + + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { if (parms_[idx]) { NexusSet*tmp = parms_[idx]->nex_input(rem_out, search_funcs); result->add(*tmp); delete tmp; } } + return result; } @@ -259,9 +254,16 @@ NexusSet* NetEUnary::nex_input(bool rem_out, bool search_funcs) const return expr_->nex_input(rem_out, search_funcs); } +NexusSet* NetAlloc::nex_input(bool, bool) const +{ + return new NexusSet; +} + NexusSet* NetAssign_::nex_input(bool rem_out, bool search_funcs) const { + assert(! nest_); NexusSet*result = new NexusSet; + if (word_) { NexusSet*tmp = word_->nex_input(rem_out, search_funcs); result->add(*tmp); @@ -278,7 +280,13 @@ NexusSet* NetAssign_::nex_input(bool rem_out, bool search_funcs) const NexusSet* NetAssignBase::nex_input(bool rem_out, bool search_funcs) const { - NexusSet*result = rval_->nex_input(rem_out, search_funcs); + NexusSet*result = new NexusSet; + // For the deassign and release statements there is no R-value. + if (rval_) { + NexusSet*tmp = rval_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } /* It is possible that the lval_ can have nex_input values. In particular, index expressions are statement inputs as well, @@ -311,14 +319,13 @@ NexusSet* NetAssignBase::nex_input(bool rem_out, bool search_funcs) const */ NexusSet* NetBlock::nex_input(bool rem_out, bool search_funcs) const { - if (last_ == 0) - return new NexusSet; + if (last_ == 0) return new NexusSet; - if (type_ != SEQU) { + if (! search_funcs && (type_ != SEQU)) { cerr << get_fileline() << ": internal error: Sorry, " << "I don't know how to synthesize fork/join blocks." << endl; - return 0; + return new NexusSet; } NetProc*cur = last_->next_; @@ -332,7 +339,7 @@ NexusSet* NetBlock::nex_input(bool rem_out, bool search_funcs) const NexusSet*tmp = cur->nex_input(rem_out, search_funcs); /* Add the current input set to the accumulated input set. */ - if (tmp != 0) result->add(*tmp); + result->add(*tmp); delete tmp; /* Add the current outputs to the accumulated output set if @@ -359,8 +366,6 @@ NexusSet* NetBlock::nex_input(bool rem_out, bool search_funcs) const NexusSet* NetCase::nex_input(bool rem_out, bool search_funcs) const { NexusSet*result = expr_->nex_input(rem_out, search_funcs); - if (result == 0) - return 0; for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { @@ -369,7 +374,6 @@ NexusSet* NetCase::nex_input(bool rem_out, bool search_funcs) const continue; NexusSet*tmp = items_[idx].statement->nex_input(rem_out, search_funcs); - assert(tmp); result->add(*tmp); delete tmp; @@ -378,7 +382,6 @@ NexusSet* NetCase::nex_input(bool rem_out, bool search_funcs) const guard. The default guard obviously has no input. */ if (items_[idx].guard) { tmp = items_[idx].guard->nex_input(rem_out, search_funcs); - assert(tmp); result->add(*tmp); delete tmp; } @@ -387,16 +390,10 @@ NexusSet* NetCase::nex_input(bool rem_out, bool search_funcs) const return result; } -NexusSet* NetCAssign::nex_input(bool, bool) const -{ - cerr << get_fileline() << ": internal warning: NetCAssign::nex_input()" - << " not implemented." << endl; - return new NexusSet; -} - NexusSet* NetCondit::nex_input(bool rem_out, bool search_funcs) const { NexusSet*result = expr_->nex_input(rem_out, search_funcs); + if (if_ != 0) { NexusSet*tmp = if_->nex_input(rem_out, search_funcs); result->add(*tmp); @@ -412,51 +409,85 @@ NexusSet* NetCondit::nex_input(bool rem_out, bool search_funcs) const return result; } +NexusSet* NetDisable::nex_input(bool, bool) const +{ + return new NexusSet; +} + NexusSet* NetDoWhile::nex_input(bool rem_out, bool search_funcs) const { - NexusSet*result = proc_->nex_input(rem_out, search_funcs); - NexusSet*tmp = cond_->nex_input(rem_out, search_funcs); - result->add(*tmp); - delete tmp; + NexusSet*result = cond_->nex_input(rem_out, search_funcs); + + if (proc_) { + NexusSet*tmp = proc_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + return result; } +NexusSet* NetEvTrig::nex_input(bool, bool) const +{ + return new NexusSet; +} + NexusSet* NetEvWait::nex_input(bool rem_out, bool search_funcs) const { - NexusSet*result; - if (statement_) - result = statement_->nex_input(rem_out, search_funcs); - else - result = new NexusSet; + NexusSet*result = new NexusSet; + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } return result; } -NexusSet* NetForce::nex_input(bool, bool) const +NexusSet* NetForever::nex_input(bool rem_out, bool search_funcs) const { - cerr << get_fileline() << ": internal warning: NetForce::nex_input()" - << " not implemented." << endl; - return new NexusSet; + NexusSet*result = new NexusSet; + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + + return result; } NexusSet* NetForLoop::nex_input(bool rem_out, bool search_funcs) const { - NexusSet*result = init_expr_->nex_input(rem_out, search_funcs); + NexusSet*result = new NexusSet; - NexusSet*tmp = condition_->nex_input(rem_out, search_funcs); - result->add(*tmp); - delete tmp; + if (init_expr_) { + NexusSet*tmp = init_expr_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } - tmp = statement_->nex_input(rem_out, search_funcs); - result->add(*tmp); - delete tmp; + if (condition_) { + NexusSet*tmp = condition_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } - tmp = step_statement_->nex_input(rem_out, search_funcs); - result->add(*tmp); - delete tmp; + if (step_statement_) { + NexusSet*tmp = step_statement_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } if (gn_shared_loop_index_flag) { - tmp = new NexusSet(); + NexusSet*tmp = new NexusSet(); for (unsigned idx = 0 ; idx < index_->pin_count() ; idx += 1) tmp->add(index_->pin(idx).nexus(), 0, index_->vector_width()); @@ -467,10 +498,9 @@ NexusSet* NetForLoop::nex_input(bool rem_out, bool search_funcs) const return result; } -NexusSet* NetForever::nex_input(bool rem_out, bool search_funcs) const +NexusSet* NetFree::nex_input(bool, bool) const { - NexusSet*result = statement_->nex_input(rem_out, search_funcs); - return result; + return new NexusSet; } /* @@ -484,17 +514,27 @@ NexusSet* NetForever::nex_input(bool rem_out, bool search_funcs) const */ NexusSet* NetPDelay::nex_input(bool rem_out, bool search_funcs) const { - if (statement_ == 0) return 0; - NexusSet*result = statement_->nex_input(rem_out, search_funcs); + NexusSet*result = new NexusSet; + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + return result; } NexusSet* NetRepeat::nex_input(bool rem_out, bool search_funcs) const { - NexusSet*result = statement_->nex_input(rem_out, search_funcs); - NexusSet*tmp = expr_->nex_input(rem_out, search_funcs); - result->add(*tmp); - delete tmp; + NexusSet*result = expr_->nex_input(rem_out, search_funcs); + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + return result; } @@ -503,13 +543,11 @@ NexusSet* NetRepeat::nex_input(bool rem_out, bool search_funcs) const */ NexusSet* NetSTask::nex_input(bool rem_out, bool search_funcs) const { - if (parms_.empty()) - return new NexusSet; + NexusSet*result = new NexusSet; - NexusSet*result; - if (parms_[0]) result = parms_[0]->nex_input(rem_out, search_funcs); - else result = new NexusSet; - for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { + if (parms_.empty()) return result; + + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { if (parms_[idx]) { NexusSet*tmp = parms_[idx]->nex_input(rem_out, search_funcs); result->add(*tmp); @@ -532,9 +570,13 @@ NexusSet* NetUTask::nex_input(bool, bool) const NexusSet* NetWhile::nex_input(bool rem_out, bool search_funcs) const { - NexusSet*result = proc_->nex_input(rem_out, search_funcs); - NexusSet*tmp = cond_->nex_input(rem_out, search_funcs); - result->add(*tmp); - delete tmp; + NexusSet*result = cond_->nex_input(rem_out, search_funcs); + + if (proc_) { + NexusSet*tmp = proc_->nex_input(rem_out, search_funcs); + result->add(*tmp); + delete tmp; + } + return result; } diff --git a/net_nex_output.cc b/net_nex_output.cc index 194947955..9c1d32e10 100644 --- a/net_nex_output.cc +++ b/net_nex_output.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2017 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -36,8 +36,13 @@ void NetProc::nex_output(NexusSet&) << endl; } +void NetAlloc::nex_output(NexusSet&) +{ +} + void NetAssign_::nex_output(NexusSet&out) { + assert(! nest_); assert(sig_); unsigned use_word = 0; unsigned use_base = 0; @@ -89,8 +94,7 @@ void NetAssignBase::nex_output(NexusSet&out) void NetBlock::nex_output(NexusSet&out) { - if (last_ == 0) - return; + if (last_ == 0) return; NetProc*cur = last_; do { @@ -104,10 +108,8 @@ void NetCase::nex_output(NexusSet&out) for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { // Empty statements clearly have no output. - if (items_[idx].statement == 0) - continue; + if (items_[idx].statement == 0) continue; - assert(items_[idx].statement); items_[idx].statement->nex_output(out); } @@ -115,22 +117,31 @@ void NetCase::nex_output(NexusSet&out) void NetCondit::nex_output(NexusSet&out) { - if (if_ != 0) - if_->nex_output(out); - if (else_ != 0) - else_->nex_output(out); + if (if_) if_->nex_output(out); + if (else_) else_->nex_output(out); +} + +void NetDisable::nex_output(NexusSet&) +{ } void NetDoWhile::nex_output(NexusSet&out) { - if (proc_ != 0) - proc_->nex_output(out); + if (proc_) proc_->nex_output(out); +} + +void NetEvTrig::nex_output(NexusSet&) +{ } void NetEvWait::nex_output(NexusSet&out) { - assert(statement_); - statement_->nex_output(out); + if (statement_) statement_->nex_output(out); +} + +void NetForever::nex_output(NexusSet&out) +{ + if (statement_) statement_->nex_output(out); } void NetForLoop::nex_output(NexusSet&out) @@ -138,11 +149,20 @@ void NetForLoop::nex_output(NexusSet&out) if (statement_) statement_->nex_output(out); } +void NetFree::nex_output(NexusSet&) +{ +} + void NetPDelay::nex_output(NexusSet&out) { if (statement_) statement_->nex_output(out); } +void NetRepeat::nex_output(NexusSet&out) +{ + if (statement_) statement_->nex_output(out); +} + /* * For the purposes of synthesis, system task calls have no output at * all. This is OK because most system tasks are not synthesizable in @@ -163,6 +183,5 @@ void NetUTask::nex_output(NexusSet&) void NetWhile::nex_output(NexusSet&out) { - if (proc_ != 0) - proc_->nex_output(out); + if (proc_) proc_->nex_output(out); } diff --git a/netlist.cc b/netlist.cc index 70d65031e..106bb3be7 100644 --- a/netlist.cc +++ b/netlist.cc @@ -2779,12 +2779,20 @@ static DelayType get_loop_delay_type(const NetExpr*expr, const NetProc*proc, boo break; /* We have a constant true expression so the body always runs. */ case DEFINITE_DELAY: - result = proc->delay_type(print_delay); + if (proc) { + result = proc->delay_type(print_delay); + } else { + result = NO_DELAY; + } break; /* We don't know if the body will run so reduce a DEFINITE_DELAY * to a POSSIBLE_DELAY. All other stay the same. */ case POSSIBLE_DELAY: - result = combine_delays(NO_DELAY, proc->delay_type(print_delay)); + if (proc) { + result = combine_delays(NO_DELAY, proc->delay_type(print_delay)); + } else { + result = NO_DELAY; + } break; /* This should never happen since delay_type_from_expr() only * returns three different values. */ @@ -2814,7 +2822,7 @@ DelayType NetBlock::delay_type(bool print_delay) const for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { DelayType dt = cur->delay_type(print_delay); if (dt < result) result = dt; - if (dt == NO_DELAY) break; + if ((dt == NO_DELAY) && !print_delay) break; } // A begin or join has the maximum delay. @@ -2823,7 +2831,7 @@ DelayType NetBlock::delay_type(bool print_delay) const for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { DelayType dt = cur->delay_type(print_delay); if (dt > result) result = dt; - if (dt == DEFINITE_DELAY) break; + if ((dt == DEFINITE_DELAY) && !print_delay) break; } } @@ -2846,6 +2854,7 @@ DelayType NetCase::delay_type(bool print_delay) const } } +// FIXME: If all the cases are covered (e.g. an enum) then this is not true. /* If we don't have a default statement we don't know for sure * that we have a delay. */ if (!def_stmt) result = combine_delays(NO_DELAY, result); @@ -2865,29 +2874,31 @@ DelayType NetCondit::delay_type(bool print_delay) const */ DelayType NetDoWhile::delay_type(bool print_delay) const { - ivl_assert(*this, proc_); - return proc_->delay_type(print_delay); + if (proc_) return proc_->delay_type(print_delay); + + return ZERO_DELAY; } DelayType NetEvWait::delay_type(bool print_delay) const { if (print_delay) { cerr << get_fileline() << ": error: an event control is not allowed " - << "in an always_comb, always_ff or always_latch process." + "in an always_comb, always_ff or always_latch process." << endl; } + return DEFINITE_DELAY; } DelayType NetForever::delay_type(bool print_delay) const { - ivl_assert(*this, statement_); - return statement_->delay_type(print_delay); + if (statement_) return statement_->delay_type(print_delay); + + return ZERO_DELAY; } DelayType NetForLoop::delay_type(bool print_delay) const { - ivl_assert(*this, statement_); return get_loop_delay_type(condition_, statement_, print_delay); } @@ -2895,28 +2906,31 @@ DelayType NetPDelay::delay_type(bool print_delay) const { if (print_delay) { cerr << get_fileline() << ": error: a blocking delay is not allowed " - << "in an always_comb, always_ff or always_latch process." + "in an always_comb, always_ff or always_latch process." << endl; } + if (expr_) { - return delay_type_from_expr(expr_); - } else { - if (delay() > 0) { - return DEFINITE_DELAY; + if (statement_) { + return combine_delays(delay_type_from_expr(expr_), + statement_->delay_type(print_delay)); } else { - if (statement_) { - return combine_delays(ZERO_DELAY, - statement_->delay_type(print_delay)); - } else { - return ZERO_DELAY; - } + return delay_type_from_expr(expr_); } } + + if (delay() > 0) return DEFINITE_DELAY; + + if (statement_) { + return combine_delays(ZERO_DELAY, + statement_->delay_type(print_delay)); + } else { + return ZERO_DELAY; + } } DelayType NetRepeat::delay_type(bool print_delay) const { - ivl_assert(*this, statement_); return get_loop_delay_type(expr_, statement_, print_delay); } @@ -2930,8 +2944,512 @@ DelayType NetUTask::delay_type(bool print_delay) const return task()->task_def()->delay_type(print_delay); } +static bool do_expr_event_match(const NetExpr*expr, const NetEvWait*evwt) +{ + // The event wait should only have a single event. + if (evwt->nevents() != 1) return false; + // The event should have a single probe. + const NetEvent* evt = evwt->event(0); + if (evt->nprobe() != 1) return false; + // The probe should be for any edge. + const NetEvProbe *prb = evt->probe(0); + if (prb->edge() != NetEvProbe::ANYEDGE) return false; + // Create a NexusSet from the event probe signals. + NexusSet *ns_evwt = new NexusSet; + for (unsigned idx =0; idx < prb->pin_count(); idx += 1) { + if (! prb->pin(idx).is_linked()) { + delete ns_evwt; + return false; + } + // Casting away const is safe since this nexus set is only being read. + ns_evwt->add(const_cast (prb->pin(idx).nexus()), + 0, prb->pin(idx).nexus()->vector_width()); + } + // Get the NexusSet for the expression + NexusSet *ns_expr = expr->nex_input(); + // Make sure the event and expression NexusSets match exactly + if (ns_evwt->size() != ns_expr->size()) { + delete ns_evwt; + delete ns_expr; + return false; + } + ns_expr->rem(*ns_evwt); + delete ns_evwt; + if (ns_expr->size() != 0) { + delete ns_expr; + return false; + } + delete ns_expr; + + return true; +} + +static bool while_is_wait(const NetExpr*expr, const NetProc*stmt) +{ + if (const NetEvWait*evwt = dynamic_cast(stmt)) { + if (evwt->statement()) return false; + const NetEBComp*cond = dynamic_cast(expr); + if (! cond) return false; + if (cond->op() != 'N') return false; + const NetEConst*cval = dynamic_cast(cond->right()); + if (! cval) return false; + const verinum val = cval->value(); + if (val.len() != 1) return false; + if (val.get(0) != verinum::V1) return false; + if (! do_expr_event_match(cond->left(), evwt)) return false; + if (evwt->get_lineno() != cond->get_lineno()) return false; + if (evwt->get_file() != cond->get_file()) return false; + return true; + } + return false; +} + DelayType NetWhile::delay_type(bool print_delay) const { - ivl_assert(*this, proc_); + // If the wait was a constant value the compiler already removed it + // so we know we can only have a possible delay. + if (while_is_wait(cond_, proc_)) { + if (print_delay) { + cerr << get_fileline() << ": error: a wait statement is " + "not allowed in an " + "always_comb, always_ff or always_latch process." + << endl; + } + return POSSIBLE_DELAY; + } return get_loop_delay_type(cond_, proc_, print_delay); } + +/* + * These are the check_synth() functions. They are used to print + * a warning if the item is not synthesizable. + */ +static const char * get_process_type_as_string(ivl_process_type_t pr_type) +{ + switch (pr_type) { + case IVL_PR_ALWAYS_COMB: + return "in an always_comb process."; + break; + case IVL_PR_ALWAYS_FF: + return "in an always_ff process."; + break; + case IVL_PR_ALWAYS_LATCH: + return "in an always_latch process."; + break; + default: + assert(0); + return 0; + } +} + +static void print_synth_warning(const NetProc *net_proc, const char *name, + ivl_process_type_t pr_type) +{ + cerr << net_proc->get_fileline() << ": warning: " << name + << " statement cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; +} + +static void check_if_logic_l_value(const NetAssignBase *base, + ivl_process_type_t pr_type) +{ + if (base->l_val_count() != 1) return; + + const NetAssign_*lval = base->l_val(0); + if (! lval) return; + + NetNet*sig = lval->sig(); + if (! sig) return; + + if ((sig->data_type() != IVL_VT_BOOL) && + (sig->data_type() != IVL_VT_LOGIC)) { + cerr << base->get_fileline() << ": warning: Assinging to a " + "non-integral variable ("<< sig->name() + << ") cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; + } +} + +/* By default elements can be synthesized or ignored. */ +bool NetProc::check_synth(ivl_process_type_t /* pr_type */, + const NetScope* /* scope */ ) const +{ + return false; +} + +/* By default assign elements can be synthesized. */ +bool NetAssignBase::check_synth(ivl_process_type_t /* pr_type */, + const NetScope* /* scope */ ) const +{ + return false; +} + +bool NetAssign::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + check_if_logic_l_value(this, pr_type); + +// FIXME: Check that ff/latch only use this for internal signals. + return false; +} + +bool NetAssignNB::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + bool result = false; + if (pr_type == IVL_PR_ALWAYS_COMB) { + cerr << get_fileline() << ": warning: A non-blocking assignment " + "should not be used in an always_comb process." << endl; + } + + if (event_) { + cerr << get_fileline() << ": error: A non-blocking assignment " + "cannot be synthesized with an event control " + << get_process_type_as_string(pr_type) << endl; + result = true; + } + + check_if_logic_l_value(this, pr_type); + + return result; +} + +bool NetBlock::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + // Only a begin/end can be synthesized. + if (type() != SEQU) { + cerr << get_fileline() << ": error: A fork/"; + switch (type()) { + case PARA: + cerr << "join"; + break; + case PARA_JOIN_ANY: + cerr << "join_any"; + break; + case PARA_JOIN_NONE: + cerr << "join_none"; + break; + default: + assert(0); + } + cerr << " statement cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; + result = true; + } + + const NetScope*save_scope = scope; + if (subscope()) scope = subscope(); + if (scope != save_scope) { + result |= scope->check_synth(pr_type, scope); + } + for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { + result |= cur->check_synth(pr_type, scope); + } + scope = save_scope; + return result; +} + +bool NetCase::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + for (unsigned idx = 0; idx < nitems(); idx += 1) { + if (stat(idx)) result |= stat(idx)->check_synth(pr_type, scope); + } +// FIXME: Check for ff/latch/comb structures. + return result; +} + +bool NetCAssign::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "A procedural assign", pr_type); + return false; +} + +bool NetCondit::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + if (if_) result |= if_->check_synth(pr_type, scope); + if (else_) result |= else_->check_synth(pr_type, scope); +// FIXME: Check for ff/latch/comb structures. + return result; +} + +bool NetDeassign::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "A procedural deassign", pr_type); + return false; +} + +bool NetDisable::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + while (scope) { + if (scope != target_) scope = scope->parent(); + else break; + } + + + if (! scope) { + cerr << get_fileline() << ": warning: A disable statement can " + "only be synthesized when disabling an enclosing block " + << get_process_type_as_string(pr_type) << endl; + } + return false; +} + +bool NetDoWhile::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + print_synth_warning(this, "A do/while", pr_type); + if (proc_) result |= proc_->check_synth(pr_type, scope); + return result; +} + +bool NetEvTrig::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "An event trigger", pr_type); + return false; +} + +// The delay check above has already marked this as an error. +bool NetEvWait::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + if (statement_) result |= statement_->check_synth(pr_type, scope); + return result; +} + +bool NetForce::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "A force", pr_type); + return false; +} + +bool NetForever::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + print_synth_warning(this, "A forever", pr_type); + if (statement_) result |= statement_->check_synth(pr_type, scope); + return result; +} + +/* + * A bunch of private routines to verify that a for loop has the correct + * structure for synthesis. + */ +static void print_for_idx_warning(const NetProc*proc, const char*check, + ivl_process_type_t pr_type, NetNet*idx) +{ + cerr << proc->get_fileline() << ": warning: A for statement must use " + "the index (" << idx->name() << ") in the " << check + << " expression to be synthesized " + << get_process_type_as_string(pr_type) << endl; +} + +static void check_for_const_synth(const NetExpr*expr, const NetProc*proc, + const char*str, ivl_process_type_t pr_type) +{ + if (! dynamic_cast(expr)) { + cerr << proc-> get_fileline() << ": warning: A for " + "statement must " << str + << " value to be synthesized " + << get_process_type_as_string(pr_type) << endl; + } +} + +static void check_for_bin_synth(const NetExpr*left,const NetExpr*right, + const char*str, const char*check, + const NetProc*proc, + ivl_process_type_t pr_type, NetNet*index) +{ + const NetESignal*lsig = dynamic_cast(left); + const NetESignal*rsig = dynamic_cast(right); + + if (lsig && (lsig->sig() == index)) { + check_for_const_synth(right, proc, str, pr_type); + } else if (rsig && (rsig->sig() == index)) { + check_for_const_synth(left, proc, str, pr_type); + } else { + print_for_idx_warning(proc, check, pr_type, index); + } +} + +static void print_for_step_warning(const NetProc*proc, + ivl_process_type_t pr_type) +{ + cerr << proc->get_fileline() << ": warning: A for statement step must " + "be a simple assignment statement to be synthesized " + << get_process_type_as_string(pr_type) << endl; +} + +static void print_for_step_warning(const NetProc*proc, + ivl_process_type_t pr_type, NetNet*idx) +{ + cerr << proc->get_fileline() << ": warning: A for statement step must " + "be an assignment to the index variable (" + << idx->name() << ") to be synthesized " + << get_process_type_as_string(pr_type) << endl; +} + +static void check_for_bstep_synth(const NetExpr*expr, const NetProc*proc, + ivl_process_type_t pr_type, NetNet*index) +{ + if (const NetECast*tmp = dynamic_cast(expr)) { + expr = tmp->expr(); + } + + if (const NetEBAdd*tmp = dynamic_cast(expr)) { + check_for_bin_synth(tmp->left(), tmp->right(), + "change by a constant", "step", proc, pr_type, + index); + } else { + cerr << proc->get_fileline() << ": warning: A for statement " + "step must be a simple binary +/- " + "to be synthesized " + << get_process_type_as_string(pr_type) << endl; + } +} + +static void check_for_step_synth(const NetAssign*assign, const NetProc*proc, + ivl_process_type_t pr_type, NetNet*index) +{ + if (assign->l_val_count() != 1) { + print_for_step_warning(proc, pr_type); + } else if (assign->l_val(0)->sig() != index) { + print_for_step_warning(proc, pr_type, index); + } else { + switch (assign->assign_operator()) { + case '+': + case '-': + check_for_const_synth(assign->rval(), proc, + "have a constant step", pr_type); + break; + case 0: + check_for_bstep_synth(assign->rval(), proc, pr_type, index); + break; + default: + cerr << proc->get_fileline() << ": warning: A for statement " + "step does not support operator '" + << assign->assign_operator() + << "' it must be +/- to be synthesized " + << get_process_type_as_string(pr_type) << endl; + break; + } + } +} + +bool NetForLoop::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + +// FIXME: What about an enum (NetEConstEnum)? + if (! dynamic_cast(init_expr_)) { + cerr << get_fileline() << ": warning: A for statement must " + "have a constant initial value to be synthesized " + << get_process_type_as_string(pr_type) << endl; + } + +// FIXME: Do the following also need to be supported in the condition? +// It would seem like they are hard to use to find the bounds. +// From NetEBinary +// What about NetEBits sig & constant, etc. +// From NetEUnary +// What about NetEUBits ! sig or ! (sig == constat) +// What about NetEUReduce &signal + if (const NetESignal*tmp = dynamic_cast(condition_)) { + if (tmp->sig() != index_) { + print_for_idx_warning(this, "condition", pr_type, index_); + } + } else if (const NetEBComp*cmp = dynamic_cast(condition_)) { + check_for_bin_synth(cmp->left(), cmp->right(), + "compare against a constant", "condition", + this, pr_type, index_); + } else { + print_for_idx_warning(this, "condition", pr_type, index_); + } + + if (const NetAssign*tmp = dynamic_cast(step_statement_)) { + check_for_step_synth(tmp, this, pr_type, index_); + } else { + print_for_step_warning(this, pr_type); + } + + if (statement_) result |= statement_->check_synth(pr_type, scope); + return result; +} + +// The delay check above has already marked this as an error. +bool NetPDelay::check_synth(ivl_process_type_t /* pr_type */, + const NetScope* /* scope */ ) const +{ + return false; +} + +bool NetRelease::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "A release", pr_type); + return false; +} + +bool NetRepeat::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + print_synth_warning(this, "A repeat", pr_type); + if (statement_) result |= statement_->check_synth(pr_type, scope); + return result; +} + +bool NetScope::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */) const +{ + bool result = false; + // Skip local events/signals + for (NetEvent*cur = events_ ; cur ; cur = cur->snext_) { + if (cur->local_flag()) continue; + cerr << cur->get_fileline() << ": warning: An event (" + << cur->name() << ") cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; + } + for (signals_map_iter_t cur = signals_map_.begin(); + cur != signals_map_.end() ; ++ cur) { + const NetNet*sig = cur->second; + if ((sig->data_type() != IVL_VT_BOOL) && + (sig->data_type() != IVL_VT_LOGIC)) { + cerr << sig->get_fileline() << ": warning: A non-integral " + "variable (" << sig->name() << ") cannot be " + "synthesized " + << get_process_type_as_string(pr_type) << endl; + } + } + return result; +} + +// FIXME: User task and function calls still need to be checked. +// : System task enables should be ignored and constant system functions +// : should also be okay. Non-constant system functions are an error. + +bool NetWhile::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + // A wait is already maked as an error in the delay check above. + if (! while_is_wait(cond_, proc_)) { + print_synth_warning(this, "A while", pr_type); + if (proc_) result |= proc_->check_synth(pr_type, scope); + } + return result; +} diff --git a/netlist.h b/netlist.h index 2f431269e..8b129de54 100644 --- a/netlist.h +++ b/netlist.h @@ -1158,6 +1158,9 @@ class NetScope : public Definitions, public Attrib { perm_string local_symbol(); void dump(ostream&) const; + // Check to see if the scope has items that are not allowed + // in an always_comb/ff/latch process. + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; void emit_scope(struct target_t*tgt) const; bool emit_defs(struct target_t*tgt) const; @@ -2716,6 +2719,8 @@ class NetProc : public virtual LineInfo { // Recursively checks to see if there is delay in this element. virtual DelayType delay_type(bool print_delay=false) const; + // Check to see if the item is synthesizable. + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; protected: bool synth_async_block_substatement_(Design*des, NetScope*scope, @@ -2743,6 +2748,8 @@ class NetAlloc : public NetProc { const NetScope* scope() const; + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; @@ -2913,6 +2920,7 @@ class NetAssignBase : public NetProc { // This dumps all the lval structures. void dump_lval(ostream&) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: NetAssign_*lval_; @@ -2934,6 +2942,7 @@ class NetAssign : public NetAssignBase { virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -2956,6 +2965,7 @@ class NetAssignNB : public NetAssignBase { virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; unsigned nevents() const; const NetEvent*event(unsigned) const; @@ -3019,6 +3029,7 @@ class NetBlock : public NetProc { virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: const Type type_; @@ -3066,6 +3077,7 @@ class NetCase : public NetProc { virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3102,9 +3114,9 @@ class NetCAssign : public NetAssignBase { explicit NetCAssign(NetAssign_*lv, NetExpr*rv); ~NetCAssign(); - virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; virtual void dump(ostream&, unsigned ind) const; virtual bool emit_proc(struct target_t*) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: // not implemented NetCAssign(const NetCAssign&); @@ -3156,6 +3168,7 @@ class NetCondit : public NetProc { virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3201,6 +3214,7 @@ class NetDeassign : public NetAssignBase { virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: // not implemented NetDeassign(const NetDeassign&); @@ -3224,8 +3238,11 @@ class NetDisable : public NetProc { const NetScope*target() const; + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3257,6 +3274,7 @@ class NetDoWhile : public NetProc { virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3313,6 +3331,9 @@ class NetEvent : public LineInfo { perm_string name() const; + bool local_flag() const { return local_flag_; } + void local_flag(bool f) { local_flag_ = f; } + // Get information about probes connected to me. unsigned nprobe() const; NetEvProbe* probe(unsigned); @@ -3344,6 +3365,7 @@ class NetEvent : public LineInfo { private: perm_string name_; + bool local_flag_; // The NetScope class uses these to list the events. NetScope*scope_; @@ -3381,8 +3403,11 @@ class NetEvTrig : public NetProc { const NetEvent*event() const; + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: NetEvent*event_; @@ -3440,6 +3465,7 @@ class NetEvWait : public NetProc { // This will ignore any statement. virtual void dump_inline(ostream&) const; virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: NetProc*statement_; @@ -3488,10 +3514,9 @@ class NetForce : public NetAssignBase { explicit NetForce(NetAssign_*l, NetExpr*r); ~NetForce(); - virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; - virtual void dump(ostream&, unsigned ind) const; virtual bool emit_proc(struct target_t*) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; }; /* @@ -3507,9 +3532,11 @@ class NetForever : public NetProc { void emit_recurse(struct target_t*) const; virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3533,6 +3560,7 @@ class NetForLoop : public NetProc { virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3565,6 +3593,8 @@ class NetFree : public NetProc { const NetScope* scope() const; + virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; @@ -3634,6 +3664,7 @@ class NetPDelay : public NetProc { virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; bool emit_proc_recurse(struct target_t*) const; @@ -3656,9 +3687,11 @@ class NetRepeat : public NetProc { void emit_recurse(struct target_t*) const; virtual NexusSet* nex_input(bool rem_out = true, bool search_funcs = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3681,6 +3714,7 @@ class NetRelease : public NetAssignBase { virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: }; @@ -3882,11 +3916,12 @@ class NetWhile : public NetProc { virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; private: - NetExpr* cond_; + NetExpr*cond_; NetProc*proc_; }; @@ -4954,6 +4989,7 @@ class Design { void add_process(NetAnalogTop*); void delete_process(NetProcTop*); bool check_proc_delay() const; + bool check_proc_synth() const; NetNet* find_discipline_reference(ivl_discipline_t dis, NetScope*scope);