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);
}
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;

View File

@ -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<const PEIdent*>(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;
}
/*

View File

@ -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);

View File

@ -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_;

View File

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

View File

@ -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);

View File

@ -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);

View File

@ -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)
{

View File

@ -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_) {

View File

@ -1172,6 +1172,8 @@ class NetScope : public Definitions, public Attrib {
perm_string genvar_tmp;
long genvar_tmp_val;
std::map<perm_string,LocalVar> 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<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 {
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;
}
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<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
* asynchronous. Figure out the nexus set of outputs from this