diff --git a/PScope.h b/PScope.h index 30e1fea7e..f7eb73f96 100644 --- a/PScope.h +++ b/PScope.h @@ -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 genvars; + // Variable initializations in this scope + vector var_inits; + // Behaviors (processes) in this scope list behaviors; list 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_; }; diff --git a/elaborate.cc b/elaborate.cc index 26acf0933..d22d35735 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -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 (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 (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::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) diff --git a/net_func_eval.cc b/net_func_eval.cc index 9e045c63c..00790d692 100644 --- a/net_func_eval.cc +++ b/net_func_eval.cc @@ -88,6 +88,10 @@ NetExpr* NetFuncDef::evaluate_function(const LineInfo&loc, const std::vectorevaluate_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 diff --git a/net_proc.cc b/net_proc.cc index 2aedec7c2..8b6902b68 100644 --- a/net_proc.cc +++ b/net_proc.cc @@ -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) diff --git a/net_scope.cc b/net_scope.cc index 688266aff..438ccd729 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -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; diff --git a/netlist.h b/netlist.h index de561e706..68bc27319 100644 --- a/netlist.h +++ b/netlist.h @@ -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 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; diff --git a/parse.y b/parse.y index 739bf9b73..a901a63de 100644 --- a/parse.y +++ b/parse.y @@ -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; } ; diff --git a/pform.cc b/pform.cc index 594026993..bc1211907 100644 --- a/pform.cc +++ b/pform.cc @@ -2248,23 +2248,30 @@ void pform_make_pgassign_list(list*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 = ; * - * 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 = ; * - * 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); } /* diff --git a/pform.h b/pform.h index 2e7f8ea5e..e1ce49065 100644 --- a/pform.h +++ b/pform.h @@ -351,8 +351,8 @@ extern void pform_makewire(const struct vlltype&li, list*names, list*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 diff --git a/pform_dump.cc b/pform_dump.cc index afd2ddc27..bc44c0519 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -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::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::const_iterator behav = behaviors.begin() ; behav != behaviors.end() ; ++ behav ) {