Merge branch 'x-mil2'

This commit is contained in:
Stephen Williams 2012-06-06 19:17:27 -07:00
commit cec68cfc61
30 changed files with 716 additions and 378 deletions

View File

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

View File

@ -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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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.

View File

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

View File

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

View File

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

View File

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

View File

@ -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
View File

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

View File

@ -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
View File

@ -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
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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} },

View File

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

View File

@ -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. */