Merge branch 'x-mil2'
This commit is contained in:
commit
cec68cfc61
|
|
@ -27,11 +27,12 @@
|
|||
list<Module::named_expr_t> Module::user_defparms;
|
||||
|
||||
/* n is a permallocated string. */
|
||||
Module::Module(perm_string n)
|
||||
: PScopeExtra(n)
|
||||
Module::Module(LexicalScope*parent, perm_string n)
|
||||
: PScopeExtra(n, parent)
|
||||
{
|
||||
library_flag = false;
|
||||
is_cell = false;
|
||||
program_block = false;
|
||||
uc_drive = UCD_NONE;
|
||||
timescale_warn_done = false;
|
||||
time_unit = 0;
|
||||
|
|
|
|||
11
Module.h
11
Module.h
|
|
@ -65,7 +65,7 @@ class Module : public PScopeExtra, public LineInfo {
|
|||
public:
|
||||
/* The name passed here is the module name, not the instance
|
||||
name. This make must be a permallocated string. */
|
||||
explicit Module(perm_string name);
|
||||
explicit Module(LexicalScope*parent, perm_string name);
|
||||
~Module();
|
||||
|
||||
/* Initially false. This is set to true if the module has been
|
||||
|
|
@ -76,6 +76,11 @@ class Module : public PScopeExtra, public LineInfo {
|
|||
|
||||
bool is_cell;
|
||||
|
||||
/* This is true if the module represents a program block
|
||||
instead of a module/cell. Program blocks have content
|
||||
restrictions and slightly modify scheduling semantics. */
|
||||
bool program_block;
|
||||
|
||||
enum UCDriveType { UCD_NONE, UCD_PULL0, UCD_PULL1 };
|
||||
UCDriveType uc_drive;
|
||||
|
||||
|
|
@ -116,6 +121,10 @@ class Module : public PScopeExtra, public LineInfo {
|
|||
the module definition. These are used at elaboration time. */
|
||||
list<PGenerate*> generate_schemes;
|
||||
|
||||
/* Nested modules are placed here, and are not elaborated
|
||||
unless they are instantiated, implicitly or explicitly. */
|
||||
std::map<perm_string,Module*> nested_modules;
|
||||
|
||||
list<PSpecPath*> specify_paths;
|
||||
|
||||
// The mod_name() is the name of the module type.
|
||||
|
|
|
|||
10
PGate.cc
10
PGate.cc
|
|
@ -260,7 +260,7 @@ const char* PGBuiltin::gate_name() const
|
|||
}
|
||||
|
||||
PGModule::PGModule(perm_string type, perm_string name, list<PExpr*>*pins)
|
||||
: PGate(name, pins), overrides_(0), pins_(0),
|
||||
: PGate(name, pins), bound_type_(0), overrides_(0), pins_(0),
|
||||
npins_(0), parms_(0), nparms_(0), msb_(0), lsb_(0)
|
||||
{
|
||||
type_ = type;
|
||||
|
|
@ -268,12 +268,18 @@ PGModule::PGModule(perm_string type, perm_string name, list<PExpr*>*pins)
|
|||
|
||||
PGModule::PGModule(perm_string type, perm_string name,
|
||||
named<PExpr*>*pins, unsigned npins)
|
||||
: PGate(name, 0), overrides_(0), pins_(pins),
|
||||
: PGate(name, 0), bound_type_(0), overrides_(0), pins_(pins),
|
||||
npins_(npins), parms_(0), nparms_(0), msb_(0), lsb_(0)
|
||||
{
|
||||
type_ = type;
|
||||
}
|
||||
|
||||
PGModule::PGModule(Module*type, perm_string name)
|
||||
: PGate(name, 0), bound_type_(type), overrides_(0), pins_(0),
|
||||
npins_(0), parms_(0), nparms_(0), msb_(0), lsb_(0)
|
||||
{
|
||||
}
|
||||
|
||||
PGModule::~PGModule()
|
||||
{
|
||||
}
|
||||
|
|
|
|||
4
PGate.h
4
PGate.h
|
|
@ -201,6 +201,9 @@ class PGModule : public PGate {
|
|||
explicit PGModule(perm_string type, perm_string name,
|
||||
named<PExpr*>*pins, unsigned npins);
|
||||
|
||||
// If the module type is known by design, then use this
|
||||
// constructor.
|
||||
explicit PGModule(Module*type, perm_string name);
|
||||
|
||||
~PGModule();
|
||||
|
||||
|
|
@ -223,6 +226,7 @@ class PGModule : public PGate {
|
|||
perm_string get_type() const;
|
||||
|
||||
private:
|
||||
Module*bound_type_;
|
||||
perm_string type_;
|
||||
list<PExpr*>*overrides_;
|
||||
named<PExpr*>*pins_;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1998-2008,2010 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 1998-2008,2010,2012 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
|
||||
|
|
@ -114,6 +114,13 @@ PBlock::~PBlock()
|
|||
delete list_[idx];
|
||||
}
|
||||
|
||||
void PBlock::set_join_type(PBlock::BL_TYPE type)
|
||||
{
|
||||
assert(bl_type_ == BL_PAR);
|
||||
assert(type==BL_PAR || type==BL_JOIN_NONE || type==BL_JOIN_ANY);
|
||||
bl_type_ = type;
|
||||
}
|
||||
|
||||
void PBlock::set_statement(const vector<Statement*>&st)
|
||||
{
|
||||
list_ = st;
|
||||
|
|
|
|||
10
Statement.h
10
Statement.h
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef __Statement_H
|
||||
#define __Statement_H
|
||||
/*
|
||||
* Copyright (c) 1998-2008 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 1998-2008,2012 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
|
||||
|
|
@ -165,7 +165,7 @@ class PAssignNB : public PAssign_ {
|
|||
class PBlock : public PScope, public Statement {
|
||||
|
||||
public:
|
||||
enum BL_TYPE { BL_SEQ, BL_PAR };
|
||||
enum BL_TYPE { BL_SEQ, BL_PAR, BL_JOIN_NONE, BL_JOIN_ANY };
|
||||
|
||||
// If the block has a name, it is a scope and also has a parent.
|
||||
explicit PBlock(perm_string n, LexicalScope*parent, BL_TYPE t);
|
||||
|
|
@ -175,6 +175,10 @@ class PBlock : public PScope, public Statement {
|
|||
|
||||
BL_TYPE bl_type() const { return bl_type_; }
|
||||
|
||||
// If the bl_type() is BL_PAR, it is possible to replace it
|
||||
// with JOIN_NONE or JOIN_ANY. This is to help the parser.
|
||||
void set_join_type(BL_TYPE);
|
||||
|
||||
void set_statement(const std::vector<Statement*>&st);
|
||||
|
||||
virtual void dump(ostream&out, unsigned ind) const;
|
||||
|
|
@ -183,7 +187,7 @@ class PBlock : public PScope, public Statement {
|
|||
virtual void elaborate_sig(Design*des, NetScope*scope) const;
|
||||
|
||||
private:
|
||||
const BL_TYPE bl_type_;
|
||||
BL_TYPE bl_type_;
|
||||
std::vector<Statement*>list_;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -177,6 +177,11 @@ static inline bool gn_system_verilog(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline bool gn_modules_nest(void)
|
||||
{
|
||||
return gn_system_verilog();
|
||||
}
|
||||
|
||||
/* The bits of these GN_KEYWORDS_* constants define non-intersecting
|
||||
sets of keywords. The compiler enables groups of keywords by setting
|
||||
lexor_keyword_mask with the OR of the bits for the keywords to be
|
||||
|
|
|
|||
|
|
@ -40,6 +40,12 @@ static ostream& operator<< (ostream&o, NetBlock::Type t)
|
|||
case NetBlock::PARA:
|
||||
o << "fork";
|
||||
break;
|
||||
case NetBlock::PARA_JOIN_NONE:
|
||||
o << "fork-join_none";
|
||||
break;
|
||||
case NetBlock::PARA_JOIN_ANY:
|
||||
o << "fork-join_any";
|
||||
break;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
|
@ -1136,6 +1142,9 @@ void NetScope::dump(ostream&o) const
|
|||
print_type(o);
|
||||
if (is_auto()) o << " (automatic)";
|
||||
if (is_cell()) o << " (cell)";
|
||||
if (nested_module()) o << " (nested)";
|
||||
if (program_block()) o << " (program)";
|
||||
|
||||
o << endl;
|
||||
|
||||
for (unsigned idx = 0 ; idx < attr_cnt() ; idx += 1)
|
||||
|
|
|
|||
|
|
@ -693,7 +693,7 @@ NetNet* PEIdent::elaborate_bi_net(Design*des, NetScope*scope) const
|
|||
*/
|
||||
NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const
|
||||
{
|
||||
assert(scope->type() == NetScope::MODULE);
|
||||
ivl_assert(*this, scope->type() == NetScope::MODULE);
|
||||
NetNet*sig = des->find_signal(scope, path_);
|
||||
if (sig == 0) {
|
||||
cerr << get_fileline() << ": error: no wire/reg " << path_
|
||||
|
|
|
|||
|
|
@ -541,6 +541,19 @@ bool Module::elaborate_scope(Design*des, NetScope*scope,
|
|||
|
||||
elaborate_scope_funcs(des, scope, funcs);
|
||||
|
||||
// Look for implicit modules and implicit gates for them.
|
||||
|
||||
for (map<perm_string,Module*>::iterator cur = nested_modules.begin()
|
||||
; cur != nested_modules.end() ; ++cur) {
|
||||
// Skip modules that must be explicitly instantiated.
|
||||
if (cur->second->port_count() > 0)
|
||||
continue;
|
||||
|
||||
PGModule*nested_gate = new PGModule(cur->second, cur->second->mod_name());
|
||||
nested_gate->set_line(*cur->second);
|
||||
gates_.push_back(nested_gate);
|
||||
}
|
||||
|
||||
// Gates include modules, which might introduce new scopes, so
|
||||
// scan all of them to create those scopes.
|
||||
|
||||
|
|
@ -1329,8 +1342,12 @@ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*s
|
|||
<< "." << endl;
|
||||
}
|
||||
|
||||
// Create the new scope as a MODULE with my name.
|
||||
NetScope*my_scope = new NetScope(sc, use_name, NetScope::MODULE);
|
||||
// Create the new scope as a MODULE with my name. Note
|
||||
// that if this is a nested module, mark it thus so that
|
||||
// scope searches will continue into the parent scope.
|
||||
NetScope*my_scope = new NetScope(sc, use_name, NetScope::MODULE,
|
||||
bound_type_? true : false,
|
||||
mod->program_block);
|
||||
my_scope->set_line(get_file(), mod->get_file(),
|
||||
get_lineno(), mod->get_lineno());
|
||||
my_scope->set_module_name(mod->mod_name());
|
||||
|
|
@ -1547,7 +1564,9 @@ void PBlock::elaborate_scope(Design*des, NetScope*scope) const
|
|||
<< "Elaborate block scope " << use_name
|
||||
<< " within " << scope_path(scope) << endl;
|
||||
|
||||
my_scope = new NetScope(scope, use_name, bl_type_==BL_PAR
|
||||
// The scope type is begin-end or fork-join. The
|
||||
// sub-types of fork-join are not interesting to the scope.
|
||||
my_scope = new NetScope(scope, use_name, bl_type_!=BL_SEQ
|
||||
? NetScope::FORK_JOIN
|
||||
: NetScope::BEGIN_END);
|
||||
my_scope->set_line(get_file(), get_lineno());
|
||||
|
|
|
|||
81
elaborate.cc
81
elaborate.cc
|
|
@ -2068,6 +2068,10 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const
|
|||
|
||||
bool PGModule::elaborate_sig(Design*des, NetScope*scope) const
|
||||
{
|
||||
if (bound_type_) {
|
||||
return elaborate_sig_mod_(des, scope, bound_type_);
|
||||
}
|
||||
|
||||
// Look for the module type
|
||||
map<perm_string,Module*>::const_iterator mod = pform_modules.find(type_);
|
||||
if (mod != pform_modules.end())
|
||||
|
|
@ -2087,6 +2091,11 @@ bool PGModule::elaborate_sig(Design*des, NetScope*scope) const
|
|||
|
||||
void PGModule::elaborate(Design*des, NetScope*scope) const
|
||||
{
|
||||
if (bound_type_) {
|
||||
elaborate_mod_(des, bound_type_, scope);
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for the module type
|
||||
map<perm_string,Module*>::const_iterator mod = pform_modules.find(type_);
|
||||
if (mod != pform_modules.end()) {
|
||||
|
|
@ -2108,10 +2117,16 @@ void PGModule::elaborate(Design*des, NetScope*scope) const
|
|||
|
||||
void PGModule::elaborate_scope(Design*des, NetScope*sc) const
|
||||
{
|
||||
// If the module type is known by design, then go right to it.
|
||||
if (bound_type_) {
|
||||
elaborate_scope_mod_(des, bound_type_, sc);
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for the module type
|
||||
map<perm_string,Module*>::const_iterator mod = pform_modules.find(type_);
|
||||
if (mod != pform_modules.end()) {
|
||||
elaborate_scope_mod_(des, (*mod).second, sc);
|
||||
elaborate_scope_mod_(des, mod->second, sc);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2128,7 +2143,7 @@ void PGModule::elaborate_scope(Design*des, NetScope*sc) const
|
|||
// Try again to find the module type
|
||||
mod = pform_modules.find(type_);
|
||||
if (mod != pform_modules.end()) {
|
||||
elaborate_scope_mod_(des, (*mod).second, sc);
|
||||
elaborate_scope_mod_(des, mod->second, sc);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2297,6 +2312,18 @@ NetProc* PAssign::elaborate_compressed_(Design*des, NetScope*scope) const
|
|||
return cur;
|
||||
}
|
||||
|
||||
static bool lval_not_program_variable(const NetAssign_*lv)
|
||||
{
|
||||
while (lv) {
|
||||
NetScope*sig_scope = lv->sig()->scope();
|
||||
if (! sig_scope->program_block())
|
||||
return true;
|
||||
|
||||
lv = lv->more;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
NetProc* PAssign::elaborate(Design*des, NetScope*scope) const
|
||||
{
|
||||
assert(scope);
|
||||
|
|
@ -2311,6 +2338,12 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const
|
|||
NetAssign_*lv = elaborate_lval(des, scope);
|
||||
if (lv == 0) return 0;
|
||||
|
||||
if (scope->program_block() && lval_not_program_variable(lv)) {
|
||||
cerr << get_fileline() << ": error: Blocking assignments to "
|
||||
<< "non-program variables are not allowed." << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
|
||||
/* If there is an internal delay expression, elaborate it. */
|
||||
NetExpr*delay = 0;
|
||||
if (delay_ != 0)
|
||||
|
|
@ -2454,6 +2487,22 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const
|
|||
return cur;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if any lvalue parts are in a program block scope.
|
||||
*/
|
||||
static bool lval_is_program_variable(const NetAssign_*lv)
|
||||
{
|
||||
while (lv) {
|
||||
NetScope*sig_scope = lv->sig()->scope();
|
||||
if (sig_scope->program_block())
|
||||
return true;
|
||||
|
||||
lv = lv->more;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Elaborate non-blocking assignments. The statement is of the general
|
||||
* form:
|
||||
|
|
@ -2490,6 +2539,14 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const
|
|||
NetAssign_*lv = elaborate_lval(des, scope);
|
||||
if (lv == 0) return 0;
|
||||
|
||||
if (scope->program_block() && lval_is_program_variable(lv)) {
|
||||
cerr << get_fileline() << ": error: Non-blocking assignments to "
|
||||
<< "program variables are not allowed." << endl;
|
||||
des->errors += 1;
|
||||
// This is an error, but we can let elaboration continue
|
||||
// because it would necessarily trigger other errors.
|
||||
}
|
||||
|
||||
NetExpr*rv = elaborate_rval_(des, scope, count_lval_width(lv), lv->expr_type());
|
||||
if (rv == 0) return 0;
|
||||
|
||||
|
|
@ -2576,9 +2633,21 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const
|
|||
{
|
||||
assert(scope);
|
||||
|
||||
NetBlock::Type type = (bl_type_==PBlock::BL_PAR)
|
||||
? NetBlock::PARA
|
||||
: NetBlock::SEQU;
|
||||
NetBlock::Type type;
|
||||
switch (bl_type_) {
|
||||
case PBlock::BL_SEQ:
|
||||
type = NetBlock::SEQU;
|
||||
break;
|
||||
case PBlock::BL_PAR:
|
||||
type = NetBlock::PARA;
|
||||
break;
|
||||
case PBlock::BL_JOIN_NONE:
|
||||
type = NetBlock::PARA_JOIN_NONE;
|
||||
break;
|
||||
case PBlock::BL_JOIN_ANY:
|
||||
type = NetBlock::PARA_JOIN_ANY;
|
||||
break;
|
||||
}
|
||||
|
||||
NetScope*nscope = 0;
|
||||
if (pscope_name() != 0) {
|
||||
|
|
@ -4833,7 +4902,7 @@ Design* elaborate(list<perm_string>roots)
|
|||
|
||||
// Make the root scope. This makes a NetScope object and
|
||||
// pushes it into the list of root scopes in the Design.
|
||||
NetScope*scope = des->make_root_scope(*root);
|
||||
NetScope*scope = des->make_root_scope(*root, rmod->program_block);
|
||||
|
||||
// Collect some basic properties of this scope from the
|
||||
// Module definition.
|
||||
|
|
|
|||
10
ivl_target.h
10
ivl_target.h
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef __ivl_target_H
|
||||
#define __ivl_target_H
|
||||
/*
|
||||
* Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2000-2012 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
|
||||
|
|
@ -406,6 +406,8 @@ typedef enum ivl_statement_type_e {
|
|||
IVL_ST_FORCE = 14,
|
||||
IVL_ST_FOREVER = 15,
|
||||
IVL_ST_FORK = 16,
|
||||
IVL_ST_FORK_JOIN_ANY = 28,
|
||||
IVL_ST_FORK_JOIN_NONE = 29,
|
||||
IVL_ST_FREE = 26,
|
||||
IVL_ST_RELEASE = 17,
|
||||
IVL_ST_REPEAT = 18,
|
||||
|
|
@ -2069,11 +2071,11 @@ extern unsigned ivl_stmt_lineno(ivl_statement_t net);
|
|||
* triggers. The statement waits even if the sub-statement is nul.
|
||||
*/
|
||||
|
||||
/* IVL_ST_BLOCK, IVL_ST_FORK */
|
||||
/* IVL_ST_BLOCK, IVL_ST_FORK, IVL_ST_FORK_JOIN_ANY, IVL_ST_FORK_JOIN_NONE */
|
||||
extern unsigned ivl_stmt_block_count(ivl_statement_t net);
|
||||
/* IVL_ST_BLOCK, IVL_ST_FORK */
|
||||
/* IVL_ST_BLOCK, IVL_ST_FORK, IVL_ST_FORK_JOIN_ANY, IVL_ST_FORK_JOIN_NONE */
|
||||
extern ivl_scope_t ivl_stmt_block_scope(ivl_statement_t net);
|
||||
/* IVL_ST_BLOCK, IVL_ST_FORK */
|
||||
/* IVL_ST_BLOCK, IVL_ST_FORK, IVL_ST_FORK_JOIN_ANY, IVL_ST_FORK_JOIN_NONE */
|
||||
extern ivl_statement_t ivl_stmt_block_stmt(ivl_statement_t net, unsigned i);
|
||||
/* IVL_ST_UTASK IVL_ST_DISABLE */
|
||||
extern ivl_scope_t ivl_stmt_call(ivl_statement_t net);
|
||||
|
|
|
|||
|
|
@ -101,10 +101,11 @@ uint64_t Design::scale_to_precision(uint64_t val,
|
|||
return val;
|
||||
}
|
||||
|
||||
NetScope* Design::make_root_scope(perm_string root)
|
||||
NetScope* Design::make_root_scope(perm_string root, bool program_block)
|
||||
{
|
||||
NetScope *root_scope_;
|
||||
root_scope_ = new NetScope(0, hname_t(root), NetScope::MODULE);
|
||||
root_scope_ = new NetScope(0, hname_t(root), NetScope::MODULE,
|
||||
false, program_block);
|
||||
/* This relies on the fact that the basename return value is
|
||||
permallocated. */
|
||||
root_scope_->set_module_name(root_scope_->basename());
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2002-2011 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2002-2012 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
|
||||
|
|
@ -257,7 +257,7 @@ NexusSet* NetBlock::nex_input(bool rem_out)
|
|||
if (last_ == 0)
|
||||
return new NexusSet;
|
||||
|
||||
if (type_ == PARA) {
|
||||
if (type_ != SEQU) {
|
||||
cerr << get_fileline() << ": internal error: Sorry, "
|
||||
<< "I don't know how to synthesize fork/join blocks."
|
||||
<< endl;
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ class PExpr;
|
|||
* in question.
|
||||
*/
|
||||
|
||||
NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t)
|
||||
: type_(t), name_(n), up_(up)
|
||||
NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, bool nest, bool prog)
|
||||
: type_(t), name_(n), nested_module_(nest), program_block_(prog), up_(up)
|
||||
{
|
||||
events_ = 0;
|
||||
lcounter_ = 0;
|
||||
|
|
|
|||
15
netlist.h
15
netlist.h
|
|
@ -729,7 +729,7 @@ class NetScope : public Attrib {
|
|||
|
||||
/* Create a new scope, and attach it to the given parent. The
|
||||
name is expected to have been permallocated. */
|
||||
NetScope(NetScope*up, const hname_t&name, TYPE t);
|
||||
NetScope(NetScope*up, const hname_t&name, TYPE t, bool nest=false, bool prog=false);
|
||||
~NetScope();
|
||||
|
||||
/* Rename the scope using the name generated by inserting as
|
||||
|
|
@ -806,6 +806,10 @@ class NetScope : public Attrib {
|
|||
const NetScope* parent() const { return up_; }
|
||||
const NetScope* child(const hname_t&name) const;
|
||||
|
||||
// Nested modules have slightly different scope search rules.
|
||||
inline bool nested_module() const { return nested_module_; }
|
||||
// Program blocks have elaboration constraints.
|
||||
inline bool program_block() const { return program_block_; }
|
||||
TYPE type() const;
|
||||
void print_type(ostream&) const;
|
||||
|
||||
|
|
@ -994,6 +998,11 @@ class NetScope : public Attrib {
|
|||
TYPE type_;
|
||||
hname_t name_;
|
||||
|
||||
// True if the scope is a nested module/program block
|
||||
bool nested_module_;
|
||||
// True if the scope is a program block
|
||||
bool program_block_;
|
||||
|
||||
perm_string file_;
|
||||
perm_string def_file_;
|
||||
unsigned lineno_;
|
||||
|
|
@ -2509,7 +2518,7 @@ class NetAssignNB : public NetAssignBase {
|
|||
class NetBlock : public NetProc {
|
||||
|
||||
public:
|
||||
enum Type { SEQU, PARA };
|
||||
enum Type { SEQU, PARA, PARA_JOIN_ANY, PARA_JOIN_NONE };
|
||||
|
||||
NetBlock(Type t, NetScope*subscope);
|
||||
~NetBlock();
|
||||
|
|
@ -4073,7 +4082,7 @@ class Design {
|
|||
|
||||
const char* get_flag(const string&key) const;
|
||||
|
||||
NetScope* make_root_scope(perm_string name);
|
||||
NetScope* make_root_scope(perm_string name, bool program_block);
|
||||
NetScope* find_root_scope();
|
||||
list<NetScope*> find_root_scopes();
|
||||
|
||||
|
|
|
|||
270
parse.y
270
parse.y
|
|
@ -356,6 +356,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector<Statement*>
|
|||
PGBuiltin::Type gatetype;
|
||||
NetNet::PortType porttype;
|
||||
ivl_variable_type_t vartype;
|
||||
PBlock::BL_TYPE join_keyword;
|
||||
|
||||
PWire*wire;
|
||||
svector<PWire*>*wires;
|
||||
|
|
@ -574,6 +575,8 @@ static void current_function_set_statement(const YYLTYPE&loc, vector<Statement*>
|
|||
|
||||
%type <statement> analog_statement
|
||||
|
||||
%type <join_keyword> join_keyword
|
||||
|
||||
%type <letter> spec_polarity
|
||||
%type <perm_strings> specify_path_identifiers
|
||||
|
||||
|
|
@ -1082,6 +1085,15 @@ integer_vector_type /* IEEE1800-2005: A.2.2.1 */
|
|||
| K_bool { $$ = IVL_VT_BOOL; } /* Icarus Verilog xtypes extension */
|
||||
;
|
||||
|
||||
join_keyword /* IEEE1800-2005: A.6.3 */
|
||||
: K_join
|
||||
{ $$ = PBlock::BL_PAR; }
|
||||
| K_join_none
|
||||
{ $$ = PBlock::BL_JOIN_NONE; }
|
||||
| K_join_any
|
||||
{ $$ = PBlock::BL_JOIN_ANY; }
|
||||
;
|
||||
|
||||
jump_statement /* IEEE1800-2005: A.6.5 */
|
||||
: K_break ';'
|
||||
{ yyerror(@1, "sorry: break statements not supported.");
|
||||
|
|
@ -3754,7 +3766,7 @@ local_timeunit_prec_decl
|
|||
|
||||
module
|
||||
: attribute_list_opt module_start IDENTIFIER
|
||||
{ pform_startmodule($3, @2.text, @2.first_line, $1); }
|
||||
{ pform_startmodule(@2, $3, $2==K_program, $1); }
|
||||
module_parameter_port_list_opt
|
||||
module_port_list_opt
|
||||
module_attribute_foreign ';'
|
||||
|
|
@ -3796,9 +3808,6 @@ module
|
|||
break;
|
||||
}
|
||||
}
|
||||
if ($2 == K_program) {
|
||||
yyerror(@2, "sorry: Program blocks not supported yet.");
|
||||
}
|
||||
pform_endmodule($3, in_celldefine, ucd);
|
||||
delete[]$3;
|
||||
have_timeunit_decl = false; // We will allow decls again.
|
||||
|
|
@ -3869,43 +3878,46 @@ module_parameter_port_list
|
|||
|
||||
module_item
|
||||
|
||||
/* Modules can contain further sub-module definitions. */
|
||||
: module
|
||||
|
||||
/* This rule detects net declarations that possibly include a
|
||||
primitive type, an optional vector range and signed flag. This
|
||||
also includes an optional delay set. The values are then applied
|
||||
to a list of names. If the primitive type is not specified, then
|
||||
resort to the default type LOGIC. */
|
||||
|
||||
: attribute_list_opt net_type
|
||||
primitive_type_opt unsigned_signed_opt range_opt
|
||||
delay3_opt
|
||||
net_variable_list ';'
|
||||
| attribute_list_opt net_type
|
||||
primitive_type_opt unsigned_signed_opt range_opt
|
||||
delay3_opt
|
||||
net_variable_list ';'
|
||||
|
||||
{ ivl_variable_type_t dtype = $3;
|
||||
if (dtype == IVL_VT_NO_TYPE)
|
||||
dtype = IVL_VT_LOGIC;
|
||||
pform_makewire(@2, $5, $4, $7, $2,
|
||||
NetNet::NOT_A_PORT, dtype, $1);
|
||||
if ($6 != 0) {
|
||||
yyerror(@6, "sorry: net delays not supported.");
|
||||
delete $6;
|
||||
}
|
||||
delete $1;
|
||||
}
|
||||
{ ivl_variable_type_t dtype = $3;
|
||||
if (dtype == IVL_VT_NO_TYPE)
|
||||
dtype = IVL_VT_LOGIC;
|
||||
pform_makewire(@2, $5, $4, $7, $2, NetNet::NOT_A_PORT, dtype, $1);
|
||||
if ($6 != 0) {
|
||||
yyerror(@6, "sorry: net delays not supported.");
|
||||
delete $6;
|
||||
}
|
||||
delete $1;
|
||||
}
|
||||
|
||||
| attribute_list_opt K_wreal delay3 net_variable_list ';'
|
||||
{ pform_makewire(@2, 0, true, $4, NetNet::WIRE,
|
||||
NetNet::NOT_A_PORT, IVL_VT_REAL, $1);
|
||||
if ($3 != 0) {
|
||||
yyerror(@3, "sorry: net delays not supported.");
|
||||
delete $3;
|
||||
}
|
||||
delete $1;
|
||||
}
|
||||
| attribute_list_opt K_wreal net_variable_list ';'
|
||||
{ pform_makewire(@2, 0, true, $3, NetNet::WIRE,
|
||||
NetNet::NOT_A_PORT, IVL_VT_REAL, $1);
|
||||
delete $1;
|
||||
}
|
||||
| attribute_list_opt K_wreal delay3 net_variable_list ';'
|
||||
{ pform_makewire(@2, 0, true, $4, NetNet::WIRE,
|
||||
NetNet::NOT_A_PORT, IVL_VT_REAL, $1);
|
||||
if ($3 != 0) {
|
||||
yyerror(@3, "sorry: net delays not supported.");
|
||||
delete $3;
|
||||
}
|
||||
delete $1;
|
||||
}
|
||||
|
||||
| attribute_list_opt K_wreal net_variable_list ';'
|
||||
{ pform_makewire(@2, 0, true, $3, NetNet::WIRE,
|
||||
NetNet::NOT_A_PORT, IVL_VT_REAL, $1);
|
||||
delete $1;
|
||||
}
|
||||
|
||||
/* Very similar to the rule above, but this takes a list of
|
||||
net_decl_assigns, which are <name> = <expr> assignment
|
||||
|
|
@ -4056,66 +4068,50 @@ module_item
|
|||
two/three-value delay. These rules handle the different cases.
|
||||
We check that the actual number of delays is correct later. */
|
||||
|
||||
| attribute_list_opt gatetype gate_instance_list ';'
|
||||
{ pform_makegates($2, str_strength, 0, $3, $1);
|
||||
}
|
||||
| attribute_list_opt gatetype gate_instance_list ';'
|
||||
{ pform_makegates(@2, $2, str_strength, 0, $3, $1); }
|
||||
|
||||
| attribute_list_opt gatetype delay3 gate_instance_list ';'
|
||||
{ pform_makegates($2, str_strength, $3, $4, $1);
|
||||
}
|
||||
| attribute_list_opt gatetype delay3 gate_instance_list ';'
|
||||
{ pform_makegates(@2, $2, str_strength, $3, $4, $1); }
|
||||
|
||||
| attribute_list_opt gatetype drive_strength gate_instance_list ';'
|
||||
{ pform_makegates($2, $3, 0, $4, $1);
|
||||
}
|
||||
| attribute_list_opt gatetype drive_strength gate_instance_list ';'
|
||||
{ pform_makegates(@2, $2, $3, 0, $4, $1); }
|
||||
|
||||
| attribute_list_opt gatetype drive_strength delay3 gate_instance_list ';'
|
||||
{ pform_makegates($2, $3, $4, $5, $1);
|
||||
}
|
||||
| attribute_list_opt gatetype drive_strength delay3 gate_instance_list ';'
|
||||
{ pform_makegates(@2, $2, $3, $4, $5, $1); }
|
||||
|
||||
/* The switch type gates do not support a strength. */
|
||||
| attribute_list_opt switchtype gate_instance_list ';'
|
||||
{ pform_makegates($2, str_strength, 0, $3, $1);
|
||||
}
|
||||
| attribute_list_opt switchtype gate_instance_list ';'
|
||||
{ pform_makegates(@2, $2, str_strength, 0, $3, $1); }
|
||||
|
||||
| attribute_list_opt switchtype delay3 gate_instance_list ';'
|
||||
{ pform_makegates($2, str_strength, $3, $4, $1);
|
||||
}
|
||||
| attribute_list_opt switchtype delay3 gate_instance_list ';'
|
||||
{ pform_makegates(@2, $2, str_strength, $3, $4, $1); }
|
||||
|
||||
/* Pullup and pulldown devices cannot have delays, and their
|
||||
strengths are limited. */
|
||||
|
||||
| K_pullup gate_instance_list ';'
|
||||
{ pform_makegates(PGBuiltin::PULLUP, pull_strength, 0,
|
||||
$2, 0);
|
||||
}
|
||||
| K_pulldown gate_instance_list ';'
|
||||
{ pform_makegates(PGBuiltin::PULLDOWN, pull_strength,
|
||||
0, $2, 0);
|
||||
}
|
||||
| K_pullup gate_instance_list ';'
|
||||
{ pform_makegates(@1, PGBuiltin::PULLUP, pull_strength, 0, $2, 0); }
|
||||
| K_pulldown gate_instance_list ';'
|
||||
{ pform_makegates(@1, PGBuiltin::PULLDOWN, pull_strength, 0, $2, 0); }
|
||||
|
||||
| K_pullup '(' dr_strength1 ')' gate_instance_list ';'
|
||||
{ pform_makegates(PGBuiltin::PULLUP, $3, 0, $5, 0);
|
||||
}
|
||||
| K_pullup '(' dr_strength1 ')' gate_instance_list ';'
|
||||
{ pform_makegates(@1, PGBuiltin::PULLUP, $3, 0, $5, 0); }
|
||||
|
||||
| K_pullup '(' dr_strength1 ',' dr_strength0 ')' gate_instance_list ';'
|
||||
{ pform_makegates(PGBuiltin::PULLUP, $3, 0, $7, 0);
|
||||
}
|
||||
| K_pullup '(' dr_strength1 ',' dr_strength0 ')' gate_instance_list ';'
|
||||
{ pform_makegates(@1, PGBuiltin::PULLUP, $3, 0, $7, 0); }
|
||||
|
||||
| K_pullup '(' dr_strength0 ',' dr_strength1 ')' gate_instance_list ';'
|
||||
{ pform_makegates(PGBuiltin::PULLUP, $5, 0, $7, 0);
|
||||
}
|
||||
| K_pullup '(' dr_strength0 ',' dr_strength1 ')' gate_instance_list ';'
|
||||
{ pform_makegates(@1, PGBuiltin::PULLUP, $5, 0, $7, 0); }
|
||||
|
||||
| K_pulldown '(' dr_strength0 ')' gate_instance_list ';'
|
||||
{ pform_makegates(PGBuiltin::PULLDOWN, $3, 0, $5, 0);
|
||||
}
|
||||
| K_pulldown '(' dr_strength0 ')' gate_instance_list ';'
|
||||
{ pform_makegates(@1, PGBuiltin::PULLDOWN, $3, 0, $5, 0); }
|
||||
|
||||
| K_pulldown '(' dr_strength1 ',' dr_strength0 ')' gate_instance_list ';'
|
||||
{ pform_makegates(PGBuiltin::PULLDOWN, $5, 0, $7, 0);
|
||||
}
|
||||
| K_pulldown '(' dr_strength1 ',' dr_strength0 ')' gate_instance_list ';'
|
||||
{ pform_makegates(@1, PGBuiltin::PULLDOWN, $5, 0, $7, 0); }
|
||||
|
||||
| K_pulldown '(' dr_strength0 ',' dr_strength1 ')' gate_instance_list ';'
|
||||
{ pform_makegates(PGBuiltin::PULLDOWN, $3, 0, $7, 0);
|
||||
}
|
||||
| K_pulldown '(' dr_strength0 ',' dr_strength1 ')' gate_instance_list ';'
|
||||
{ pform_makegates(@1, PGBuiltin::PULLDOWN, $3, 0, $7, 0); }
|
||||
|
||||
/* This rule handles instantiations of modules and user defined
|
||||
primitives. These devices to not have delay lists or strengths,
|
||||
|
|
@ -4124,7 +4120,7 @@ module_item
|
|||
| attribute_list_opt
|
||||
IDENTIFIER parameter_value_opt gate_instance_list ';'
|
||||
{ perm_string tmp1 = lex_strings.make($2);
|
||||
pform_make_modgates(tmp1, $3, $4);
|
||||
pform_make_modgates(@2, tmp1, $3, $4);
|
||||
delete[]$2;
|
||||
if ($1) delete $1;
|
||||
}
|
||||
|
|
@ -4251,13 +4247,6 @@ module_item
|
|||
module items. These rules try to catch them at a point where a
|
||||
reasonable error message can be produced. */
|
||||
|
||||
| K_module error ';'
|
||||
{ yyerror(@1, "error: missing endmodule or attempt to "
|
||||
"nest modules.");
|
||||
pform_error_nested_modules();
|
||||
yyerrok;
|
||||
}
|
||||
|
||||
| error ';'
|
||||
{ yyerror(@2, "error: invalid module item.");
|
||||
yyerrok;
|
||||
|
|
@ -5349,13 +5338,13 @@ statement_item /* This is roughly statement_item in the LRM */
|
|||
need to do is remember that this is a parallel block so that the
|
||||
code generator can do the right thing. */
|
||||
|
||||
| K_fork K_join
|
||||
{ PBlock*tmp = new PBlock(PBlock::BL_PAR);
|
||||
| K_fork join_keyword
|
||||
{ PBlock*tmp = new PBlock($2);
|
||||
FILE_NAME(tmp, @1);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_fork statement_or_null_list K_join
|
||||
{ PBlock*tmp = new PBlock(PBlock::BL_PAR);
|
||||
| K_fork statement_or_null_list join_keyword
|
||||
{ PBlock*tmp = new PBlock($3);
|
||||
FILE_NAME(tmp, @1);
|
||||
tmp->set_statement(*$2);
|
||||
delete $2;
|
||||
|
|
@ -5367,11 +5356,12 @@ statement_item /* This is roughly statement_item in the LRM */
|
|||
current_block_stack.push(tmp);
|
||||
}
|
||||
block_item_decls_opt
|
||||
statement_or_null_list_opt K_join
|
||||
statement_or_null_list_opt join_keyword
|
||||
{ pform_pop_scope();
|
||||
assert(! current_block_stack.empty());
|
||||
PBlock*tmp = current_block_stack.top();
|
||||
current_block_stack.pop();
|
||||
tmp->set_join_type($7);
|
||||
if ($6) tmp->set_statement(*$6);
|
||||
delete[]$3;
|
||||
delete $6;
|
||||
|
|
@ -5487,56 +5477,56 @@ statement_item /* This is roughly statement_item in the LRM */
|
|||
$$ = tmp;
|
||||
}
|
||||
|
||||
| error '=' expression ';'
|
||||
{ yyerror(@2, "Syntax in assignment statement l-value.");
|
||||
yyerrok;
|
||||
$$ = new PNoop;
|
||||
}
|
||||
| lpvalue K_LE expression ';'
|
||||
{ PAssignNB*tmp = new PAssignNB($1,$3);
|
||||
FILE_NAME(tmp, @1);
|
||||
$$ = tmp;
|
||||
}
|
||||
| error K_LE expression ';'
|
||||
{ yyerror(@2, "Syntax in assignment statement l-value.");
|
||||
yyerrok;
|
||||
$$ = new PNoop;
|
||||
}
|
||||
| lpvalue '=' delay1 expression ';'
|
||||
{ PExpr*del = $3->front(); $3->pop_front();
|
||||
assert($3->empty());
|
||||
PAssign*tmp = new PAssign($1,del,$4);
|
||||
FILE_NAME(tmp, @1);
|
||||
$$ = tmp;
|
||||
}
|
||||
| lpvalue K_LE delay1 expression ';'
|
||||
{ PExpr*del = $3->front(); $3->pop_front();
|
||||
assert($3->empty());
|
||||
PAssignNB*tmp = new PAssignNB($1,del,$4);
|
||||
FILE_NAME(tmp, @1);
|
||||
$$ = tmp;
|
||||
}
|
||||
| lpvalue '=' event_control expression ';'
|
||||
{ PAssign*tmp = new PAssign($1,0,$3,$4);
|
||||
FILE_NAME(tmp, @1);
|
||||
$$ = tmp;
|
||||
}
|
||||
| lpvalue '=' K_repeat '(' expression ')' event_control expression ';'
|
||||
{ PAssign*tmp = new PAssign($1,$5,$7,$8);
|
||||
FILE_NAME(tmp,@1);
|
||||
tmp->set_lineno(@1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| lpvalue K_LE event_control expression ';'
|
||||
{ PAssignNB*tmp = new PAssignNB($1,0,$3,$4);
|
||||
FILE_NAME(tmp, @1);
|
||||
$$ = tmp;
|
||||
}
|
||||
| lpvalue K_LE K_repeat '(' expression ')' event_control expression ';'
|
||||
{ PAssignNB*tmp = new PAssignNB($1,$5,$7,$8);
|
||||
FILE_NAME(tmp, @1);
|
||||
$$ = tmp;
|
||||
}
|
||||
| error '=' expression ';'
|
||||
{ yyerror(@2, "Syntax in assignment statement l-value.");
|
||||
yyerrok;
|
||||
$$ = new PNoop;
|
||||
}
|
||||
| lpvalue K_LE expression ';'
|
||||
{ PAssignNB*tmp = new PAssignNB($1,$3);
|
||||
FILE_NAME(tmp, @1);
|
||||
$$ = tmp;
|
||||
}
|
||||
| error K_LE expression ';'
|
||||
{ yyerror(@2, "Syntax in assignment statement l-value.");
|
||||
yyerrok;
|
||||
$$ = new PNoop;
|
||||
}
|
||||
| lpvalue '=' delay1 expression ';'
|
||||
{ PExpr*del = $3->front(); $3->pop_front();
|
||||
assert($3->empty());
|
||||
PAssign*tmp = new PAssign($1,del,$4);
|
||||
FILE_NAME(tmp, @1);
|
||||
$$ = tmp;
|
||||
}
|
||||
| lpvalue K_LE delay1 expression ';'
|
||||
{ PExpr*del = $3->front(); $3->pop_front();
|
||||
assert($3->empty());
|
||||
PAssignNB*tmp = new PAssignNB($1,del,$4);
|
||||
FILE_NAME(tmp, @1);
|
||||
$$ = tmp;
|
||||
}
|
||||
| lpvalue '=' event_control expression ';'
|
||||
{ PAssign*tmp = new PAssign($1,0,$3,$4);
|
||||
FILE_NAME(tmp, @1);
|
||||
$$ = tmp;
|
||||
}
|
||||
| lpvalue '=' K_repeat '(' expression ')' event_control expression ';'
|
||||
{ PAssign*tmp = new PAssign($1,$5,$7,$8);
|
||||
FILE_NAME(tmp,@1);
|
||||
tmp->set_lineno(@1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| lpvalue K_LE event_control expression ';'
|
||||
{ PAssignNB*tmp = new PAssignNB($1,0,$3,$4);
|
||||
FILE_NAME(tmp, @1);
|
||||
$$ = tmp;
|
||||
}
|
||||
| lpvalue K_LE K_repeat '(' expression ')' event_control expression ';'
|
||||
{ PAssignNB*tmp = new PAssignNB($1,$5,$7,$8);
|
||||
FILE_NAME(tmp, @1);
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
/* The IEEE1800 standard defines dynamic_array_new assignment as a
|
||||
different rule from regular assignment. That implies that the
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ std::ostream& operator << (std::ostream&o, const YYLTYPE&loc)
|
|||
{
|
||||
if (loc.text)
|
||||
o << loc.text << ":";
|
||||
else
|
||||
o << "<>:";
|
||||
o << loc.first_line;
|
||||
return o;
|
||||
}
|
||||
|
|
|
|||
226
pform.cc
226
pform.cc
|
|
@ -41,7 +41,13 @@
|
|||
# include "ivl_assert.h"
|
||||
# include "ivl_alloc.h"
|
||||
|
||||
/*
|
||||
* The pform_modules is a map of the modules that have been defined in
|
||||
* the top level. This should not contain nested modules/programs.
|
||||
*/
|
||||
map<perm_string,Module*> pform_modules;
|
||||
/*
|
||||
*/
|
||||
map<perm_string,PUdp*> pform_primitives;
|
||||
|
||||
|
||||
|
|
@ -217,7 +223,7 @@ extern int VLparse();
|
|||
/* This tracks the current module being processed. There can only be
|
||||
exactly one module currently being parsed, since Verilog does not
|
||||
allow nested module definitions. */
|
||||
static Module*pform_cur_module = 0;
|
||||
static list<Module*>pform_cur_module;
|
||||
|
||||
bool pform_library_flag = false;
|
||||
|
||||
|
|
@ -307,7 +313,7 @@ PTask* pform_push_task_scope(const struct vlltype&loc, char*name, bool is_auto)
|
|||
pform_cur_generate->tasks.end()) {
|
||||
cerr << task->get_fileline() << ": error: duplicate "
|
||||
"definition for task '" << name << "' in '"
|
||||
<< pform_cur_module->mod_name() << "' (generate)."
|
||||
<< pform_cur_module.front()->mod_name() << "' (generate)."
|
||||
<< endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
|
@ -350,7 +356,7 @@ PFunction* pform_push_function_scope(const struct vlltype&loc, char*name,
|
|||
pform_cur_generate->funcs.end()) {
|
||||
cerr << func->get_fileline() << ": error: duplicate "
|
||||
"definition for function '" << name << "' in '"
|
||||
<< pform_cur_module->mod_name() << "' (generate)."
|
||||
<< pform_cur_module.front()->mod_name() << "' (generate)."
|
||||
<< endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
|
@ -400,9 +406,18 @@ void pform_bind_attributes(map<perm_string,PExpr*>&attributes,
|
|||
delete attr;
|
||||
}
|
||||
|
||||
bool pform_in_program_block()
|
||||
{
|
||||
if (pform_cur_module.size() == 0)
|
||||
return false;
|
||||
if (pform_cur_module.front()->program_block)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool pform_at_module_level()
|
||||
{
|
||||
return (lexical_scope == pform_cur_module)
|
||||
return (lexical_scope == pform_cur_module.front())
|
||||
|| (lexical_scope == pform_cur_generate);
|
||||
}
|
||||
|
||||
|
|
@ -479,15 +494,15 @@ void pform_set_default_nettype(NetNet::Type type,
|
|||
{
|
||||
pform_default_nettype = type;
|
||||
|
||||
if (pform_cur_module) {
|
||||
if (pform_cur_module.size() > 0) {
|
||||
cerr << file<<":"<<lineno << ": error: "
|
||||
<< "`default_nettype directives must appear" << endl;
|
||||
cerr << file<<":"<<lineno << ": : "
|
||||
<< "outside module definitions. The containing" << endl;
|
||||
cerr << file<<":"<<lineno << ": : "
|
||||
<< "module " << pform_cur_module->mod_name()
|
||||
<< "module " << pform_cur_module.back()->mod_name()
|
||||
<< " starts on line "
|
||||
<< pform_cur_module->get_fileline() << "." << endl;
|
||||
<< pform_cur_module.back()->get_fileline() << "." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -690,14 +705,14 @@ void pform_set_timeunit(const char*txt, bool in_module, bool only_check)
|
|||
|
||||
if (in_module) {
|
||||
if (!only_check) {
|
||||
pform_cur_module->time_unit = val;
|
||||
pform_cur_module.front()->time_unit = val;
|
||||
tu_decl_flag = true;
|
||||
tu_local_flag = true;
|
||||
} else if (!tu_decl_flag) {
|
||||
VLerror(yylloc, "error: repeat timeunit found and the "
|
||||
"initial module timeunit is missing.");
|
||||
return;
|
||||
} else if (pform_cur_module->time_unit != val) {
|
||||
} else if (pform_cur_module.front()->time_unit != val) {
|
||||
VLerror(yylloc, "error: repeat timeunit does not match "
|
||||
"the initial module timeunit "
|
||||
"declaration.");
|
||||
|
|
@ -714,7 +729,7 @@ void pform_set_timeunit(const char*txt, bool in_module, bool only_check)
|
|||
|
||||
int pform_get_timeunit()
|
||||
{
|
||||
return pform_cur_module->time_unit;
|
||||
return pform_cur_module.front()->time_unit;
|
||||
}
|
||||
|
||||
void pform_set_timeprecision(const char*txt, bool in_module, bool only_check)
|
||||
|
|
@ -725,14 +740,14 @@ void pform_set_timeprecision(const char*txt, bool in_module, bool only_check)
|
|||
|
||||
if (in_module) {
|
||||
if (!only_check) {
|
||||
pform_cur_module->time_precision = val;
|
||||
pform_cur_module.front()->time_precision = val;
|
||||
tp_decl_flag = true;
|
||||
tp_local_flag = true;
|
||||
} else if (!tp_decl_flag) {
|
||||
VLerror(yylloc, "error: repeat timeprecision found and the "
|
||||
"initial module timeprecision is missing.");
|
||||
return;
|
||||
} else if (pform_cur_module->time_precision != val) {
|
||||
} else if (pform_cur_module.front()->time_precision != val) {
|
||||
VLerror(yylloc, "error: repeat timeprecision does not match "
|
||||
"the initial module timeprecision "
|
||||
"declaration.");
|
||||
|
|
@ -796,43 +811,54 @@ verinum* pform_verinum_with_size(verinum*siz, verinum*val,
|
|||
return res;
|
||||
}
|
||||
|
||||
void pform_startmodule(const char*name, const char*file, unsigned lineno,
|
||||
list<named_pexpr_t>*attr)
|
||||
void pform_startmodule(const struct vlltype&loc, const char*name,
|
||||
bool program_block, list<named_pexpr_t>*attr)
|
||||
{
|
||||
assert( pform_cur_module == 0 );
|
||||
if (pform_cur_module.size() > 0 && !gn_system_verilog()) {
|
||||
cerr << loc << ": error: Module definition " << name
|
||||
<< " cannot nest into module " << pform_cur_module.front()->mod_name() << "." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
||||
if (gn_system_verilog() && pform_cur_module.size() > 0 && pform_cur_module.front()->program_block) {
|
||||
cerr << loc << ": error: Program blocks cannot contain nested modules/program blocks." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
||||
perm_string lex_name = lex_strings.make(name);
|
||||
pform_cur_module = new Module(lex_name);
|
||||
Module*cur_module = new Module(lexical_scope, lex_name);
|
||||
cur_module->program_block = program_block;
|
||||
/* Set the local time unit/precision to the global value. */
|
||||
pform_cur_module->time_unit = pform_time_unit;
|
||||
pform_cur_module->time_precision = pform_time_prec;
|
||||
cur_module->time_unit = pform_time_unit;
|
||||
cur_module->time_precision = pform_time_prec;
|
||||
tu_local_flag = tu_global_flag;
|
||||
tp_local_flag = tp_global_flag;
|
||||
|
||||
/* If we have a timescale file then the time information is from
|
||||
* a timescale directive. */
|
||||
pform_cur_module->time_from_timescale = pform_timescale_file != 0;
|
||||
cur_module->time_from_timescale = pform_timescale_file != 0;
|
||||
|
||||
FILE_NAME(pform_cur_module, file, lineno);
|
||||
pform_cur_module->library_flag = pform_library_flag;
|
||||
FILE_NAME(cur_module, loc);
|
||||
cur_module->library_flag = pform_library_flag;
|
||||
|
||||
ivl_assert(*pform_cur_module, lexical_scope == 0);
|
||||
lexical_scope = pform_cur_module;
|
||||
pform_cur_module.push_front(cur_module);
|
||||
|
||||
lexical_scope = cur_module;
|
||||
|
||||
/* The generate scheme numbering starts with *1*, not
|
||||
zero. That's just the way it is, thanks to the standard. */
|
||||
scope_generate_counter = 1;
|
||||
|
||||
if (warn_timescale && pform_timescale_file
|
||||
&& (strcmp(pform_timescale_file,file) != 0)) {
|
||||
&& (strcmp(pform_timescale_file,loc.text) != 0)) {
|
||||
|
||||
cerr << pform_cur_module->get_fileline() << ": warning: "
|
||||
cerr << cur_module->get_fileline() << ": warning: "
|
||||
<< "timescale for " << name
|
||||
<< " inherited from another file." << endl;
|
||||
cerr << pform_timescale_file << ":" << pform_timescale_line
|
||||
<< ": ...: The inherited timescale is here." << endl;
|
||||
}
|
||||
pform_bind_attributes(pform_cur_module->attributes, attr);
|
||||
pform_bind_attributes(cur_module->attributes, attr);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -842,13 +868,12 @@ void pform_startmodule(const char*name, const char*file, unsigned lineno,
|
|||
*/
|
||||
void pform_check_timeunit_prec()
|
||||
{
|
||||
assert(pform_cur_module);
|
||||
assert(pform_cur_module.size() > 0);
|
||||
if ((generation_flag & (GN_VER2005_SV | GN_VER2009)) &&
|
||||
(pform_cur_module->time_unit < pform_cur_module->time_precision)) {
|
||||
VLerror("error: a timeprecision is missing or is too "
|
||||
"large!");
|
||||
} else assert(pform_cur_module->time_unit >=
|
||||
pform_cur_module->time_precision);
|
||||
(pform_cur_module.front()->time_unit < pform_cur_module.front()->time_precision)) {
|
||||
VLerror("error: a timeprecision is missing or is too large!");
|
||||
} else assert(pform_cur_module.front()->time_unit >=
|
||||
pform_cur_module.front()->time_precision);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -871,7 +896,7 @@ Module::port_t* pform_module_port_reference(perm_string name,
|
|||
|
||||
void pform_module_set_ports(vector<Module::port_t*>*ports)
|
||||
{
|
||||
assert(pform_cur_module);
|
||||
assert(pform_cur_module.size() > 0);
|
||||
|
||||
/* The parser parses ``module foo()'' as having one
|
||||
unconnected port, but it is really a module with no
|
||||
|
|
@ -882,7 +907,7 @@ void pform_module_set_ports(vector<Module::port_t*>*ports)
|
|||
}
|
||||
|
||||
if (ports != 0) {
|
||||
pform_cur_module->ports = *ports;
|
||||
pform_cur_module.front()->ports = *ports;
|
||||
delete ports;
|
||||
}
|
||||
}
|
||||
|
|
@ -890,34 +915,44 @@ void pform_module_set_ports(vector<Module::port_t*>*ports)
|
|||
void pform_endmodule(const char*name, bool inside_celldefine,
|
||||
Module::UCDriveType uc_drive_def)
|
||||
{
|
||||
assert(pform_cur_module);
|
||||
pform_cur_module->time_from_timescale = (tu_local_flag &&
|
||||
tp_local_flag) ||
|
||||
(pform_timescale_file != 0);
|
||||
perm_string mod_name = pform_cur_module->mod_name();
|
||||
assert(pform_cur_module.size() > 0);
|
||||
Module*cur_module = pform_cur_module.front();
|
||||
pform_cur_module.pop_front();
|
||||
|
||||
cur_module->time_from_timescale = (tu_local_flag && tp_local_flag)
|
||||
|| (pform_timescale_file != 0);
|
||||
perm_string mod_name = cur_module->mod_name();
|
||||
assert(strcmp(name, mod_name) == 0);
|
||||
pform_cur_module->is_cell = inside_celldefine;
|
||||
pform_cur_module->uc_drive = uc_drive_def;
|
||||
cur_module->is_cell = inside_celldefine;
|
||||
cur_module->uc_drive = uc_drive_def;
|
||||
|
||||
// If this is a root module, then there is no parent module
|
||||
// and we try to put this newly defined module into the global
|
||||
// root list of modules. Otherwise, this is a nested module
|
||||
// and we put it into the parent module scope to be elaborated
|
||||
// if needed.
|
||||
map<perm_string,Module*>&use_module_map = (pform_cur_module.size() == 0)
|
||||
? pform_modules
|
||||
: pform_cur_module.front()->nested_modules;
|
||||
|
||||
map<perm_string,Module*>::const_iterator test =
|
||||
pform_modules.find(mod_name);
|
||||
use_module_map.find(mod_name);
|
||||
|
||||
if (test != pform_modules.end()) {
|
||||
if (test != use_module_map.end()) {
|
||||
ostringstream msg;
|
||||
msg << "Module " << name << " was already declared here: "
|
||||
<< (*test).second->get_fileline() << endl;
|
||||
<< test->second->get_fileline() << endl;
|
||||
VLerror(msg.str().c_str());
|
||||
} else {
|
||||
pform_modules[mod_name] = pform_cur_module;
|
||||
use_module_map[mod_name] = cur_module;
|
||||
}
|
||||
|
||||
// The current lexical scope should be this module by now, and
|
||||
// this module should not have a parent lexical scope.
|
||||
ivl_assert(*pform_cur_module, lexical_scope == pform_cur_module);
|
||||
ivl_assert(*cur_module, lexical_scope == cur_module);
|
||||
pform_pop_scope();
|
||||
ivl_assert(*pform_cur_module, lexical_scope == 0);
|
||||
ivl_assert(*cur_module, pform_cur_module.size()>0 || lexical_scope == 0);
|
||||
|
||||
pform_cur_module = 0;
|
||||
tp_decl_flag = false;
|
||||
tu_decl_flag = false;
|
||||
tu_local_flag = false;
|
||||
|
|
@ -948,7 +983,7 @@ void pform_genvars(const struct vlltype&li, list<perm_string>*names)
|
|||
if (pform_cur_generate)
|
||||
pform_add_genvar(li, *cur, pform_cur_generate->genvars);
|
||||
else
|
||||
pform_add_genvar(li, *cur, pform_cur_module->genvars);
|
||||
pform_add_genvar(li, *cur, pform_cur_module.front()->genvars);
|
||||
}
|
||||
|
||||
delete names;
|
||||
|
|
@ -1103,7 +1138,7 @@ void pform_generate_block_name(char*name)
|
|||
void pform_endgenerate()
|
||||
{
|
||||
assert(pform_cur_generate != 0);
|
||||
assert(pform_cur_module);
|
||||
assert(pform_cur_module.size() > 0);
|
||||
|
||||
// If there is no explicit block name then generate a temporary
|
||||
// name. This will be replaced by the correct name later, once
|
||||
|
|
@ -1127,7 +1162,7 @@ void pform_endgenerate()
|
|||
parent_generate->generate_schemes.push_back(pform_cur_generate);
|
||||
} else {
|
||||
assert(pform_cur_generate->scheme_type != PGenerate::GS_CASE_ITEM);
|
||||
pform_cur_module->generate_schemes.push_back(pform_cur_generate);
|
||||
pform_cur_module.front()->generate_schemes.push_back(pform_cur_generate);
|
||||
}
|
||||
pform_cur_generate = parent_generate;
|
||||
}
|
||||
|
|
@ -1583,7 +1618,7 @@ static void pform_make_event(perm_string name, const char*fn, unsigned ln)
|
|||
FILE_NAME(&tloc, fn, ln);
|
||||
cerr << tloc.get_fileline() << ": error: duplicate definition "
|
||||
"for named event '" << name << "' in '"
|
||||
<< pform_cur_module->mod_name() << "'." << endl;
|
||||
<< pform_cur_module.front()->mod_name() << "'." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
||||
|
|
@ -1643,15 +1678,23 @@ static void pform_makegate(PGBuiltin::Type type,
|
|||
if (pform_cur_generate)
|
||||
pform_cur_generate->add_gate(cur);
|
||||
else
|
||||
pform_cur_module->add_gate(cur);
|
||||
pform_cur_module.front()->add_gate(cur);
|
||||
}
|
||||
|
||||
void pform_makegates(PGBuiltin::Type type,
|
||||
void pform_makegates(const struct vlltype&loc,
|
||||
PGBuiltin::Type type,
|
||||
struct str_pair_t str,
|
||||
list<PExpr*>*delay,
|
||||
svector<lgate>*gates,
|
||||
list<named_pexpr_t>*attr)
|
||||
{
|
||||
assert(pform_cur_module.size() > 0);
|
||||
if (pform_cur_module.front()->program_block) {
|
||||
cerr << loc << ": error: Gates and switches may not be instantiated"
|
||||
<< " in program blocks." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
||||
for (unsigned idx = 0 ; idx < gates->count() ; idx += 1) {
|
||||
pform_makegate(type, str, delay, (*gates)[idx], attr);
|
||||
}
|
||||
|
|
@ -1709,7 +1752,7 @@ static void pform_make_modgate(perm_string type,
|
|||
if (pform_cur_generate)
|
||||
pform_cur_generate->add_gate(cur);
|
||||
else
|
||||
pform_cur_module->add_gate(cur);
|
||||
pform_cur_module.front()->add_gate(cur);
|
||||
}
|
||||
|
||||
static void pform_make_modgate(perm_string type,
|
||||
|
|
@ -1753,13 +1796,20 @@ static void pform_make_modgate(perm_string type,
|
|||
if (pform_cur_generate)
|
||||
pform_cur_generate->add_gate(cur);
|
||||
else
|
||||
pform_cur_module->add_gate(cur);
|
||||
pform_cur_module.front()->add_gate(cur);
|
||||
}
|
||||
|
||||
void pform_make_modgates(perm_string type,
|
||||
void pform_make_modgates(const struct vlltype&loc,
|
||||
perm_string type,
|
||||
struct parmvalue_t*overrides,
|
||||
svector<lgate>*gates)
|
||||
{
|
||||
assert(pform_cur_module.size() > 0);
|
||||
if (pform_cur_module.front()->program_block) {
|
||||
cerr << loc << ": error: Module instantiations are not allowed"
|
||||
<< " in program blocks." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
||||
for (unsigned idx = 0 ; idx < gates->count() ; idx += 1) {
|
||||
lgate cur = (*gates)[idx];
|
||||
|
|
@ -1823,7 +1873,7 @@ static PGAssign* pform_make_pgassign(PExpr*lval, PExpr*rval,
|
|||
if (pform_cur_generate)
|
||||
pform_cur_generate->add_gate(cur);
|
||||
else
|
||||
pform_cur_module->add_gate(cur);
|
||||
pform_cur_module.front()->add_gate(cur);
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
|
@ -2295,7 +2345,7 @@ void pform_set_attrib(perm_string name, perm_string key, char*value)
|
|||
if (PWire*cur = lexical_scope->wires_find(name)) {
|
||||
cur->attributes[key] = new PEString(value);
|
||||
|
||||
} else if (PGate*curg = pform_cur_module->get_gate(name)) {
|
||||
} else if (PGate*curg = pform_cur_module.front()->get_gate(name)) {
|
||||
curg->attributes[key] = new PEString(value);
|
||||
|
||||
} else {
|
||||
|
|
@ -2374,23 +2424,23 @@ void pform_set_parameter(const struct vlltype&loc,
|
|||
FILE_NAME(&tloc, loc);
|
||||
cerr << tloc.get_fileline() << ": error: duplicate definition "
|
||||
"for parameter '" << name << "' in '"
|
||||
<< pform_cur_module->mod_name() << "'." << endl;
|
||||
<< pform_cur_module.front()->mod_name() << "'." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
if (scope->localparams.find(name) != scope->localparams.end()) {
|
||||
LineInfo tloc;
|
||||
FILE_NAME(&tloc, loc);
|
||||
cerr << tloc.get_fileline() << ": error: localparam and "
|
||||
"parameter in '" << pform_cur_module->mod_name()
|
||||
<< "parameter in '" << pform_cur_module.front()->mod_name()
|
||||
<< "' have the same name '" << name << "'." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
if ((scope == pform_cur_module) &&
|
||||
(pform_cur_module->specparams.find(name) != pform_cur_module->specparams.end())) {
|
||||
if ((scope == pform_cur_module.front()) &&
|
||||
(pform_cur_module.front()->specparams.find(name) != pform_cur_module.front()->specparams.end())) {
|
||||
LineInfo tloc;
|
||||
FILE_NAME(&tloc, loc);
|
||||
cerr << tloc.get_fileline() << ": error: specparam and "
|
||||
"parameter in '" << pform_cur_module->mod_name()
|
||||
"parameter in '" << pform_cur_module.front()->mod_name()
|
||||
<< "' have the same name '" << name << "'." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
|
@ -2416,8 +2466,8 @@ void pform_set_parameter(const struct vlltype&loc,
|
|||
parm.signed_flag = signed_flag;
|
||||
parm.range = value_range;
|
||||
|
||||
if (scope == pform_cur_module)
|
||||
pform_cur_module->param_names.push_back(name);
|
||||
if (scope == pform_cur_module.front())
|
||||
pform_cur_module.front()->param_names.push_back(name);
|
||||
}
|
||||
|
||||
void pform_set_localparam(const struct vlltype&loc,
|
||||
|
|
@ -2432,23 +2482,23 @@ void pform_set_localparam(const struct vlltype&loc,
|
|||
FILE_NAME(&tloc, loc);
|
||||
cerr << tloc.get_fileline() << ": error: duplicate definition "
|
||||
"for localparam '" << name << "' in '"
|
||||
<< pform_cur_module->mod_name() << "'." << endl;
|
||||
<< pform_cur_module.front()->mod_name() << "'." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
if (scope->parameters.find(name) != scope->parameters.end()) {
|
||||
LineInfo tloc;
|
||||
FILE_NAME(&tloc, loc);
|
||||
cerr << tloc.get_fileline() << ": error: parameter and "
|
||||
"localparam in '" << pform_cur_module->mod_name()
|
||||
<< "localparam in '" << pform_cur_module.front()->mod_name()
|
||||
<< "' have the same name '" << name << "'." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
if ((scope == pform_cur_module) &&
|
||||
(pform_cur_module->specparams.find(name) != pform_cur_module->specparams.end())) {
|
||||
if ((scope == pform_cur_module.front()) &&
|
||||
(pform_cur_module.front()->specparams.find(name) != pform_cur_module.front()->specparams.end())) {
|
||||
LineInfo tloc;
|
||||
FILE_NAME(&tloc, loc);
|
||||
cerr << tloc.get_fileline() << ": error: specparam and "
|
||||
"localparam in '" << pform_cur_module->mod_name()
|
||||
"localparam in '" << pform_cur_module.front()->mod_name()
|
||||
<< "' have the same name '" << name << "'." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
|
@ -2478,23 +2528,25 @@ void pform_set_localparam(const struct vlltype&loc,
|
|||
void pform_set_specparam(const struct vlltype&loc, perm_string name,
|
||||
list<pform_range_t>*range, PExpr*expr)
|
||||
{
|
||||
Module*scope = pform_cur_module;
|
||||
assert(pform_cur_module.size() > 0);
|
||||
Module*scope = pform_cur_module.front();
|
||||
assert(scope == lexical_scope);
|
||||
|
||||
// Check if the specparam name is already in the dictionary.
|
||||
if (scope->specparams.find(name) != scope->specparams.end()) {
|
||||
if (pform_cur_module.front()->specparams.find(name) !=
|
||||
pform_cur_module.front()->specparams.end()) {
|
||||
LineInfo tloc;
|
||||
FILE_NAME(&tloc, loc);
|
||||
cerr << tloc.get_fileline() << ": error: duplicate definition "
|
||||
"for specparam '" << name << "' in '"
|
||||
<< pform_cur_module->mod_name() << "'." << endl;
|
||||
<< pform_cur_module.front()->mod_name() << "'." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
if (scope->parameters.find(name) != scope->parameters.end()) {
|
||||
LineInfo tloc;
|
||||
FILE_NAME(&tloc, loc);
|
||||
cerr << tloc.get_fileline() << ": error: parameter and "
|
||||
"specparam in '" << pform_cur_module->mod_name()
|
||||
"specparam in '" << pform_cur_module.front()->mod_name()
|
||||
<< "' have the same name '" << name << "'." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
|
@ -2502,13 +2554,14 @@ void pform_set_specparam(const struct vlltype&loc, perm_string name,
|
|||
LineInfo tloc;
|
||||
FILE_NAME(&tloc, loc);
|
||||
cerr << tloc.get_fileline() << ": error: localparam and "
|
||||
"specparam in '" << pform_cur_module->mod_name()
|
||||
"specparam in '" << pform_cur_module.front()->mod_name()
|
||||
<< "' have the same name '" << name << "'." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
||||
assert(expr);
|
||||
Module::param_expr_t&parm = scope->specparams[name];
|
||||
|
||||
Module::param_expr_t&parm = pform_cur_module.front()->specparams[name];
|
||||
FILE_NAME(&parm, loc);
|
||||
|
||||
parm.expr = expr;
|
||||
|
|
@ -2532,7 +2585,7 @@ void pform_set_specparam(const struct vlltype&loc, perm_string name,
|
|||
void pform_set_defparam(const pform_name_t&name, PExpr*expr)
|
||||
{
|
||||
assert(expr);
|
||||
pform_cur_module->defparms.push_back(make_pair(name,expr));
|
||||
pform_cur_module.front()->defparms.push_back(make_pair(name,expr));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2599,7 +2652,7 @@ extern void pform_module_specify_path(PSpecPath*obj)
|
|||
{
|
||||
if (obj == 0)
|
||||
return;
|
||||
pform_cur_module->specify_paths.push_back(obj);
|
||||
pform_cur_module.front()->specify_paths.push_back(obj);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2849,6 +2902,14 @@ PProcess* pform_make_behavior(ivl_process_type_t type, Statement*st,
|
|||
pform_bind_attributes(pp->attributes, attr);
|
||||
|
||||
pform_put_behavior_in_scope(pp);
|
||||
|
||||
ivl_assert(*st, pform_cur_module.size() > 0);
|
||||
if (pform_cur_module.front()->program_block && type == IVL_PR_ALWAYS) {
|
||||
cerr << st->get_fileline() << ": error: Always statements not allowed"
|
||||
<< " in program blocks." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
||||
return pp;
|
||||
}
|
||||
|
||||
|
|
@ -2890,10 +2951,3 @@ int pform_parse(const char*path, FILE*file)
|
|||
destroy_lexor();
|
||||
return error_count;
|
||||
}
|
||||
|
||||
void pform_error_nested_modules()
|
||||
{
|
||||
assert( pform_cur_module != 0 );
|
||||
cerr << pform_cur_module->get_fileline() << ": error: original module "
|
||||
"(" << pform_cur_module->mod_name() << ") defined here." << endl;
|
||||
}
|
||||
|
|
|
|||
24
pform.h
24
pform.h
|
|
@ -130,6 +130,10 @@ extern void pform_set_default_nettype(NetNet::Type net,
|
|||
const char*file,
|
||||
unsigned lineno);
|
||||
|
||||
/* Return true if currently processing a program block. This can be
|
||||
used to reject statements that cannot exist in program blocks. */
|
||||
extern bool pform_in_program_block(void);
|
||||
|
||||
/*
|
||||
* Look for the given wire in the current lexical scope. If the wire
|
||||
* (including variables of any type) cannot be found in the current
|
||||
|
|
@ -145,9 +149,13 @@ extern PWire* pform_get_make_wire_in_scope(perm_string name, NetNet::Type net_ty
|
|||
* module has been noticed in the source file and the following events
|
||||
* are to apply to the scope of that module. The endmodule causes the
|
||||
* pform to close up and finish the named module.
|
||||
*
|
||||
* The program_flag indicates that the module is actually a program
|
||||
* block. This has implications during parse and during
|
||||
* elaboration/code generation.
|
||||
*/
|
||||
extern void pform_startmodule(const char*, const char*file, unsigned lineno,
|
||||
list<named_pexpr_t>*attr);
|
||||
extern void pform_startmodule(const struct vlltype&loc, const char*name,
|
||||
bool program_block, list<named_pexpr_t>*attr);
|
||||
extern void pform_check_timeunit_prec();
|
||||
extern void pform_module_set_ports(vector<Module::port_t*>*);
|
||||
|
||||
|
|
@ -369,13 +377,15 @@ extern void pform_make_reals(list<perm_string>*names,
|
|||
* The makegate function creates a new gate (which need not have a
|
||||
* name) and connects it to the specified wires.
|
||||
*/
|
||||
extern void pform_makegates(PGBuiltin::Type type,
|
||||
extern void pform_makegates(const struct vlltype&loc,
|
||||
PGBuiltin::Type type,
|
||||
struct str_pair_t str,
|
||||
list<PExpr*>*delay,
|
||||
svector<lgate>*gates,
|
||||
list<named_pexpr_t>*attr);
|
||||
|
||||
extern void pform_make_modgates(perm_string type,
|
||||
extern void pform_make_modgates(const struct vlltype&loc,
|
||||
perm_string type,
|
||||
struct parmvalue_t*overrides,
|
||||
svector<lgate>*gates);
|
||||
|
||||
|
|
@ -416,12 +426,6 @@ extern PAssign* pform_compressed_assign_from_inc_dec(const struct vlltype&loc,
|
|||
*/
|
||||
extern void pform_dump(ostream&out, Module*mod);
|
||||
|
||||
/*
|
||||
* Used to report the original module location when a nested module
|
||||
* (missing endmodule) is found by the parser.
|
||||
*/
|
||||
extern void pform_error_nested_modules();
|
||||
|
||||
/* ** pform_discipline.cc
|
||||
* Functions for handling the parse of natures and disciplines. These
|
||||
* functions are in pform_disciplines.cc
|
||||
|
|
|
|||
|
|
@ -1249,6 +1249,11 @@ void Module::dump(ostream&out) const
|
|||
out << ")" << endl;
|
||||
}
|
||||
|
||||
for (map<perm_string,Module*>::const_iterator cur = nested_modules.begin()
|
||||
; cur != nested_modules.end() ; ++cur) {
|
||||
out << setw(4) << "" << "Nested module " << cur->first << ";" << endl;
|
||||
}
|
||||
|
||||
dump_typedefs_(out, 4);
|
||||
|
||||
dump_parameters_(out, 4);
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ NetScope*symbol_search(const LineInfo*li, Design*des, NetScope*scope,
|
|||
|
||||
/* We can't look up if we are at the enclosing module scope
|
||||
* or if a hierarchical path was given. */
|
||||
if ((scope->type() == NetScope::MODULE) || hier_path)
|
||||
if ((scope->type()==NetScope::MODULE && !scope->nested_module()) || hier_path)
|
||||
scope = 0;
|
||||
else
|
||||
scope = scope->parent();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2000-2012 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
|
||||
|
|
@ -2271,6 +2271,8 @@ extern "C" ivl_scope_t ivl_stmt_block_scope(ivl_statement_t net)
|
|||
switch (net->type_) {
|
||||
case IVL_ST_BLOCK:
|
||||
case IVL_ST_FORK:
|
||||
case IVL_ST_FORK_JOIN_ANY:
|
||||
case IVL_ST_FORK_JOIN_NONE:
|
||||
return net->u_.block_.scope;
|
||||
default:
|
||||
assert(0);
|
||||
|
|
@ -2283,6 +2285,8 @@ extern "C" unsigned ivl_stmt_block_count(ivl_statement_t net)
|
|||
switch (net->type_) {
|
||||
case IVL_ST_BLOCK:
|
||||
case IVL_ST_FORK:
|
||||
case IVL_ST_FORK_JOIN_ANY:
|
||||
case IVL_ST_FORK_JOIN_NONE:
|
||||
return net->u_.block_.nstmt_;
|
||||
default:
|
||||
assert(0);
|
||||
|
|
@ -2296,6 +2300,8 @@ extern "C" ivl_statement_t ivl_stmt_block_stmt(ivl_statement_t net,
|
|||
switch (net->type_) {
|
||||
case IVL_ST_BLOCK:
|
||||
case IVL_ST_FORK:
|
||||
case IVL_ST_FORK_JOIN_ANY:
|
||||
case IVL_ST_FORK_JOIN_NONE:
|
||||
return net->u_.block_.stmt_ + i;
|
||||
default:
|
||||
assert(0);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2000-2012 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
|
||||
|
|
@ -386,9 +386,20 @@ bool dll_target::proc_block(const NetBlock*net)
|
|||
it, so fill in the block fields of the existing statement,
|
||||
and generate the contents for the statement array. */
|
||||
|
||||
stmt_cur_->type_ = (net->type() == NetBlock::SEQU)
|
||||
? IVL_ST_BLOCK
|
||||
: IVL_ST_FORK;
|
||||
switch (net->type()) {
|
||||
case NetBlock::SEQU:
|
||||
stmt_cur_->type_ = IVL_ST_BLOCK;
|
||||
break;
|
||||
case NetBlock::PARA:
|
||||
stmt_cur_->type_ = IVL_ST_FORK;
|
||||
break;
|
||||
case NetBlock::PARA_JOIN_ANY:
|
||||
stmt_cur_->type_ = IVL_ST_FORK_JOIN_ANY;
|
||||
break;
|
||||
case NetBlock::PARA_JOIN_NONE:
|
||||
stmt_cur_->type_ = IVL_ST_FORK_JOIN_NONE;
|
||||
break;
|
||||
}
|
||||
stmt_cur_->u_.block_.nstmt_ = count;
|
||||
stmt_cur_->u_.block_.stmt_ = (struct ivl_statement_s*)
|
||||
calloc(count, sizeof(struct ivl_statement_s));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2004-2007 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2004-2007,2012 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
|
||||
|
|
@ -329,6 +329,28 @@ void show_statement(ivl_statement_t net, unsigned ind)
|
|||
break;
|
||||
}
|
||||
|
||||
case IVL_ST_FORK_JOIN_ANY: {
|
||||
unsigned cnt = ivl_stmt_block_count(net);
|
||||
fprintf(out, "%*sfork\n", ind, "");
|
||||
for (idx = 0 ; idx < cnt ; idx += 1) {
|
||||
ivl_statement_t cur = ivl_stmt_block_stmt(net, idx);
|
||||
show_statement(cur, ind+4);
|
||||
}
|
||||
fprintf(out, "%*sjoin_any\n", ind, "");
|
||||
break;
|
||||
}
|
||||
|
||||
case IVL_ST_FORK_JOIN_NONE: {
|
||||
unsigned cnt = ivl_stmt_block_count(net);
|
||||
fprintf(out, "%*sfork\n", ind, "");
|
||||
for (idx = 0 ; idx < cnt ; idx += 1) {
|
||||
ivl_statement_t cur = ivl_stmt_block_stmt(net, idx);
|
||||
show_statement(cur, ind+4);
|
||||
}
|
||||
fprintf(out, "%*sjoin_none\n", ind, "");
|
||||
break;
|
||||
}
|
||||
|
||||
case IVL_ST_FREE:
|
||||
fprintf(out, "%*sfree automatic storage ...\n", ind, "");
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1314,55 +1314,85 @@ static int show_stmt_fork(ivl_statement_t net, ivl_scope_t sscope)
|
|||
{
|
||||
unsigned idx;
|
||||
int rc = 0;
|
||||
unsigned cnt = ivl_stmt_block_count(net);
|
||||
unsigned join_count = ivl_stmt_block_count(net);
|
||||
unsigned join_detach_count = 0;
|
||||
ivl_scope_t scope = ivl_stmt_block_scope(net);
|
||||
unsigned is_named = (scope != 0);
|
||||
int is_named = (scope != 0);
|
||||
/* This is TRUE if it is allowed to embed one of the threads
|
||||
into this thread. */
|
||||
int is_embeddable = 1;
|
||||
|
||||
unsigned out = transient_id++;
|
||||
unsigned id_base = transient_id;
|
||||
|
||||
/* Children are certainly not embeddable if they are going
|
||||
into a new scope. */
|
||||
if (is_named)
|
||||
is_embeddable = 0;
|
||||
|
||||
switch (ivl_statement_type(net)) {
|
||||
case IVL_ST_FORK:
|
||||
break;
|
||||
case IVL_ST_FORK_JOIN_ANY:
|
||||
if (join_count < 2)
|
||||
break;
|
||||
is_embeddable = 0;
|
||||
join_detach_count = join_count - 1;
|
||||
join_count = 1;
|
||||
break;
|
||||
case IVL_ST_FORK_JOIN_NONE:
|
||||
is_embeddable = 0;
|
||||
join_detach_count = join_count;
|
||||
join_count = 0;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* cnt is the number of sub-threads. If the fork-join has no
|
||||
name, then we can put one of the sub-threads in the current
|
||||
thread, so decrement the count by one and use the current
|
||||
scope for all the threads. */
|
||||
if (! is_named) {
|
||||
cnt -= 1;
|
||||
if (is_embeddable)
|
||||
join_count -= 1;
|
||||
if (scope==0)
|
||||
scope = sscope;
|
||||
}
|
||||
|
||||
transient_id += cnt;
|
||||
transient_id += join_count;
|
||||
|
||||
/* Draw a fork statement for all but one of the threads of the
|
||||
fork/join. Send the threads off to a bit of code where they
|
||||
are implemented. */
|
||||
for (idx = 0 ; idx < cnt ; idx += 1) {
|
||||
for (idx = 0 ; idx < (join_count+join_detach_count) ; idx += 1) {
|
||||
fprintf(vvp_out, " %%fork t_%u, S_%p;\n",
|
||||
id_base+idx, scope);
|
||||
}
|
||||
|
||||
/* If we are putting one sub-thread into the current thread,
|
||||
then draw its code here. */
|
||||
if (! is_named)
|
||||
rc += show_statement(ivl_stmt_block_stmt(net, cnt), scope);
|
||||
if (is_embeddable)
|
||||
rc += show_statement(ivl_stmt_block_stmt(net, join_count), scope);
|
||||
|
||||
|
||||
/* Generate enough joins to collect all the sub-threads. */
|
||||
for (idx = 0 ; idx < cnt ; idx += 1) {
|
||||
for (idx = 0 ; idx < join_count ; idx += 1)
|
||||
fprintf(vvp_out, " %%join;\n");
|
||||
}
|
||||
if (join_detach_count > 0)
|
||||
fprintf(vvp_out, " %%join/detach %u;\n", join_detach_count);
|
||||
/* Jump around all the threads that I'm creating. */
|
||||
fprintf(vvp_out, " %%jmp t_%u;\n", out);
|
||||
|
||||
/* Change the compiling scope to be the named forks scope. */
|
||||
if (is_named) fprintf(vvp_out, " .scope S_%p;\n", scope);
|
||||
/* Generate the sub-threads themselves. */
|
||||
for (idx = 0 ; idx < cnt ; idx += 1) {
|
||||
for (idx = 0 ; idx < (join_count + join_detach_count) ; idx += 1) {
|
||||
fprintf(vvp_out, "t_%u ;\n", id_base+idx);
|
||||
clear_expression_lookaside();
|
||||
rc += show_statement(ivl_stmt_block_stmt(net, idx), scope);
|
||||
fprintf(vvp_out, " %%end;\n");
|
||||
}
|
||||
/* Return to the previous scope. */
|
||||
if (is_named) fprintf(vvp_out, " .scope S_%p;\n", sscope);
|
||||
if (sscope) fprintf(vvp_out, " .scope S_%p;\n", sscope);
|
||||
|
||||
/* This is the label for the out. Use this to branch around
|
||||
the implementations of all the child threads. */
|
||||
|
|
@ -2073,6 +2103,8 @@ static int show_statement(ivl_statement_t net, ivl_scope_t sscope)
|
|||
break;
|
||||
|
||||
case IVL_ST_FORK:
|
||||
case IVL_ST_FORK_JOIN_ANY:
|
||||
case IVL_ST_FORK_JOIN_NONE:
|
||||
rc += show_stmt_fork(net, sscope);
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ extern bool of_JMP0(vthread_t thr, vvp_code_t code);
|
|||
extern bool of_JMP0XZ(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_JMP1(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_JOIN(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_JOIN_DETACH(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_AR(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_AV(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_AVP0(vthread_t thr, vvp_code_t code);
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ static const struct opcode_table_s opcode_table[] = {
|
|||
{ "%jmp/0xz",of_JMP0XZ, 2, {OA_CODE_PTR, OA_BIT1, OA_NONE} },
|
||||
{ "%jmp/1", of_JMP1, 2, {OA_CODE_PTR, OA_BIT1, OA_NONE} },
|
||||
{ "%join", of_JOIN, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%join/detach",of_JOIN_DETACH,1,{OA_NUMBER,OA_NONE, OA_NONE} },
|
||||
{ "%load/ar",of_LOAD_AR,3, {OA_BIT1, OA_ARR_PTR, OA_BIT2} },
|
||||
{ "%load/av",of_LOAD_AV,3, {OA_BIT1, OA_ARR_PTR, OA_BIT2} },
|
||||
{ "%load/avp0",of_LOAD_AVP0,3, {OA_BIT1, OA_ARR_PTR, OA_BIT2} },
|
||||
|
|
|
|||
|
|
@ -526,6 +526,14 @@ If the matching child instruction is still running, a %join suspends
|
|||
the calling thread until the child ends. If the child is already
|
||||
ended, then the %join does not block or yield the thread.
|
||||
|
||||
* %join/detach <n>
|
||||
|
||||
This is also a partner to the %ork. This instruction causes the thread
|
||||
to detach <n> threads from the current thread. The <n> should be ALL
|
||||
the children, and none of those children may be automatic. This
|
||||
instruction is used to implement join_none and join_any from the
|
||||
Verilog source.
|
||||
|
||||
* %load/av <bit>, <array-label>, <wid>
|
||||
|
||||
This instruction loads a word from the specified array. The word
|
||||
|
|
|
|||
237
vvp/vthread.cc
237
vvp/vthread.cc
|
|
@ -28,7 +28,9 @@
|
|||
#ifdef CHECK_WITH_VALGRIND
|
||||
# include "vvp_cleanup.h"
|
||||
#endif
|
||||
# include <set>
|
||||
# include <typeinfo>
|
||||
# include <vector>
|
||||
# include <cstdlib>
|
||||
# include <climits>
|
||||
# include <cstring>
|
||||
|
|
@ -38,6 +40,8 @@
|
|||
# include <iostream>
|
||||
# include <cstdio>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/* This is the size of an unsigned long in bits. This is just a
|
||||
convenience macro. */
|
||||
# define CPU_WORD_BITS (8*sizeof(unsigned long))
|
||||
|
|
@ -51,35 +55,26 @@
|
|||
*
|
||||
* ** Notes On The Interactions of %fork/%join/%end:
|
||||
*
|
||||
* The %fork instruction creates a new thread and pushes that onto the
|
||||
* stack of children for the thread. This new thread, then, becomes
|
||||
* the new direct descendant of the thread. This new thread is
|
||||
* therefore also the first thread to be reaped when the parent does a
|
||||
* %join.
|
||||
* The %fork instruction creates a new thread and pushes that into a
|
||||
* set of children for the thread. This new thread, then, becomes a
|
||||
* child of the current thread, and the current thread a parent of the
|
||||
* new thread. Any child can be reaped by a %join.
|
||||
*
|
||||
* Children placed into an automatic scope are given special
|
||||
* treatment, which is required to make function/tasks calls that they
|
||||
* represent work correctly. These automatic children are copied into
|
||||
* an automatic_children set to mark them for this handling. %join
|
||||
* operations will guarantee that automatic threads are joined first,
|
||||
* before any non-automatic threads.
|
||||
*
|
||||
* It is a programming error for a thread that created threads to not
|
||||
* %join as many as it created before it %ends. The linear stack for
|
||||
* tracking thread relationships will create a mess otherwise. For
|
||||
* example, if A creates B then C, the stack is:
|
||||
* %join (or %join/detach) as many as it created before it %ends. The
|
||||
* children set will get messed up otherwise.
|
||||
*
|
||||
* A --> C --> B
|
||||
*
|
||||
* If C then %forks X, the stack is:
|
||||
*
|
||||
* A --> C --> X --> B
|
||||
*
|
||||
* If C %ends without a join, then the stack is:
|
||||
*
|
||||
* A --> C(zombie) --> X --> B
|
||||
*
|
||||
* If A then executes 2 %joins, it will reap C and X (when it ends)
|
||||
* leaving B in purgatory. What's worse, A will block on the schedules
|
||||
* of X and C instead of C and B, possibly creating incorrect timing.
|
||||
*
|
||||
* The schedule_parent_on_end flag is used by threads to tell their
|
||||
* children that they are waiting for it to end. It is set by a %join
|
||||
* instruction if the child is not already done. The thread that
|
||||
* executes a %join instruction sets the flag in its child.
|
||||
* the i_am_joining flag is a clue to children that the parent is
|
||||
* blocked in a %join and may need to be scheduled. The %end
|
||||
* instruction will check this flag in the parent to see if it should
|
||||
* notify the parent that something is interesting.
|
||||
*
|
||||
* The i_have_ended flag, on the other hand, is used by threads to
|
||||
* tell their parents that they are already dead. A thread that
|
||||
|
|
@ -105,14 +100,15 @@ struct vthread_s {
|
|||
} words[16];
|
||||
|
||||
/* My parent sets this when it wants me to wake it up. */
|
||||
unsigned schedule_parent_on_end :1;
|
||||
unsigned i_am_joining :1;
|
||||
unsigned i_have_ended :1;
|
||||
unsigned waiting_for_event :1;
|
||||
unsigned is_scheduled :1;
|
||||
unsigned delay_delete :1;
|
||||
unsigned fork_count :8;
|
||||
/* This points to the sole child of the thread. */
|
||||
struct vthread_s*child;
|
||||
/* This points to the children of the thread. */
|
||||
set<struct vthread_s*>children;
|
||||
/* No more then 1 of the children is automatic. */
|
||||
set<vthread_s*>automatic_children;
|
||||
/* This points to my parent, if I have one. */
|
||||
struct vthread_s*parent;
|
||||
/* This points to the containing scope. */
|
||||
|
|
@ -126,6 +122,9 @@ struct vthread_s {
|
|||
uint64_t ecount;
|
||||
};
|
||||
|
||||
static bool test_joinable(vthread_t thr, vthread_t child);
|
||||
static void do_join(vthread_t thr, vthread_t child);
|
||||
|
||||
struct __vpiScope* vthread_scope(struct vthread_s*thr)
|
||||
{
|
||||
return thr->parent_scope;
|
||||
|
|
@ -400,19 +399,17 @@ vthread_t vthread_new(vvp_code_t pc, struct __vpiScope*scope)
|
|||
vthread_t thr = new struct vthread_s;
|
||||
thr->pc = pc;
|
||||
thr->bits4 = vvp_vector4_t(32);
|
||||
thr->child = 0;
|
||||
thr->parent = 0;
|
||||
thr->parent_scope = scope;
|
||||
thr->wait_next = 0;
|
||||
thr->wt_context = 0;
|
||||
thr->rd_context = 0;
|
||||
|
||||
thr->schedule_parent_on_end = 0;
|
||||
thr->i_am_joining = 0;
|
||||
thr->is_scheduled = 0;
|
||||
thr->i_have_ended = 0;
|
||||
thr->delay_delete = 0;
|
||||
thr->waiting_for_event = 0;
|
||||
thr->fork_count = 0;
|
||||
thr->event = 0;
|
||||
thr->ecount = 0;
|
||||
|
||||
|
|
@ -469,16 +466,19 @@ void vthreads_delete(struct __vpiScope*scope)
|
|||
*/
|
||||
static void vthread_reap(vthread_t thr)
|
||||
{
|
||||
if (thr->child) {
|
||||
assert(thr->child->parent == thr);
|
||||
thr->child->parent = thr->parent;
|
||||
if (thr->children.size() > 0) {
|
||||
for (set<vthread_t>::iterator cur = thr->children.begin()
|
||||
; cur != thr->children.end() ; ++cur) {
|
||||
vthread_t curp = *cur;
|
||||
assert(curp->parent == thr);
|
||||
curp->parent = thr->parent;
|
||||
}
|
||||
}
|
||||
if (thr->parent) {
|
||||
assert(thr->parent->child == thr);
|
||||
thr->parent->child = thr->child;
|
||||
//assert(thr->parent->child == thr);
|
||||
thr->parent->children.erase(thr);
|
||||
}
|
||||
|
||||
thr->child = 0;
|
||||
thr->parent = 0;
|
||||
|
||||
// Remove myself from the containing scope.
|
||||
|
|
@ -490,7 +490,7 @@ static void vthread_reap(vthread_t thr)
|
|||
it now. Otherwise, let the schedule event (which will
|
||||
execute the thread at of_ZOMBIE) delete the object. */
|
||||
if ((thr->is_scheduled == 0) && (thr->waiting_for_event == 0)) {
|
||||
assert(thr->fork_count == 0);
|
||||
assert(thr->children.size() == 0);
|
||||
assert(thr->wait_next == 0);
|
||||
if (thr->delay_delete)
|
||||
schedule_del_thr(thr);
|
||||
|
|
@ -1956,28 +1956,30 @@ static bool do_disable(vthread_t thr, vthread_t match)
|
|||
/* Turn off all the children of the thread. Simulate a %join
|
||||
for as many times as needed to clear the results of all the
|
||||
%forks that this thread has done. */
|
||||
while (thr->fork_count > 0) {
|
||||
while (!thr->children.empty()) {
|
||||
|
||||
vthread_t tmp = thr->child;
|
||||
vthread_t tmp = *(thr->children.begin());
|
||||
assert(tmp);
|
||||
assert(tmp->parent == thr);
|
||||
tmp->schedule_parent_on_end = 0;
|
||||
thr->i_am_joining = 0;
|
||||
if (do_disable(tmp, match))
|
||||
flag = true;
|
||||
|
||||
thr->fork_count -= 1;
|
||||
|
||||
vthread_reap(tmp);
|
||||
}
|
||||
|
||||
if (thr->parent && thr->parent->i_am_joining) {
|
||||
// If a parent is waiting in a %join, wake it up. Note
|
||||
// that it is possible to be waiting in a %join yet
|
||||
// already scheduled if multiple child threads are
|
||||
// ending. So check if the thread is already scheduled
|
||||
// before scheduling it again.
|
||||
vthread_t parent = thr->parent;
|
||||
parent->i_am_joining = 0;
|
||||
if (! parent->i_have_ended)
|
||||
schedule_vthread(parent, 0, true);
|
||||
|
||||
if (thr->schedule_parent_on_end) {
|
||||
/* If a parent is waiting in a %join, wake it up. */
|
||||
assert(thr->parent);
|
||||
assert(thr->parent->fork_count > 0);
|
||||
|
||||
thr->parent->fork_count -= 1;
|
||||
schedule_vthread(thr->parent, 0, true);
|
||||
// Let the parent do the reaping.
|
||||
vthread_reap(thr);
|
||||
|
||||
} else if (thr->parent) {
|
||||
|
|
@ -2007,7 +2009,7 @@ bool of_DISABLE(vthread_t thr, vvp_code_t cp)
|
|||
while (! scope->threads.empty()) {
|
||||
set<vthread_t>::iterator cur = scope->threads.begin();
|
||||
|
||||
/* If I am disabling myself, that remember that fact so
|
||||
/* If I am disabling myself, then remember that fact so
|
||||
that I can finish this statement differently. */
|
||||
if (*cur == thr)
|
||||
disabled_myself_flag = true;
|
||||
|
|
@ -2350,20 +2352,25 @@ bool of_DIV_WR(vthread_t thr, vvp_code_t cp)
|
|||
bool of_END(vthread_t thr, vvp_code_t)
|
||||
{
|
||||
assert(! thr->waiting_for_event);
|
||||
assert( thr->fork_count == 0 );
|
||||
thr->i_have_ended = 1;
|
||||
thr->pc = codespace_null();
|
||||
|
||||
/* If I have a parent who is waiting for me, then mark that I
|
||||
have ended, and schedule that parent. Also, finish the
|
||||
%join for the parent. */
|
||||
if (thr->schedule_parent_on_end) {
|
||||
assert(thr->parent);
|
||||
assert(thr->parent->fork_count > 0);
|
||||
if (thr->parent && thr->parent->i_am_joining) {
|
||||
vthread_t tmp = thr->parent;
|
||||
|
||||
thr->parent->fork_count -= 1;
|
||||
schedule_vthread(thr->parent, 0, true);
|
||||
vthread_reap(thr);
|
||||
// Detect that the parent is waiting on an automatic
|
||||
// thread. Automatic threads must be reaped first. If
|
||||
// the parent is waiting on an auto (other then me) then
|
||||
// go into zomple state to be picked up later.
|
||||
if (!test_joinable(tmp, thr))
|
||||
return false;
|
||||
|
||||
tmp->i_am_joining = 0;
|
||||
schedule_vthread(tmp, 0, true);
|
||||
do_join(tmp, thr);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -2375,7 +2382,7 @@ bool of_END(vthread_t thr, vvp_code_t)
|
|||
main thread (there is no other parent) and an error (not
|
||||
enough %joins) has been detected. */
|
||||
if (thr->parent == 0) {
|
||||
assert(thr->child == 0);
|
||||
assert(thr->children.empty());
|
||||
vthread_reap(thr);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2523,29 +2530,25 @@ bool of_FORCE_X0(vthread_t thr, vvp_code_t cp)
|
|||
|
||||
/*
|
||||
* The %fork instruction causes a new child to be created and pushed
|
||||
* in front of any existing child. This causes the new child to be the
|
||||
* parent of any previous children, and for me to be the parent of the
|
||||
* in front of any existing child. This causes the new child to be
|
||||
* added to the list of children, and for me to be the parent of the
|
||||
* new child.
|
||||
*/
|
||||
bool of_FORK(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
vthread_t child = vthread_new(cp->cptr2, cp->scope);
|
||||
|
||||
if (cp->scope->is_automatic) {
|
||||
/* The context allocated for this child is the top entry
|
||||
on the write context stack. */
|
||||
child->wt_context = thr->wt_context;
|
||||
child->rd_context = thr->wt_context;
|
||||
|
||||
thr->automatic_children.insert(child);
|
||||
}
|
||||
|
||||
child->child = thr->child;
|
||||
child->parent = thr;
|
||||
thr->child = child;
|
||||
if (child->child) {
|
||||
assert(child->child->parent == thr);
|
||||
child->child->parent = child;
|
||||
}
|
||||
|
||||
thr->fork_count += 1;
|
||||
thr->children.insert(child);
|
||||
|
||||
/* If the new child was created to evaluate a function,
|
||||
run it immediately, then return to this thread. */
|
||||
|
|
@ -2854,20 +2857,27 @@ bool of_JMP1(vthread_t thr, vvp_code_t cp)
|
|||
}
|
||||
|
||||
/*
|
||||
* The %join instruction causes the thread to wait for the one and
|
||||
* only child to die. If it is already dead (and a zombie) then I
|
||||
* reap it and go on. Otherwise, I tell the child that I am ready for
|
||||
* it to die, and it will reschedule me when it does.
|
||||
* The %join instruction causes the thread to wait for one child
|
||||
* to die. If a child is already dead (and a zombie) then I reap
|
||||
* it and go on. Otherwise, I mark myself as waiting in a join so that
|
||||
* children know to wake me when they finish.
|
||||
*/
|
||||
bool of_JOIN(vthread_t thr, vvp_code_t)
|
||||
|
||||
static bool test_joinable(vthread_t thr, vthread_t child)
|
||||
{
|
||||
assert(thr->child);
|
||||
assert(thr->child->parent == thr);
|
||||
set<vthread_t>::iterator auto_cur = thr->automatic_children.find(child);
|
||||
if (!thr->automatic_children.empty() && auto_cur == thr->automatic_children.end())
|
||||
return false;
|
||||
|
||||
assert(thr->fork_count > 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the child thread is in an automatic scope... */
|
||||
if (thr->child->wt_context) {
|
||||
static void do_join(vthread_t thr, vthread_t child)
|
||||
{
|
||||
assert(child->parent == thr);
|
||||
|
||||
/* If the immediate child thread is in an automatic scope... */
|
||||
if (thr->automatic_children.erase(child) != 0) {
|
||||
/* and is the top level task/function thread... */
|
||||
if (thr->wt_context != thr->rd_context) {
|
||||
/* Pop the child context from the write context stack. */
|
||||
|
|
@ -2880,18 +2890,66 @@ bool of_JOIN(vthread_t thr, vvp_code_t)
|
|||
}
|
||||
}
|
||||
|
||||
/* If the child has already ended, reap it now. */
|
||||
if (thr->child->i_have_ended) {
|
||||
thr->fork_count -= 1;
|
||||
vthread_reap(thr->child);
|
||||
vthread_reap(child);
|
||||
}
|
||||
|
||||
bool of_JOIN(vthread_t thr, vvp_code_t)
|
||||
{
|
||||
assert( !thr->i_am_joining );
|
||||
assert( !thr->children.empty());
|
||||
|
||||
// Are there any children that have already ended? If so, then
|
||||
// join with that one.
|
||||
for (set<vthread_t>::iterator cur = thr->children.begin()
|
||||
; cur != thr->children.end() ; ++cur) {
|
||||
vthread_t curp = *cur;
|
||||
if (!curp->i_have_ended)
|
||||
continue;
|
||||
|
||||
if (!test_joinable(thr, curp))
|
||||
continue;
|
||||
|
||||
// found somenting!
|
||||
do_join(thr, curp);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Otherwise, I get to start waiting. */
|
||||
thr->child->schedule_parent_on_end = 1;
|
||||
// Otherwise, tell my children to awaken me when they end,
|
||||
// then pause.
|
||||
thr->i_am_joining = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* This %join/detach <n> instruction causes the thread to detach
|
||||
* threads that were created by an earlier %fork.
|
||||
*/
|
||||
bool of_JOIN_DETACH(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
unsigned long count = cp->number;
|
||||
|
||||
assert(thr->automatic_children.empty());
|
||||
assert(count == thr->children.size());
|
||||
|
||||
while (!thr->children.empty()) {
|
||||
vthread_t child = *thr->children.begin();
|
||||
assert(child->parent == thr);
|
||||
|
||||
// We cannot detach automatic tasks/functions
|
||||
assert(child->wt_context == 0);
|
||||
if (child->i_have_ended) {
|
||||
// If the child has already ended, then reap it.
|
||||
vthread_reap(child);
|
||||
|
||||
} else {
|
||||
thr->children.erase(child);
|
||||
child->parent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* %load/ar <bit>, <array-label>, <index>;
|
||||
*/
|
||||
|
|
@ -4608,7 +4666,7 @@ bool of_XOR(vthread_t thr, vvp_code_t cp)
|
|||
bool of_ZOMBIE(vthread_t thr, vvp_code_t)
|
||||
{
|
||||
thr->pc = codespace_null();
|
||||
if ((thr->parent == 0) && (thr->child == 0)) {
|
||||
if ((thr->parent == 0) && (thr->children.empty())) {
|
||||
if (thr->delay_delete)
|
||||
schedule_del_thr(thr);
|
||||
else
|
||||
|
|
@ -4629,8 +4687,7 @@ bool of_EXEC_UFUNC(vthread_t thr, vvp_code_t cp)
|
|||
struct __vpiScope*child_scope = cp->ufunc_core_ptr->func_scope();
|
||||
assert(child_scope);
|
||||
|
||||
assert(thr->child == 0);
|
||||
assert(thr->fork_count == 0);
|
||||
assert(thr->children.empty());
|
||||
|
||||
/* We can take a number of shortcuts because we know that a
|
||||
continuous assignment can only occur in a static scope. */
|
||||
|
|
|
|||
Loading…
Reference in New Issue