Add synthesis of for-loops

This required keeping for-loops as actual things through the
netlist form so that the synthesizer can get at and understand
the parts of the for-loop. This may improve vvp code generation
in the future, but for now continue to present to the vvp code
generation the block-while form.
This commit is contained in:
Stephen Williams 2014-05-01 20:37:33 -07:00
parent ec0c66ff25
commit be0c61051d
11 changed files with 250 additions and 34 deletions

View File

@ -1165,6 +1165,13 @@ void NetForever::dump(ostream&o, unsigned ind) const
statement_->dump(o, ind+2); 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 void NetFree::dump(ostream&o, unsigned ind) const
{ {
o << setw(ind) << "// free storage : " << scope_path(scope_) << endl; o << setw(ind) << "// free storage : " << scope_path(scope_) << endl;

View File

@ -4549,15 +4549,12 @@ NetForce* PForce::elaborate(Design*des, NetScope*scope) const
*/ */
NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const
{ {
NetExpr*etmp; NetExpr*initial_expr;
assert(scope); assert(scope);
const PEIdent*id1 = dynamic_cast<const PEIdent*>(name1_); const PEIdent*id1 = dynamic_cast<const PEIdent*>(name1_);
assert(id1); assert(id1);
NetBlock*top = new NetBlock(NetBlock::SEQU, 0);
top->set_line(*this);
/* make the expression, and later the initial assignment to /* make the expression, and later the initial assignment to
the condition variable. The statement in the for loop is the condition variable. The statement in the for loop is
very specifically an assignment. */ very specifically an assignment. */
@ -4569,34 +4566,23 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const
return 0; return 0;
} }
assert(sig); assert(sig);
NetAssign_*lv = new NetAssign_(sig);
/* Make the r-value of the initial assignment, and size it /* Make the r-value of the initial assignment, and size it
properly. Then use it to build the assignment statement. */ properly. Then use it to build the assignment statement. */
etmp = elaborate_rval_expr(des, scope, sig->net_type(), initial_expr = elaborate_rval_expr(des, scope, sig->net_type(),
lv->expr_type(), lv->lwidth(), sig->data_type(), sig->vector_width(),
expr1_); expr1_);
if (debug_elaborate) { if (debug_elaborate) {
cerr << get_fileline() << ": debug: FOR initial assign: " 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 /* Elaborate the statement that is contained in the for
loop. If there is an error, this will return 0 and I should loop. If there is an error, this will return 0 and I should
skip the append. No need to worry, the error has been skip the append. No need to worry, the error has been
reported so it's OK that the netlist is bogus. */ reported so it's OK that the netlist is bogus. */
NetProc*tmp = statement_->elaborate(des, scope); NetProc*sub = statement_->elaborate(des, scope);
if (tmp)
body->append(tmp);
/* Now elaborate the for_step statement. I really should do /* 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. */ really does step the variable. */
if (debug_elaborate) { if (debug_elaborate) {
cerr << get_fileline() << ": debug: Elaborate for_step statement " cerr << get_fileline() << ": debug: Elaborate for_step statement "
<< sig->name() << " = " << *etmp << endl; << sig->name() << " = " << *initial_expr << endl;
} }
NetProc*step = step_->elaborate(des, scope); NetProc*step = step_->elaborate(des, scope);
body->append(step);
/* Elaborate the condition expression. Try to evaluate it too, /* Elaborate the condition expression. Try to evaluate it too,
in case it is a constant. This is an interesting case in case it is a constant. This is an interesting case
worthy of a warning. */ worthy of a warning. */
NetExpr*ce = elab_and_eval(des, scope, cond_, -1); NetExpr*ce = elab_and_eval(des, scope, cond_, -1);
if (ce == 0) { if (ce == 0) {
delete top; delete sub;
delete step;
return 0; return 0;
} }
@ -4629,10 +4614,10 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const
/* All done, build up the loop. */ /* 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); loop->set_line(*this);
top->append(loop); loop->wrap_up();
return top; return loop;
} }
/* /*

View File

@ -283,6 +283,11 @@ bool NetForever::emit_proc(struct target_t*tgt) const
return true; 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 bool NetFree::emit_proc(struct target_t*tgt) const
{ {
tgt->proc_free(this); tgt->proc_free(this);

View File

@ -1271,6 +1271,22 @@ NetNet* NetETernary::synthesize(Design *des, NetScope*scope, NetExpr*root)
*/ */
NetNet* NetESignal::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) if (word_ == 0)
return net_; return net_;

View File

@ -628,6 +628,16 @@ bool NetForever::evaluate_function(const LineInfo&loc,
return flag; 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<perm_string,LocalVar>&context_map) const
{
return as_block_->evaluate_function(loc, context_map);
}
bool NetRepeat::evaluate_function(const LineInfo&loc, bool NetRepeat::evaluate_function(const LineInfo&loc,
map<perm_string,LocalVar>&context_map) const map<perm_string,LocalVar>&context_map) const
{ {

View File

@ -406,6 +406,25 @@ NexusSet* NetForce::nex_input(bool)
return new NexusSet; 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* NetForever::nex_input(bool rem_out)
{ {
NexusSet*result = statement_->nex_input(rem_out); NexusSet*result = statement_->nex_input(rem_out);

View File

@ -134,6 +134,11 @@ void NetEvWait::nex_output(NexusSet&out)
statement_->nex_output(out); statement_->nex_output(out);
} }
void NetForLoop::nex_output(NexusSet&out)
{
if (statement_) statement_->nex_output(out);
}
void NetPDelay::nex_output(NexusSet&out) void NetPDelay::nex_output(NexusSet&out)
{ {
if (statement_) statement_->nex_output(out); if (statement_) statement_->nex_output(out);

View File

@ -126,6 +126,43 @@ NetForever::~NetForever()
delete statement_; 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) NetPDelay::NetPDelay(uint64_t d, NetProc*st)
: delay_(d), expr_(0), statement_(st) : delay_(d), expr_(0), statement_(st)
{ {

View File

@ -2756,6 +2756,11 @@ DelayType NetForever::delay_type() const
return statement_->delay_type(); return statement_->delay_type();
} }
DelayType NetForLoop::delay_type() const
{
return get_loop_delay_type(condition_, statement_);
}
DelayType NetPDelay::delay_type() const DelayType NetPDelay::delay_type() const
{ {
if (expr_) { if (expr_) {

View File

@ -1172,6 +1172,8 @@ class NetScope : public Definitions, public Attrib {
perm_string genvar_tmp; perm_string genvar_tmp;
long genvar_tmp_val; long genvar_tmp_val;
std::map<perm_string,LocalVar> loop_index_tmp;
private: private:
void evaluate_parameter_logic_(Design*des, param_ref_t cur); void evaluate_parameter_logic_(Design*des, param_ref_t cur);
void evaluate_parameter_real_(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_; 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<perm_string,LocalVar>&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 { class NetFree : public NetProc {
public: public:

101
synth2.cc
View File

@ -100,16 +100,22 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope,
<< ", nex_out.pin_count()==" << nex_out.pin_count() << endl; << ", nex_out.pin_count()==" << nex_out.pin_count() << endl;
} }
ivl_assert(*this, nex_out.pin_count()==1); // Here we note if the l-value is actually a bit/part
ivl_assert(*this, rsig->pin_count()==1); // select. If so, generate a NetPartSelect to perform the select.
connect(nex_out.pin(0), rsig->pin(0));
#if 0
if (lval_->lwidth() != lsig->vector_width()) { if (lval_->lwidth() != lsig->vector_width()) {
ivl_assert(*this, 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; 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); assert(0);
} }
ivl_assert(*this, base_off >= 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)); connect(ps->pin(0), rsig->pin(0));
rsig = tmp; 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 /* This lval_ represents a reg that is a WIRE in the
synthesized results. This function signals the destructor synthesized results. This function signals the destructor
@ -533,6 +543,83 @@ bool NetEvWait::synth_async(Design*des, NetScope*scope,
return flag; 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<NetAssign*> (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<perm_string,LocalVar> 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 * This method is called when the process is shown to be
* asynchronous. Figure out the nexus set of outputs from this * asynchronous. Figure out the nexus set of outputs from this