diff --git a/PScope.h b/PScope.h index e04fe54e3..3a16c72fa 100644 --- a/PScope.h +++ b/PScope.h @@ -99,6 +99,9 @@ class LexicalScope { list behaviors; list analog_behaviors; + // Enumeration sets. + list enum_sets; + LexicalScope* parent_scope() const { return parent_; } protected: @@ -106,6 +109,8 @@ class LexicalScope { void dump_localparams_(ostream&out, unsigned indent) const; + void dump_enumerations_(ostream&out, unsigned indent) const; + void dump_events_(ostream&out, unsigned indent) const; void dump_wires_(ostream&out, unsigned indent) const; diff --git a/PWire.cc b/PWire.cc index 911c2d537..652c02d6a 100644 --- a/PWire.cc +++ b/PWire.cc @@ -30,7 +30,7 @@ PWire::PWire(perm_string n, signed_(false), isint_(false), port_msb_(0), port_lsb_(0), port_set_(false), net_msb_(0), net_lsb_(0), net_set_(false), is_scalar_(false), - error_cnt_(0), lidx_(0), ridx_(0), discipline_(0) + error_cnt_(0), lidx_(0), ridx_(0), enum_set_(0), discipline_(0) { if (t == NetNet::INTEGER) { type_ = NetNet::REG; @@ -206,6 +206,12 @@ void PWire::set_memory_idx(PExpr*ldx, PExpr*rdx) } } +void PWire::set_enumeration(enum_set_t enum_set) +{ + assert(enum_set_ == 0); + enum_set_ = enum_set; +} + void PWire::set_discipline(ivl_discipline_t d) { assert(discipline_ == 0); diff --git a/PWire.h b/PWire.h index d87738e08..72c02a63d 100644 --- a/PWire.h +++ b/PWire.h @@ -79,6 +79,8 @@ class PWire : public LineInfo { void set_memory_idx(PExpr*ldx, PExpr*rdx); + void set_enumeration(enum_set_t enum_set); + void set_discipline(ivl_discipline_t); ivl_discipline_t get_discipline(void) const; @@ -113,6 +115,8 @@ class PWire : public LineInfo { PExpr*lidx_; PExpr*ridx_; + enum_set_t enum_set_; + ivl_discipline_t discipline_; private: // not implemented diff --git a/design_dump.cc b/design_dump.cc index 2eb7c8793..5b525abfa 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1187,6 +1187,23 @@ void NetScope::dump(ostream&o) const } } + o << " enum sets {" << endl; + + /* Dump the enumerations and enum names in this scope. */ + for (list::const_iterator cur = enum_sets_.begin() + ; cur != enum_sets_.end() ; ++ cur) { + o << " " << *cur << endl; + } + o << " }" << endl; + + o << " enum names {" << endl; + for (map::const_iterator cur = enum_names_.begin() + ; cur != enum_names_.end() ; ++ cur) { + o << " " << cur->first << " = " << cur->second->value() + << " from " << cur->second->enumeration() << endl; + } + o << " }" << endl; + /* Dump the events in this scope. */ for (NetEvent*cur = events_ ; cur ; cur = cur->snext_) { o << " event " << cur->name() << "; nprobe=" @@ -1401,6 +1418,13 @@ void NetEConst::dump(ostream&o) const o << value_; } +void NetEConstEnum::dump(ostream&o) const +{ + o << "<" << name_ << "="; + NetEConst::dump(o); + o << ", wid=" << expr_width() << ">"; +} + void NetEConstParam::dump(ostream&o) const { o << "<" << name_ << "="; diff --git a/dup_expr.cc b/dup_expr.cc index 97720703d..3c3c2340c 100644 --- a/dup_expr.cc +++ b/dup_expr.cc @@ -49,6 +49,14 @@ NetEConst* NetEConst::dup_expr() const return tmp; } +NetEConstEnum* NetEConstEnum::dup_expr() const +{ + NetEConstEnum*tmp = new NetEConstEnum(scope_, name_, enum_set_, value()); + assert(tmp); + tmp->set_line(*this); + return tmp; +} + NetEConstParam* NetEConstParam::dup_expr() const { NetEConstParam*tmp = new NetEConstParam(scope_, name_, value()); diff --git a/elab_scope.cc b/elab_scope.cc index a805e3294..8f5abb55f 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -206,6 +206,31 @@ static void elaborate_scope_localparams_(Design*des, NetScope*scope, } } +static void elaborate_scope_enumeration(Design*des, NetScope*scope, + enum_set_t enum_set) +{ + scope->add_enumeration_set(enum_set); + + for (map::const_iterator cur = enum_set->begin() + ; cur != enum_set->end() ; ++ cur) { + + bool rc = scope->add_enumeration_name(enum_set, cur->first); + if (! rc) { + cerr << "<>:0: error: Duplicate enumeration name " << cur->first << endl; + des->errors += 1; + } + } +} + +static void elaborate_scope_enumerations(Design*des, NetScope*scope, + const list&enum_sets) +{ + for (list::const_iterator cur = enum_sets.begin() + ; cur != enum_sets.end() ; ++ cur) { + elaborate_scope_enumeration(des, scope, *cur); + } +} + static void replace_scope_parameters_(NetScope*scope, const LineInfo&loc, const Module::replace_t&replacements) { @@ -434,6 +459,8 @@ bool Module::elaborate_scope(Design*des, NetScope*scope, elaborate_scope_localparams_(des, scope, localparams); + elaborate_scope_enumerations(des, scope, enum_sets); + // Run through the defparams for this module, elaborate the // expressions in this context and save the result is a table // for later final override. diff --git a/emit.cc b/emit.cc index 8cd2dcc07..e78c6daf0 100644 --- a/emit.cc +++ b/emit.cc @@ -516,6 +516,11 @@ void NetEConst::expr_scan(struct expr_scan_t*tgt) const tgt->expr_const(this); } +void NetEConstEnum::expr_scan(struct expr_scan_t*tgt) const +{ + tgt->expr_const(this); +} + void NetEConstParam::expr_scan(struct expr_scan_t*tgt) const { tgt->expr_param(this); diff --git a/net_expr.cc b/net_expr.cc index 1dc1c3cb1..3e4b2d5a6 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -432,6 +432,20 @@ unsigned NetEConcat::repeat() const return repeat_value_; } +NetEConstEnum::NetEConstEnum(NetScope*s, perm_string n, enum_set_t eset, const verinum&v) +: NetEConst(v), scope_(s), enum_set_(eset) +{ +} + +NetEConstEnum::~NetEConstEnum() +{ +} + +const enum_set_t NetEConstEnum::enumeration() const +{ + return enum_set_; +} + NetECReal::NetECReal(const verireal&val) : value_(val) { diff --git a/net_scope.cc b/net_scope.cc index 5bae6462b..22b0ca2ae 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -207,25 +207,42 @@ NetExpr* NetScope::set_localparam(perm_string key, NetExpr*expr, const NetExpr* NetScope::get_parameter(const char* key, const NetExpr*&msb, const NetExpr*&lsb) const +{ + return get_parameter(perm_string::literal(key), msb, lsb); +} + +const NetExpr* NetScope::get_parameter(perm_string key, + const NetExpr*&msb, + const NetExpr*&lsb) const { map::const_iterator idx; - idx = parameters.find(perm_string::literal(key)); + idx = parameters.find(key); if (idx != parameters.end()) { msb = (*idx).second.msb; lsb = (*idx).second.lsb; return (*idx).second.expr; } - idx = localparams.find(perm_string::literal(key)); + idx = localparams.find(key); if (idx != localparams.end()) { msb = (*idx).second.msb; lsb = (*idx).second.lsb; return (*idx).second.expr; } + map::const_iterator eidx; + + eidx = enum_names_.find(key); + if (eidx != enum_names_.end()) { + msb = 0; + lsb = 0; + return eidx->second; + } + return 0; } + map::iterator NetScope::find_parameter(perm_string key) { map::iterator idx; @@ -436,6 +453,25 @@ NetNet* NetScope::find_signal(perm_string key) return 0; } +void NetScope::add_enumeration_set(enum_set_t enum_set) +{ + enum_sets_.push_back(enum_set); +} + +bool NetScope::add_enumeration_name(enum_set_t enum_set, perm_string name) +{ + enum_set_m::const_iterator enum_val = enum_set->find(name); + assert(enum_val != enum_set->end()); + + NetEConstEnum*val = new NetEConstEnum(this, name, enum_set, enum_val->second); + + pair::iterator, bool> cur; + cur = enum_names_.insert(make_pair(name,val)); + + // Return TRUE if the name is added (i.e. is NOT a duplicate.) + return cur.second; +} + /* * This method locates a child scope by name. The name is the simple * name of the child, no hierarchy is searched. diff --git a/netlist.h b/netlist.h index 074dfc55c..87fb2cbe6 100644 --- a/netlist.h +++ b/netlist.h @@ -64,6 +64,7 @@ class NetScope; class NetEvProbe; class NetExpr; class NetEAccess; +class NetEConstEnum; class NetESignal; class NetFuncDef; class NetRamDq; @@ -718,6 +719,9 @@ class NetScope : public Attrib { const NetExpr*get_parameter(const char* name, const NetExpr*&msb, const NetExpr*&lsb) const; + const NetExpr*get_parameter(perm_string name, + const NetExpr*&msb, + const NetExpr*&lsb) const; /* These are used by defparam elaboration to replace the expression with a new expression, without affecting the @@ -745,6 +749,9 @@ class NetScope : public Attrib { void rem_signal(NetNet*); NetNet* find_signal(perm_string name); + void add_enumeration_set(enum_set_t enum_set); + bool add_enumeration_name(enum_set_t enum_set, perm_string); + /* The parent and child() methods allow users of NetScope objects to locate nearby scopes. */ NetScope* parent() { return up_; } @@ -920,6 +927,13 @@ class NetScope : public Attrib { NetFuncDef*func_; }; + // Enumerations. The enum_sets_ is a list of all the + // enumerations present in this scope. The enum_names_ is a + // map of all the enumeration names back to the sets that + // contain them. + std::list enum_sets_; + std::map enum_names_; + NetScope*up_; map children_; @@ -1707,6 +1721,29 @@ class NetEConst : public NetExpr { verinum value_; }; +class NetEConstEnum : public NetEConst { + + public: + explicit NetEConstEnum(NetScope*scope, perm_string name, + enum_set_t enum_set, const verinum&val); + ~NetEConstEnum(); + + perm_string name() const; + const NetScope*scope() const; + const enum_set_t enumeration() const; + + virtual bool set_width(unsigned w, bool last_chance =false); + virtual void expr_scan(struct expr_scan_t*) const; + virtual void dump(ostream&) const; + + virtual NetEConstEnum* dup_expr() const; + + private: + NetScope*scope_; + enum_set_t enum_set_; + perm_string name_; +}; + class NetEConstParam : public NetEConst { public: diff --git a/netmisc.h b/netmisc.h index 3d88ca8aa..b7618f7ed 100644 --- a/netmisc.h +++ b/netmisc.h @@ -41,7 +41,7 @@ extern NetScope* symbol_search(const LineInfo*li, NetScope*start, pform_name_t path, NetNet*&net, /* net/reg */ - const NetExpr*&par,/* parameter */ + const NetExpr*&par,/* parameter/expr */ NetEvent*&eve, /* named event */ const NetExpr*&ex1, const NetExpr*&ex2); @@ -50,7 +50,7 @@ inline NetScope* symbol_search(const LineInfo*li, NetScope*start, const pform_name_t&path, NetNet*&net, /* net/reg */ - const NetExpr*&par,/* parameter */ + const NetExpr*&par,/* parameter/expr */ NetEvent*&eve /* named event */) { const NetExpr*ex1, *ex2; diff --git a/pform.cc b/pform.cc index 953cd3a07..24e5d9c5a 100644 --- a/pform.cc +++ b/pform.cc @@ -378,6 +378,27 @@ static void pform_put_wire_in_scope(perm_string name, PWire*net) lexical_scope->wires[name] = net; } +static void pform_put_enum_set_in_scope(enum_set_t enum_set) +{ + lexical_scope->enum_sets.push_back(enum_set); +} + +static PWire*pform_get_make_wire_in_scope(perm_string name, NetNet::Type net_type, NetNet::PortType port_type, ivl_variable_type_t vt_type) +{ + PWire*cur = pform_get_wire_in_scope(name); + if (cur == 0) { + cur = new PWire(name, net_type, port_type, vt_type); + pform_put_wire_in_scope(name, cur); + } else { + bool rc = cur->set_wire_type(net_type); + assert(rc); + rc = cur->set_data_type(vt_type); + assert(rc); + } + + return cur; +} + static void pform_put_behavior_in_scope(PProcess*pp) { lexical_scope->behaviors.push_back(pp); @@ -2377,19 +2398,7 @@ void pform_set_port_type(const struct vlltype&li, static void pform_set_reg_integer(perm_string name) { - PWire*cur = pform_get_wire_in_scope(name); - if (cur == 0) { - cur = new PWire(name, NetNet::INTEGER, - NetNet::NOT_A_PORT, - IVL_VT_LOGIC); - cur->set_signed(true); - pform_put_wire_in_scope(name, cur); - } else { - bool rc = cur->set_wire_type(NetNet::INTEGER); - assert(rc); - cur->set_data_type(IVL_VT_LOGIC); - cur->set_signed(true); - } + PWire*cur = pform_get_make_wire_in_scope(name, NetNet::INTEGER, NetNet::NOT_A_PORT, IVL_VT_LOGIC); assert(cur); cur->set_range(new PENumber(new verinum(integer_width-1, integer_width)), @@ -2411,16 +2420,7 @@ void pform_set_reg_integer(list*names) static void pform_set_reg_time(perm_string name) { - PWire*cur = pform_get_wire_in_scope(name); - if (cur == 0) { - cur = new PWire(name, NetNet::REG, NetNet::NOT_A_PORT, IVL_VT_LOGIC); - pform_put_wire_in_scope(name, cur); - } else { - bool rc = cur->set_wire_type(NetNet::REG); - assert(rc); - rc = cur->set_data_type(IVL_VT_LOGIC); - assert(rc); - } + PWire*cur = pform_get_make_wire_in_scope(name, NetNet::REG, NetNet::NOT_A_PORT, IVL_VT_LOGIC); assert(cur); cur->set_range(new PENumber(new verinum(TIME_WIDTH-1, integer_width)), @@ -2441,17 +2441,7 @@ void pform_set_reg_time(list*names) static void pform_set_integer_2atom(uint64_t width, bool signed_flag, perm_string name) { - PWire*cur = pform_get_wire_in_scope(name); - if (cur == 0) { - cur = new PWire(name, NetNet::REG, NetNet::NOT_A_PORT, IVL_VT_BOOL); - pform_put_wire_in_scope(name, cur); - } else { - bool rc = cur->set_wire_type(NetNet::REG); - assert(rc); - rc = cur->set_data_type(IVL_VT_BOOL); - assert(rc); - } - + PWire*cur = pform_get_make_wire_in_scope(name, NetNet::REG, NetNet::NOT_A_PORT, IVL_VT_BOOL); assert(cur); cur->set_signed(signed_flag); @@ -2471,13 +2461,85 @@ void pform_set_integer_2atom(uint64_t width, bool signed_flag, list delete names; } +static void pform_set_enum(const struct vlltype&li, enum_type_t*enum_type, + enum_set_t enum_set, perm_string name) +{ + PWire*cur = pform_get_make_wire_in_scope(name, NetNet::REG, NetNet::NOT_A_PORT, enum_type->base_type); + assert(cur); + + cur->set_signed(enum_type->signed_flag); + + assert(enum_type->range.get() != 0); + assert(enum_type->range->size() == 2); + cur->set_range(enum_type->range->front(), enum_type->range->back(), SR_NET, false); + cur->set_enumeration(enum_set); +} + void pform_set_enum(const struct vlltype&li, enum_type_t*enum_type, list*names) { - cerr << li.text << ":" << li.first_line << ": " - << "sorry: enum types not supported yet." << endl; - error_count += 1; - delete enum_type; + // By definition, the base type can only be IVL_VT_LOGIC or + // IVL_VT_BOOL. + assert(enum_type->base_type==IVL_VT_LOGIC || enum_type->base_type==IVL_VT_BOOL); + + assert(enum_type->range.get() != 0); + assert(enum_type->range->size() == 2); + + // Scan the list of enum name declarations and evaluate them + // to a map of names with values. This expands out the + // inferred values (if any) and checks for duplicates. + enum_set_t enum_map = new enum_set_m; + verinum cur_value (0); + verinum one_value (1); + for (list::iterator cur = enum_type->names->begin() + ; cur != enum_type->names->end() ; ++ cur) { + + verinum next_value = cur->parm; + if (next_value.len() == 0) { + if (! cur_value.is_defined()) { + cerr << li.text << ":" << li.first_line << ": " + << "error: Enumeration name " << cur->name + << " cannot have inferred value." << endl; + next_value = cur_value; + error_count += 1; + } else { + next_value = cur_value; + cur_value = cur_value + one_value; + } + } else { + if (enum_type->base_type==IVL_VT_BOOL && ! next_value.is_defined()) { + cerr << li.text << ":" << li.first_line << ": " + << "error: Enumeration name " << cur->name + << " Cannot have logic value " << next_value << "." << endl; + error_count += 1; + } + cur_value = next_value + one_value; + } + + map::iterator map_name = enum_map->find(cur->name); + if (map_name == enum_map->end()) { + enum_map->insert(make_pair(cur->name, next_value));; + + } else { + cerr << li.text << ":" << li.first_line << ": " + << "error: Enumeration name " << cur->name + << " is already defined." << endl; + error_count += 1; + } + } + + // Attach the enumeration to the current scope. + pform_put_enum_set_in_scope(enum_map); + + // Now apply the checked enumeration type to the variables + // that are being declared with this type. + for (list::iterator cur = names->begin() + ; cur != names->end() ; ++ cur) { + perm_string txt = *cur; + pform_set_enum(li, enum_type, enum_map, txt); + } + delete names; + delete enum_type; } svector* pform_make_udp_input_ports(list*names) diff --git a/pform_dump.cc b/pform_dump.cc index db3aa7e25..dc071f27b 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -1106,6 +1106,22 @@ void LexicalScope::dump_localparams_(ostream&out, unsigned indent) const } } +void LexicalScope::dump_enumerations_(ostream&out, unsigned indent) const +{ + for (list::const_iterator cur = enum_sets.begin() + ; cur != enum_sets.end() ; ++ cur) { + out << setw(indent) << "" << "enum {" << endl; + + for (map::const_iterator idx = (*cur)->begin() + ; idx != (*cur)->end() ; ++ idx) { + out << setw(indent+4) << "" << idx->first + << " = " << idx->second << endl; + } + + out << setw(indent) << "" << "}" << endl; + } +} + void LexicalScope::dump_events_(ostream&out, unsigned indent) const { for (map::const_iterator cur = events.begin() @@ -1167,6 +1183,8 @@ void Module::dump(ostream&out) const dump_localparams_(out, 4); + dump_enumerations_(out, 4); + typedef map::const_iterator genvar_iter_t; for (genvar_iter_t cur = genvars.begin() ; cur != genvars.end() ; cur++) { diff --git a/pform_types.h b/pform_types.h index df88f6cab..94eb516bd 100644 --- a/pform_types.h +++ b/pform_types.h @@ -21,8 +21,10 @@ // This for the perm_string type. # include "StringHeap.h" +# include "verinum.h" # include # include +# include /* * parse-form types. @@ -47,6 +49,13 @@ struct name_component_t { std::listindex; }; +/* + * The enum_map_t holds the values that represent the enumeration. An + * enumeration, then, is defined by its pointer to the set. + */ +typedef map enum_set_m; +typedef enum_set_m *enum_set_t; + /* * The pform_name_t is the general form for a hierarchical diff --git a/set_width.cc b/set_width.cc index cdd75b094..ebecb81be 100644 --- a/set_width.cc +++ b/set_width.cc @@ -377,13 +377,18 @@ void NetEConst::cast_signed(bool sign_flag) /* * Parameter vectors cannot be resized because they refer to a common - * value. + * value. Ditto for enumeration names. */ bool NetEConstParam::set_width(unsigned w, bool) { return w == expr_width(); } +bool NetEConstEnum::set_width(unsigned w, bool) +{ + return w == expr_width(); +} + /* * Real constants can have whatever width the environment wants, * because it isn't really a vector. The environment will convert this