diff --git a/design_dump.cc b/design_dump.cc index 9a8a256d7..0004cc3b4 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1165,6 +1165,13 @@ void NetForever::dump(ostream&o, unsigned ind) const statement_->dump(o, ind+2); } +void NetForLoop::dump(ostream&fd, unsigned ind) const +{ + fd << setw(ind) << "" << "FOR LOOP index=" << index_->name() << endl; + statement_->dump(fd, ind+4); + step_statement_->dump(fd, ind+4); +} + void NetFree::dump(ostream&o, unsigned ind) const { o << setw(ind) << "// free storage : " << scope_path(scope_) << endl; diff --git a/elaborate.cc b/elaborate.cc index 4c203db17..be406747e 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -4549,15 +4549,12 @@ NetForce* PForce::elaborate(Design*des, NetScope*scope) const */ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const { - NetExpr*etmp; + NetExpr*initial_expr; assert(scope); const PEIdent*id1 = dynamic_cast(name1_); assert(id1); - NetBlock*top = new NetBlock(NetBlock::SEQU, 0); - top->set_line(*this); - /* make the expression, and later the initial assignment to the condition variable. The statement in the for loop is very specifically an assignment. */ @@ -4569,34 +4566,23 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const return 0; } assert(sig); - NetAssign_*lv = new NetAssign_(sig); /* Make the r-value of the initial assignment, and size it properly. Then use it to build the assignment statement. */ - etmp = elaborate_rval_expr(des, scope, sig->net_type(), - lv->expr_type(), lv->lwidth(), - expr1_); + initial_expr = elaborate_rval_expr(des, scope, sig->net_type(), + sig->data_type(), sig->vector_width(), + expr1_); if (debug_elaborate) { cerr << get_fileline() << ": debug: FOR initial assign: " - << sig->name() << " = " << *etmp << endl; + << sig->name() << " = " << *initial_expr << endl; } - NetAssign*init = new NetAssign(lv, etmp); - init->set_line(*this); - - top->append(init); - - NetBlock*body = new NetBlock(NetBlock::SEQU, 0); - body->set_line(*this); - /* Elaborate the statement that is contained in the for loop. If there is an error, this will return 0 and I should skip the append. No need to worry, the error has been reported so it's OK that the netlist is bogus. */ - NetProc*tmp = statement_->elaborate(des, scope); - if (tmp) - body->append(tmp); + NetProc*sub = statement_->elaborate(des, scope); /* Now elaborate the for_step statement. I really should do @@ -4604,20 +4590,19 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const really does step the variable. */ if (debug_elaborate) { cerr << get_fileline() << ": debug: Elaborate for_step statement " - << sig->name() << " = " << *etmp << endl; + << sig->name() << " = " << *initial_expr << endl; } NetProc*step = step_->elaborate(des, scope); - body->append(step); - /* Elaborate the condition expression. Try to evaluate it too, in case it is a constant. This is an interesting case worthy of a warning. */ NetExpr*ce = elab_and_eval(des, scope, cond_, -1); if (ce == 0) { - delete top; + delete sub; + delete step; return 0; } @@ -4629,10 +4614,10 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const /* All done, build up the loop. */ - NetWhile*loop = new NetWhile(ce, body); + NetForLoop*loop = new NetForLoop(sig, initial_expr, ce, sub, step); loop->set_line(*this); - top->append(loop); - return top; + loop->wrap_up(); + return loop; } /* diff --git a/emit.cc b/emit.cc index 4da13d0d3..7f8634b20 100644 --- a/emit.cc +++ b/emit.cc @@ -283,6 +283,11 @@ bool NetForever::emit_proc(struct target_t*tgt) const return true; } +bool NetForLoop::emit_proc(struct target_t*tgt) const +{ + return tgt->proc_block(as_block_); +} + bool NetFree::emit_proc(struct target_t*tgt) const { tgt->proc_free(this); diff --git a/expr_synth.cc b/expr_synth.cc index 8812f23f0..f709668b9 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -1271,6 +1271,22 @@ NetNet* NetETernary::synthesize(Design *des, NetScope*scope, NetExpr*root) */ NetNet* NetESignal::synthesize(Design*des, NetScope*scope, NetExpr*root) { + // If this is a synthesis with a specific value for the + // signal, then replace it (here) with a constant value. + if (net_->scope()==scope && net_->name()==scope->genvar_tmp) { + netvector_t*tmp_vec = new netvector_t(net_->data_type(), + net_->vector_width()-1, 0); + NetNet*tmp = new NetNet(scope, scope->local_symbol(), + NetNet::IMPLICIT, tmp_vec); + verinum tmp_val ((uint64_t)scope->genvar_tmp_val, net_->vector_width()); + NetConst*tmp_const = new NetConst(scope, scope->local_symbol(), tmp_val); + tmp_const->set_line(*this); + des->add_node(tmp_const); + + connect(tmp->pin(0), tmp_const->pin(0)); + return tmp; + } + if (word_ == 0) return net_; diff --git a/net_func_eval.cc b/net_func_eval.cc index d130f3723..a5b336b11 100644 --- a/net_func_eval.cc +++ b/net_func_eval.cc @@ -628,6 +628,16 @@ bool NetForever::evaluate_function(const LineInfo&loc, return flag; } +/* + * For now, resort to the block form of the statement until we learn + * to do this directly. + */ +bool NetForLoop::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + return as_block_->evaluate_function(loc, context_map); +} + bool NetRepeat::evaluate_function(const LineInfo&loc, map&context_map) const { diff --git a/net_nex_input.cc b/net_nex_input.cc index fa2d9ecfc..50053bf04 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -406,6 +406,25 @@ NexusSet* NetForce::nex_input(bool) return new NexusSet; } +NexusSet* NetForLoop::nex_input(bool rem_out) +{ + NexusSet*result = init_expr_->nex_input(rem_out); + + NexusSet*tmp = condition_->nex_input(rem_out); + result->add(*tmp); + delete tmp; + + tmp = statement_->nex_input(rem_out); + result->add(*tmp); + delete tmp; + + tmp = step_statement_->nex_input(rem_out); + result->add(*tmp); + delete tmp; + + return result; +} + NexusSet* NetForever::nex_input(bool rem_out) { NexusSet*result = statement_->nex_input(rem_out); diff --git a/net_nex_output.cc b/net_nex_output.cc index 761937fb8..232f0a122 100644 --- a/net_nex_output.cc +++ b/net_nex_output.cc @@ -134,6 +134,11 @@ void NetEvWait::nex_output(NexusSet&out) statement_->nex_output(out); } +void NetForLoop::nex_output(NexusSet&out) +{ + if (statement_) statement_->nex_output(out); +} + void NetPDelay::nex_output(NexusSet&out) { if (statement_) statement_->nex_output(out); diff --git a/net_proc.cc b/net_proc.cc index d9412fa9d..081a4ea40 100644 --- a/net_proc.cc +++ b/net_proc.cc @@ -126,6 +126,43 @@ NetForever::~NetForever() delete statement_; } +NetForLoop::NetForLoop(NetNet*ind, NetExpr*iexpr, NetExpr*cond, NetProc*sub, NetProc*step) +: index_(ind), init_expr_(iexpr), condition_(cond), statement_(sub), step_statement_(step) +{ +} + +void NetForLoop::wrap_up() +{ + NetBlock*top = new NetBlock(NetBlock::SEQU, 0); + top->set_line(*this); + + NetAssign_*lv = new NetAssign_(index_); + NetAssign*set_stmt = new NetAssign(lv, init_expr_); + set_stmt->set_line(*init_expr_); + top->append(set_stmt); + + NetBlock*internal_block = new NetBlock(NetBlock::SEQU, 0); + internal_block->set_line(*this); + + internal_block->append(statement_); + internal_block->append(step_statement_); + + NetWhile*wloop = new NetWhile(condition_, internal_block); + wloop->set_line(*this); + + top->append(wloop); + + as_block_ = top; +} + +NetForLoop::~NetForLoop() +{ + delete init_expr_; + delete condition_; + delete statement_; + delete step_statement_; +} + NetPDelay::NetPDelay(uint64_t d, NetProc*st) : delay_(d), expr_(0), statement_(st) { diff --git a/netlist.cc b/netlist.cc index 4d184dc51..bd0070c6c 100644 --- a/netlist.cc +++ b/netlist.cc @@ -2756,6 +2756,11 @@ DelayType NetForever::delay_type() const return statement_->delay_type(); } +DelayType NetForLoop::delay_type() const +{ + return get_loop_delay_type(condition_, statement_); +} + DelayType NetPDelay::delay_type() const { if (expr_) { diff --git a/netlist.h b/netlist.h index 3242e26c0..bb8a2ac73 100644 --- a/netlist.h +++ b/netlist.h @@ -1172,6 +1172,8 @@ class NetScope : public Definitions, public Attrib { perm_string genvar_tmp; long genvar_tmp_val; + std::map loop_index_tmp; + private: void evaluate_parameter_logic_(Design*des, param_ref_t cur); void evaluate_parameter_real_(Design*des, param_ref_t cur); @@ -3308,6 +3310,44 @@ class NetForever : public NetProc { NetProc*statement_; }; +class NetForLoop : public NetProc { + + public: + explicit NetForLoop(NetNet*index, NetExpr*initial_expr, NetExpr*cond, + NetProc*sub, NetProc*step); + ~NetForLoop(); + + void wrap_up(); + + void emit_recurse(struct target_t*) const; + + virtual NexusSet* nex_input(bool rem_out = true); + virtual void nex_output(NexusSet&); + virtual bool emit_proc(struct target_t*) const; + virtual void dump(ostream&, unsigned ind) const; + virtual DelayType delay_type() const; + virtual bool evaluate_function(const LineInfo&loc, + map&ctx) const; + + // synthesize as asynchronous logic, and return true. + bool synth_async(Design*des, NetScope*scope, + NexusSet&nex_map, NetBus&nex_out, + NetBus&accumulated_nex_out); + + private: + NetNet*index_; + NetExpr*init_expr_; + NetExpr*condition_; + NetProc*statement_; + NetProc*step_statement_; + + // The code generator needs to see this rewritten as a while + // loop with synthetic statements. This is a hack that I + // should probably take out later as the ivl_target learns + // about for loops. + NetBlock*as_block_; +}; + class NetFree : public NetProc { public: diff --git a/synth2.cc b/synth2.cc index 560d553f6..577be4cd3 100644 --- a/synth2.cc +++ b/synth2.cc @@ -100,16 +100,22 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, << ", nex_out.pin_count()==" << nex_out.pin_count() << endl; } - ivl_assert(*this, nex_out.pin_count()==1); - ivl_assert(*this, rsig->pin_count()==1); - connect(nex_out.pin(0), rsig->pin(0)); - -#if 0 + // Here we note if the l-value is actually a bit/part + // select. If so, generate a NetPartSelect to perform the select. if (lval_->lwidth() != lsig->vector_width()) { ivl_assert(*this, lval_->lwidth() < lsig->vector_width()); + // XXXX If we ar within a NetForLoop or similar + // processing, then there may be an index value. I + // currently do not know how to handle that, but + // probably I'm going to need the index_args passed in. long base_off = 0; - if (! eval_as_long(base_off, lval_->get_base())) { + + // Evaluate the index expression to a constant. + const NetExpr*base_expr_raw = lval_->get_base(); + ivl_assert(*this, base_expr_raw); + NetExpr*base_expr = base_expr_raw->evaluate_function(*this, scope->loop_index_tmp); + if (! eval_as_long(base_off, base_expr)) { assert(0); } ivl_assert(*this, base_off >= 0); @@ -129,7 +135,11 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, connect(ps->pin(0), rsig->pin(0)); rsig = tmp; } -#endif + + ivl_assert(*this, nex_out.pin_count()==1); + ivl_assert(*this, rsig->pin_count()==1); + connect(nex_out.pin(0), rsig->pin(0)); + /* This lval_ represents a reg that is a WIRE in the synthesized results. This function signals the destructor @@ -533,6 +543,83 @@ bool NetEvWait::synth_async(Design*des, NetScope*scope, return flag; } +bool NetForLoop::synth_async(Design*des, NetScope*scope, + NexusSet&nex_map, NetBus&nex_out, + NetBus&accumulated_nex_out) +{ + if (debug_synth2) { + cerr << get_fileline() << ": NetForLoop::synth_async: " + << "Index variable is " << index_->name() << endl; + cerr << get_fileline() << ": NetForLoop::synth_async: " + << "Initialization expression: " << *init_expr_ << endl; + } + + // Get the step assignment statement and break it into the + // l-value (should be the index) and the r-value, which is the + // step expressions. + NetAssign*step_assign = dynamic_cast (step_statement_); + ivl_assert(*this, step_assign); + ivl_assert(*this, step_assign->assign_operator()==0); + NetExpr*step_expr = step_assign->rval(); + + // Tell the scope that this index value is like a genvar. + LocalVar index_var; + index_var.nwords = 0; + + map index_args; + + // Calculate the initial value for the index. + index_var.value = init_expr_->evaluate_function(*this, index_args); + ivl_assert(*this, index_var.value); + index_args[index_->name()] = index_var; + + for (;;) { + // Evaluate the condition expression. If it is false, + // then we are going to break out of this synthesis loop. + NetExpr*tmp = condition_->evaluate_function(*this, index_args); + ivl_assert(*this, tmp); + + long cond_value; + bool rc = eval_as_long(cond_value, tmp); + ivl_assert(*this, rc); + delete tmp; + if (!cond_value) break; + + scope->genvar_tmp = index_->name(); + rc = eval_as_long(scope->genvar_tmp_val, index_var.value); + ivl_assert(*this, rc); + + if (debug_synth2) { + cerr << get_fileline() << ": NetForLoop::synth_async: " + << "Synthesis iteration with " << index_->name() + << "=" << *index_var.value << endl; + } + + // Synthesize the iterated expression. Stash the loop + // index value so that the substatements can see this + // value and use it during its own synthesis. + ivl_assert(*this, scope->loop_index_tmp.empty()); + scope->loop_index_tmp = index_args; + rc = statement_->synth_async(des, scope, nex_map, nex_out, accumulated_nex_out); + scope->loop_index_tmp.clear(); + + // Evaluate the step_expr to generate the next index value. + tmp = step_expr->evaluate_function(*this, index_args); + ivl_assert(*this, tmp); + delete index_var.value; + index_var.value = tmp; + index_args[index_->name()] = index_var; + } + + delete index_var.value; +#if 0 + cerr << get_fileline() << ": sorry: Synthesis of for-loops not implemented." << endl; + return as_block_->synth_async(des, scope, nex_map, nex_out, accumulated_nex_out); +#else + return true; +#endif +} + /* * This method is called when the process is shown to be * asynchronous. Figure out the nexus set of outputs from this