Fully support variable initialization in tasks/functions/named blocks.

(cherry picked from commit 635adfc01e)
This commit is contained in:
Martin Whitaker 2016-03-19 13:04:38 +00:00
parent 9be3fc3a56
commit 3f75f6b155
10 changed files with 182 additions and 28 deletions

View File

@ -1,7 +1,7 @@
#ifndef IVL_PScope_H #ifndef IVL_PScope_H
#define IVL_PScope_H #define IVL_PScope_H
/* /*
* Copyright (c) 2008-2014 Stephen Williams (steve@icarus.com) * Copyright (c) 2008-2016 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -36,6 +36,7 @@ class PProcess;
class PClass; class PClass;
class PTask; class PTask;
class PWire; class PWire;
class Statement;
class Design; class Design;
class NetScope; class NetScope;
@ -108,6 +109,9 @@ class LexicalScope {
// creating implicit nets. // creating implicit nets.
map<perm_string,LineInfo*> genvars; map<perm_string,LineInfo*> genvars;
// Variable initializations in this scope
vector<Statement*> var_inits;
// Behaviors (processes) in this scope // Behaviors (processes) in this scope
list<PProcess*> behaviors; list<PProcess*> behaviors;
list<AProcess*> analog_behaviors; list<AProcess*> analog_behaviors;
@ -130,6 +134,10 @@ class LexicalScope {
void dump_wires_(ostream&out, unsigned indent) const; void dump_wires_(ostream&out, unsigned indent) const;
void dump_var_inits_(ostream&out, unsigned indent) const;
bool elaborate_var_inits_(Design*des, NetScope*scope) const;
private: private:
LexicalScope*parent_; LexicalScope*parent_;
}; };

View File

@ -2894,14 +2894,27 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const
des->errors += 1; des->errors += 1;
return 0; return 0;
} }
assert(nscope); assert(nscope);
elaborate_behaviors_(des, nscope);
} }
NetBlock*cur = new NetBlock(type, nscope); NetBlock*cur = new NetBlock(type, nscope);
if (nscope) {
// Handle any variable initialization statements in this scope.
// For automatic scopes these statements need to be executed
// each time the block is entered, so add them to the main
// block. For static scopes, put them in a separate process
// that will be executed at the start of simulation.
if (nscope->is_auto()) {
for (unsigned idx = 0; idx < var_inits.size(); idx += 1) {
NetProc*tmp = var_inits[idx]->elaborate(des, nscope);
if (tmp) cur->append(tmp);
}
} else {
elaborate_var_inits_(des, nscope);
}
}
if (nscope == 0) if (nscope == 0)
nscope = scope; nscope = scope;
@ -5023,7 +5036,6 @@ void PFunction::elaborate(Design*des, NetScope*scope) const
des->errors += 1; des->errors += 1;
return; return;
} }
assert(def); assert(def);
ivl_assert(*this, statement_); ivl_assert(*this, statement_);
@ -5036,6 +5048,31 @@ void PFunction::elaborate(Design*des, NetScope*scope) const
return; return;
} }
// Handle any variable initialization statements in this scope.
// For automatic functions, these statements need to be executed
// each time the function is called, so insert them at the start
// of the elaborated definition. For static functions, put them
// in a separate process that will be executed before the start
// of simulation.
if (is_auto_) {
// Get the NetBlock of the statement. If it is not a
// NetBlock then create one to wrap the initialization
// statements and the original statement.
NetBlock*blk = dynamic_cast<NetBlock*> (st);
if ((blk == 0) && (var_inits.size() > 0)) {
blk = new NetBlock(NetBlock::SEQU, scope);
blk->set_line(*this);
blk->append(st);
st = blk;
}
for (unsigned idx = var_inits.size(); idx > 0; idx -= 1) {
NetProc*tmp = var_inits[idx-1]->elaborate(des, scope);
if (tmp) blk->prepend(tmp);
}
} else {
elaborate_var_inits_(des, scope);
}
def->set_proc(st); def->set_proc(st);
} }
@ -5198,11 +5235,6 @@ NetProc* PReturn::elaborate(Design*des, NetScope*scope) const
void PTask::elaborate(Design*des, NetScope*task) const void PTask::elaborate(Design*des, NetScope*task) const
{ {
// Elaborate any processes that are part of this scope that
// aren't the definition itself. This can happen, for example,
// with variable initialization statements in this scope.
elaborate_behaviors_(des, task);
NetTaskDef*def = task->task_def(); NetTaskDef*def = task->task_def();
assert(def); assert(def);
@ -5221,6 +5253,31 @@ void PTask::elaborate(Design*des, NetScope*task) const
} }
} }
// Handle any variable initialization statements in this scope.
// For automatic tasks , these statements need to be executed
// each time the task is called, so insert them at the start
// of the elaborated definition. For static tasks, put them
// in a separate process that will be executed before the start
// of simulation.
if (is_auto_) {
// Get the NetBlock of the statement. If it is not a
// NetBlock then create one to wrap the initialization
// statements and the original statement.
NetBlock*blk = dynamic_cast<NetBlock*> (st);
if ((blk == 0) && (var_inits.size() > 0)) {
blk = new NetBlock(NetBlock::SEQU, task);
blk->set_line(*this);
blk->append(st);
st = blk;
}
for (unsigned idx = var_inits.size(); idx > 0; idx -= 1) {
NetProc*tmp = var_inits[idx-1]->elaborate(des, task);
if (tmp) blk->prepend(tmp);
}
} else {
elaborate_var_inits_(des, task);
}
def->set_proc(st); def->set_proc(st);
} }
@ -5677,6 +5734,10 @@ bool Module::elaborate(Design*des, NetScope*scope) const
(*gt)->elaborate(des, scope); (*gt)->elaborate(des, scope);
} }
// Elaborate the variable initialization statements, making a
// single initial process out of them.
result_flag &= elaborate_var_inits_(des, scope);
// Elaborate the behaviors, making processes out of them. This // Elaborate the behaviors, making processes out of them. This
// involves scanning the PProcess* list, creating a NetProcTop // involves scanning the PProcess* list, creating a NetProcTop
// for each process. // for each process.
@ -5867,6 +5928,8 @@ bool PGenerate::elaborate_(Design*des, NetScope*scope) const
for (gates_it_t cur = gates.begin() ; cur != gates.end() ; ++ cur ) for (gates_it_t cur = gates.begin() ; cur != gates.end() ; ++ cur )
(*cur)->elaborate(des, scope); (*cur)->elaborate(des, scope);
elaborate_var_inits_(des, scope);
typedef list<PProcess*>::const_iterator proc_it_t; typedef list<PProcess*>::const_iterator proc_it_t;
for (proc_it_t cur = behaviors.begin(); cur != behaviors.end(); ++ cur ) for (proc_it_t cur = behaviors.begin(); cur != behaviors.end(); ++ cur )
(*cur)->elaborate(des, scope); (*cur)->elaborate(des, scope);
@ -5902,6 +5965,37 @@ bool PScope::elaborate_behaviors_(Design*des, NetScope*scope) const
return result_flag; return result_flag;
} }
bool LexicalScope::elaborate_var_inits_(Design*des, NetScope*scope) const
{
if (var_inits.size() == 0)
return true;
NetProc*proc = 0;
if (var_inits.size() == 1) {
proc = var_inits[0]->elaborate(des, scope);
} else {
NetBlock*blk = new NetBlock(NetBlock::SEQU, scope);
bool flag = true;
for (unsigned idx = 0; idx < var_inits.size(); idx += 1) {
NetProc*tmp = var_inits[idx]->elaborate(des, scope);
if (tmp)
blk->append(tmp);
else
flag = false;
}
if (flag) proc = blk;
}
if (proc == 0)
return false;
NetProcTop*top = new NetProcTop(scope, IVL_PR_INITIAL, proc);
des->add_process(top);
scope->set_var_init(proc);
return true;
}
class elaborate_package_t : public elaborator_work_item_t { class elaborate_package_t : public elaborator_work_item_t {
public: public:
elaborate_package_t(Design*d, NetScope*scope, PPackage*p) elaborate_package_t(Design*d, NetScope*scope, PPackage*p)

View File

@ -88,6 +88,10 @@ NetExpr* NetFuncDef::evaluate_function(const LineInfo&loc, const std::vector<Net
// fills in the context_map with local variables held by the scope. // fills in the context_map with local variables held by the scope.
scope()->evaluate_function_find_locals(loc, context_map); scope()->evaluate_function_find_locals(loc, context_map);
// Execute any variable initialization statements.
if (const NetProc*init_proc = scope()->var_init())
init_proc->evaluate_function(loc, context_map);
if (debug_eval_tree && proc_==0) { if (debug_eval_tree && proc_==0) {
cerr << loc.get_fileline() << ": NetFuncDef::evaluate_function: " cerr << loc.get_fileline() << ": NetFuncDef::evaluate_function: "
<< "Function " << scope_path(scope()) << "Function " << scope_path(scope())
@ -505,6 +509,10 @@ bool NetBlock::evaluate_function(const LineInfo&loc,
// Now collect the new locals. // Now collect the new locals.
subscope_->evaluate_function_find_locals(loc, local_context_map); subscope_->evaluate_function_find_locals(loc, local_context_map);
use_local_context_map = true; use_local_context_map = true;
// Execute any variable initialization statements.
if (const NetProc*init_proc = subscope_->var_init())
init_proc->evaluate_function(loc, local_context_map);
} }
// Now use the local context map if there is any local // Now use the local context map if there is any local

View File

@ -56,6 +56,17 @@ void NetBlock::append(NetProc*cur)
} }
} }
void NetBlock::prepend(NetProc*cur)
{
if (last_ == 0) {
last_ = cur;
cur->next_ = cur;
} else {
cur->next_ = last_->next_;
last_->next_ = cur;
}
}
const NetProc* NetBlock::proc_first() const const NetProc* NetBlock::proc_first() const
{ {
if (last_ == 0) if (last_ == 0)

View File

@ -139,6 +139,7 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, bool nest,
time_from_timescale_ = false; time_from_timescale_ = false;
} }
var_init_ = 0;
switch (t) { switch (t) {
case NetScope::TASK: case NetScope::TASK:
task_ = 0; task_ = 0;

View File

@ -1020,6 +1020,13 @@ class NetScope : public Definitions, public Attrib {
TYPE type() const; TYPE type() const;
void print_type(ostream&) const; void print_type(ostream&) const;
// This provides a link to the variable initialisation process
// for use when evaluating a constant function. Note this is
// only used for static functions - the variable initialization
// for automatic functions is included in the function definition.
void set_var_init(const NetProc*proc) { var_init_ = proc; }
const NetProc* var_init() const { return var_init_; }
void set_task_def(NetTaskDef*); void set_task_def(NetTaskDef*);
void set_func_def(NetFuncDef*); void set_func_def(NetFuncDef*);
void set_class_def(netclass_t*); void set_class_def(netclass_t*);
@ -1250,6 +1257,8 @@ class NetScope : public Definitions, public Attrib {
vector<PortInfo> ports_; vector<PortInfo> ports_;
const NetProc*var_init_;
union { union {
NetTaskDef*task_; NetTaskDef*task_;
NetFuncDef*func_; NetFuncDef*func_;
@ -2904,6 +2913,7 @@ class NetBlock : public NetProc {
NetScope* subscope() const { return subscope_; } NetScope* subscope() const { return subscope_; }
void append(NetProc*); void append(NetProc*);
void prepend(NetProc*);
const NetProc*proc_first() const; const NetProc*proc_first() const;
const NetProc*proc_next(const NetProc*cur) const; const NetProc*proc_next(const NetProc*cur) const;

View File

@ -4223,7 +4223,7 @@ port_declaration
port_declaration_context.port_net_type = use_type; port_declaration_context.port_net_type = use_type;
port_declaration_context.data_type = $4; port_declaration_context.data_type = $4;
pform_make_reginit(@5, name, $7); pform_make_var_init(@5, name, $7);
delete[]$5; delete[]$5;
$$ = ptmp; $$ = ptmp;
@ -4622,7 +4622,7 @@ module_item
IVL_VT_NO_TYPE, $1, SR_BOTH); IVL_VT_NO_TYPE, $1, SR_BOTH);
for (pp = $6->begin(); pp != $6->end(); ++ pp ) { for (pp = $6->begin(); pp != $6->end(); ++ pp ) {
if ((*pp).second) { if ((*pp).second) {
pform_make_reginit(@2, (*pp).first, (*pp).second); pform_make_var_init(@2, (*pp).first, (*pp).second);
} }
} }
delete $6; delete $6;
@ -5486,7 +5486,7 @@ register_variable
pform_makewire(@1, name, NetNet::REG, pform_makewire(@1, name, NetNet::REG,
NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0); NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0);
pform_set_reg_idx(name, $2); pform_set_reg_idx(name, $2);
pform_make_reginit(@1, name, $4); pform_make_var_init(@1, name, $4);
$$ = $1; $$ = $1;
} }
; ;

View File

@ -2248,23 +2248,30 @@ void pform_make_pgassign_list(list<PExpr*>*alist,
} }
/* /*
* this function makes the initial assignment to a register as given * This function makes the initial assignment to a variable as given
* in the source. It handles the case where a reg variable is assigned * in the source. It handles the case where a variable is assigned
* where it it declared: * where it is declared, e.g.
* *
* reg foo = <expr>; * reg foo = <expr>;
* *
* This is equivalent to the combination of statements: * In Verilog-2001 this is only supported at the module level, and is
* equivalent to the combination of statements:
* *
* reg foo; * reg foo;
* initial foo = <expr>; * initial foo = <expr>;
* *
* and that is how it is parsed. This syntax is not part of the * In SystemVerilog, variable initializations are allowed in any scope.
* IEEE1364-1995 standard, but is approved by OVI as enhancement * For static variables, initializations are performed before the start
* BTF-B14. * of simulation. For automatic variables, initializations are performed
* each time the enclosing block is entered. Here we store the variable
* assignments in the current scope, and later elaboration creates an
* initialization block that will be executed at the appropriate time.
*
* This syntax is not part of the IEEE1364-1995 standard, but is
* approved by OVI as enhancement BTF-B14.
*/ */
void pform_make_reginit(const struct vlltype&li, void pform_make_var_init(const struct vlltype&li,
perm_string name, PExpr*expr) perm_string name, PExpr*expr)
{ {
if (! pform_at_module_level() && !gn_system_verilog()) { if (! pform_at_module_level() && !gn_system_verilog()) {
VLerror(li, "error: variable declaration assignments are only " VLerror(li, "error: variable declaration assignments are only "
@ -2275,7 +2282,7 @@ void pform_make_reginit(const struct vlltype&li,
PWire*cur = pform_get_wire_in_scope(name); PWire*cur = pform_get_wire_in_scope(name);
if (cur == 0) { if (cur == 0) {
VLerror(li, "internal error: reginit to non-register?"); VLerror(li, "internal error: var_init to non-register?");
delete expr; delete expr;
return; return;
} }
@ -2284,10 +2291,8 @@ void pform_make_reginit(const struct vlltype&li,
FILE_NAME(lval, li); FILE_NAME(lval, li);
PAssign*ass = new PAssign(lval, expr, true); PAssign*ass = new PAssign(lval, expr, true);
FILE_NAME(ass, li); FILE_NAME(ass, li);
PProcess*top = new PProcess(IVL_PR_INITIAL, ass);
FILE_NAME(top, li);
pform_put_behavior_in_scope(top); lexical_scope->var_inits.push_back(ass);
} }
/* /*

View File

@ -351,8 +351,8 @@ extern void pform_makewire(const struct vlltype&li,
list<perm_string>*names, list<perm_string>*names,
list<named_pexpr_t>*attr); list<named_pexpr_t>*attr);
extern void pform_make_reginit(const struct vlltype&li, extern void pform_make_var_init(const struct vlltype&li,
perm_string name, PExpr*expr); perm_string name, PExpr*expr);
/* Look up the names of the wires, and set the port type, /* Look up the names of the wires, and set the port type,
i.e. input, output or inout. If the wire does not exist, create i.e. input, output or inout. If the wire does not exist, create

View File

@ -789,6 +789,8 @@ void PBlock::dump(ostream&out, unsigned ind) const
dump_events_(out, ind+2); dump_events_(out, ind+2);
dump_wires_(out, ind+2); dump_wires_(out, ind+2);
dump_var_inits_(out, ind+2);
} }
for (unsigned idx = 0 ; idx < list_.size() ; idx += 1) { for (unsigned idx = 0 ; idx < list_.size() ; idx += 1) {
@ -1030,6 +1032,8 @@ void PFunction::dump(ostream&out, unsigned ind) const
dump_wires_(out, ind+2); dump_wires_(out, ind+2);
dump_var_inits_(out, ind+2);
if (statement_) if (statement_)
statement_->dump(out, ind+2); statement_->dump(out, ind+2);
else else
@ -1072,6 +1076,8 @@ void PTask::dump(ostream&out, unsigned ind) const
dump_wires_(out, ind+2); dump_wires_(out, ind+2);
dump_var_inits_(out, ind+2);
if (statement_) if (statement_)
statement_->dump(out, ind+2); statement_->dump(out, ind+2);
else else
@ -1269,6 +1275,8 @@ void PGenerate::dump(ostream&out, unsigned indent) const
(*idx)->dump(out, indent+2); (*idx)->dump(out, indent+2);
} }
dump_var_inits_(out, indent+2);
for (list<PProcess*>::const_iterator idx = behaviors.begin() for (list<PProcess*>::const_iterator idx = behaviors.begin()
; idx != behaviors.end() ; ++ idx ) { ; idx != behaviors.end() ; ++ idx ) {
(*idx)->dump(out, indent+2); (*idx)->dump(out, indent+2);
@ -1408,6 +1416,14 @@ void LexicalScope::dump_wires_(ostream&out, unsigned indent) const
} }
} }
void LexicalScope::dump_var_inits_(ostream&out, unsigned indent) const
{
// Iterate through and display all the register initializations.
for (unsigned idx = 0; idx < var_inits.size(); idx += 1) {
var_inits[idx]->dump(out, indent);
}
}
void PScopeExtra::dump_classes_(ostream&out, unsigned indent) const void PScopeExtra::dump_classes_(ostream&out, unsigned indent) const
{ {
// Dump the task definitions. // Dump the task definitions.
@ -1564,6 +1580,7 @@ void Module::dump(ostream&out) const
(*gate)->dump(out); (*gate)->dump(out);
} }
dump_var_inits_(out, 4);
for (list<PProcess*>::const_iterator behav = behaviors.begin() for (list<PProcess*>::const_iterator behav = behaviors.begin()
; behav != behaviors.end() ; ++ behav ) { ; behav != behaviors.end() ; ++ behav ) {