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
#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
* and/or modify it in source code form under the terms of the GNU
@ -36,6 +36,7 @@ class PProcess;
class PClass;
class PTask;
class PWire;
class Statement;
class Design;
class NetScope;
@ -108,6 +109,9 @@ class LexicalScope {
// creating implicit nets.
map<perm_string,LineInfo*> genvars;
// Variable initializations in this scope
vector<Statement*> var_inits;
// Behaviors (processes) in this scope
list<PProcess*> behaviors;
list<AProcess*> analog_behaviors;
@ -130,6 +134,10 @@ class LexicalScope {
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:
LexicalScope*parent_;
};

View File

@ -2894,14 +2894,27 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const
des->errors += 1;
return 0;
}
assert(nscope);
elaborate_behaviors_(des, 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)
nscope = scope;
@ -5023,7 +5036,6 @@ void PFunction::elaborate(Design*des, NetScope*scope) const
des->errors += 1;
return;
}
assert(def);
ivl_assert(*this, statement_);
@ -5036,6 +5048,31 @@ void PFunction::elaborate(Design*des, NetScope*scope) const
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);
}
@ -5198,11 +5235,6 @@ NetProc* PReturn::elaborate(Design*des, NetScope*scope) 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();
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);
}
@ -5677,6 +5734,10 @@ bool Module::elaborate(Design*des, NetScope*scope) const
(*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
// involves scanning the PProcess* list, creating a NetProcTop
// 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 )
(*cur)->elaborate(des, scope);
elaborate_var_inits_(des, scope);
typedef list<PProcess*>::const_iterator proc_it_t;
for (proc_it_t cur = behaviors.begin(); cur != behaviors.end(); ++ cur )
(*cur)->elaborate(des, scope);
@ -5902,6 +5965,37 @@ bool PScope::elaborate_behaviors_(Design*des, NetScope*scope) const
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 {
public:
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.
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) {
cerr << loc.get_fileline() << ": NetFuncDef::evaluate_function: "
<< "Function " << scope_path(scope())
@ -505,6 +509,10 @@ bool NetBlock::evaluate_function(const LineInfo&loc,
// Now collect the new locals.
subscope_->evaluate_function_find_locals(loc, local_context_map);
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

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
{
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;
}
var_init_ = 0;
switch (t) {
case NetScope::TASK:
task_ = 0;

View File

@ -1020,6 +1020,13 @@ class NetScope : public Definitions, public Attrib {
TYPE type() 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_func_def(NetFuncDef*);
void set_class_def(netclass_t*);
@ -1250,6 +1257,8 @@ class NetScope : public Definitions, public Attrib {
vector<PortInfo> ports_;
const NetProc*var_init_;
union {
NetTaskDef*task_;
NetFuncDef*func_;
@ -2904,6 +2913,7 @@ class NetBlock : public NetProc {
NetScope* subscope() const { return subscope_; }
void append(NetProc*);
void prepend(NetProc*);
const NetProc*proc_first() 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.data_type = $4;
pform_make_reginit(@5, name, $7);
pform_make_var_init(@5, name, $7);
delete[]$5;
$$ = ptmp;
@ -4622,7 +4622,7 @@ module_item
IVL_VT_NO_TYPE, $1, SR_BOTH);
for (pp = $6->begin(); pp != $6->end(); ++ pp ) {
if ((*pp).second) {
pform_make_reginit(@2, (*pp).first, (*pp).second);
pform_make_var_init(@2, (*pp).first, (*pp).second);
}
}
delete $6;
@ -5486,7 +5486,7 @@ register_variable
pform_makewire(@1, name, NetNet::REG,
NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0);
pform_set_reg_idx(name, $2);
pform_make_reginit(@1, name, $4);
pform_make_var_init(@1, name, $4);
$$ = $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
* in the source. It handles the case where a reg variable is assigned
* where it it declared:
* This function makes the initial assignment to a variable as given
* in the source. It handles the case where a variable is assigned
* where it is declared, e.g.
*
* 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;
* initial foo = <expr>;
*
* and that is how it is parsed. This syntax is not part of the
* IEEE1364-1995 standard, but is approved by OVI as enhancement
* BTF-B14.
* In SystemVerilog, variable initializations are allowed in any scope.
* For static variables, initializations are performed before the start
* 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,
perm_string name, PExpr*expr)
void pform_make_var_init(const struct vlltype&li,
perm_string name, PExpr*expr)
{
if (! pform_at_module_level() && !gn_system_verilog()) {
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);
if (cur == 0) {
VLerror(li, "internal error: reginit to non-register?");
VLerror(li, "internal error: var_init to non-register?");
delete expr;
return;
}
@ -2284,10 +2291,8 @@ void pform_make_reginit(const struct vlltype&li,
FILE_NAME(lval, li);
PAssign*ass = new PAssign(lval, expr, true);
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<named_pexpr_t>*attr);
extern void pform_make_reginit(const struct vlltype&li,
perm_string name, PExpr*expr);
extern void pform_make_var_init(const struct vlltype&li,
perm_string name, PExpr*expr);
/* 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

View File

@ -789,6 +789,8 @@ void PBlock::dump(ostream&out, unsigned ind) const
dump_events_(out, ind+2);
dump_wires_(out, ind+2);
dump_var_inits_(out, ind+2);
}
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_var_inits_(out, ind+2);
if (statement_)
statement_->dump(out, ind+2);
else
@ -1072,6 +1076,8 @@ void PTask::dump(ostream&out, unsigned ind) const
dump_wires_(out, ind+2);
dump_var_inits_(out, ind+2);
if (statement_)
statement_->dump(out, ind+2);
else
@ -1269,6 +1275,8 @@ void PGenerate::dump(ostream&out, unsigned indent) const
(*idx)->dump(out, indent+2);
}
dump_var_inits_(out, indent+2);
for (list<PProcess*>::const_iterator idx = behaviors.begin()
; idx != behaviors.end() ; ++ idx ) {
(*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
{
// Dump the task definitions.
@ -1564,6 +1580,7 @@ void Module::dump(ostream&out) const
(*gate)->dump(out);
}
dump_var_inits_(out, 4);
for (list<PProcess*>::const_iterator behav = behaviors.begin()
; behav != behaviors.end() ; ++ behav ) {