From 7d53ac342fab96dcc90186e850fc5c1c81d99da4 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 2 Apr 2011 15:50:49 -0700 Subject: [PATCH 01/19] Fix handling of bool(bit) vectors with odd widths. --- sv_vpi_user.h | 1 + vvp/vpi_signal.cc | 22 +++++++++++++++++++++- vvp/words.cc | 24 +++++------------------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/sv_vpi_user.h b/sv_vpi_user.h index d613f1185..e6e20b527 100644 --- a/sv_vpi_user.h +++ b/sv_vpi_user.h @@ -48,6 +48,7 @@ EXTERN_C_START #define vpiIntVar 612 #define vpiByteVar 614 #define vpiLogicVar vpiReg +#define vpiBitVar 620 /********* TYPESPECS *************/ #define vpiEnumTypespec 633 diff --git a/vvp/vpi_signal.cc b/vvp/vpi_signal.cc index 4d07c997f..520b331b5 100644 --- a/vvp/vpi_signal.cc +++ b/vvp/vpi_signal.cc @@ -924,6 +924,20 @@ static const struct __vpirt vpip_byte_rt = { 0 }; +static const struct __vpirt vpip_bitvar_rt = { + vpiBitVar, + signal_get, + signal_get_str, + signal_get_value, + signal_put_value, + signal_get_handle, + signal_iterate, + 0, + 0, + 0, + 0 +}; + static const struct __vpirt vpip_shortint_rt = { vpiShortIntVar, signal_get, @@ -979,6 +993,9 @@ vpiHandle vpip_make_int4(const char*name, int msb, int lsb, vvp_net_t*vec) return obj; } +/* + * Construct a vpi + */ vpiHandle vpip_make_int2(const char*name, int msb, int lsb, vvp_net_t*vec) { vpiHandle obj = vpip_make_net4(name, msb,lsb, true, vec); @@ -998,7 +1015,10 @@ vpiHandle vpip_make_int2(const char*name, int msb, int lsb, vvp_net_t*vec) obj->vpi_type = &vpip_longint_rt; break; default: - assert(0); + // Every other type of bit vector is a vpiBitVar with + // array dimensions. + obj->vpi_type = &vpip_bitvar_rt; + break; } return obj; diff --git a/vvp/words.cc b/vvp/words.cc index f6336d3f6..9485cc257 100644 --- a/vvp/words.cc +++ b/vvp/words.cc @@ -83,10 +83,9 @@ void compile_varw_real(char*label, vvp_array_t array, * A variable is a special functor, so we allocate that functor and * write the label into the symbol table. */ -static void __compile_var(char*label, char*name, - vvp_array_t array, unsigned long array_addr, - int msb, int lsb, int vpi_type_code, - bool signed_flag, bool local_flag) +void compile_variable(char*label, char*name, + int msb, int lsb, int vpi_type_code, + bool signed_flag, bool local_flag) { unsigned wid = ((msb > lsb)? msb-lsb : lsb-msb) + 1; @@ -108,7 +107,7 @@ static void __compile_var(char*label, char*name, define_functor_symbol(label, net); vpiHandle obj = 0; - if (! local_flag && !array) { + if (! local_flag) { /* Make the vpiHandle for the reg. */ switch (vpi_type_code) { case vpiLogicVar: @@ -130,7 +129,6 @@ static void __compile_var(char*label, char*name, // If the signal has a name, then it goes into the current // scope as a signal. if (name) { - assert(!array); if (obj) vpip_attach_to_current_scope(obj); if (!vpip_peek_current_scope()->is_automatic) { vvp_vector4_t tmp; @@ -138,23 +136,11 @@ static void __compile_var(char*label, char*name, schedule_init_vector(vvp_net_ptr_t(net,0), tmp); } } - // If this is an array word, then it does not have a name, and - // it is attached to the addressed array. - if (array) { - assert(!name); - if (obj) array_attach_word(array, array_addr, obj); - } + free(label); delete[] name; } -void compile_variable(char*label, char*name, - int msb, int lsb, int vpi_type_code, - bool signed_flag, bool local_flag) -{ - __compile_var(label, name, 0, 0, msb, lsb, vpi_type_code, signed_flag, local_flag); -} - vvp_net_t* create_constant_node(const char*val_str) { if (c4string_test(val_str)) { From 098bbeea7c2e668fcfe82ee47662c7a947e329d5 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 3 Apr 2011 17:21:43 -0700 Subject: [PATCH 02/19] Support collapse of PartSelect::PV to concatenation During elaboration, it is sometimes efficient to collapse a collections of PV drivers to a net to a single concatenation. This removes a bunch of resolutions and other nodes, and also is the only way that 2-value logic should work. --- elab_net.cc | 1 + netmisc.cc | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++ netmisc.h | 2 ++ 3 files changed, 79 insertions(+) diff --git a/elab_net.cc b/elab_net.cc index 88b5088c9..9ff968c39 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -616,6 +616,7 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, des->add_node(sub); sub->set_line(*this); connect(sub->pin(0), subsig->pin(0)); + collapse_partselect_pv_to_concat(des, sig); } sig = subsig; } diff --git a/netmisc.cc b/netmisc.cc index 0479092ff..f44b9f7a5 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -791,3 +791,79 @@ uint64_t get_scaled_time_from_real(Design*des, NetScope*scope, NetECReal*val) return delay; } + +/* + * This function looks at the NetNet signal to see if there are any + * NetPartSelect::PV nodes driving this signal. If so, See if they can + * be collapsed into a single concatenation. + */ +void collapse_partselect_pv_to_concat(Design*des, NetNet*sig) +{ + NetScope*scope = sig->scope(); + vector ps_map (sig->vector_width()); + + Nexus*nex = sig->pin(0).nexus(); + + for (Link*cur = nex->first_nlink(); cur ; cur = cur->next_nlink()) { + NetPins*obj; + unsigned obj_pin; + cur->cur_link(obj, obj_pin); + + // Look for NetPartSelect devices, where this signal is + // connected to pin 1 of a NetPartSelect::PV. + NetPartSelect*ps_obj = dynamic_cast (obj); + if (ps_obj == 0) + continue; + if (ps_obj->dir() != NetPartSelect::PV) + continue; + if (obj_pin != 1) + continue; + + ivl_assert(*ps_obj, ps_obj->base() < ps_map.size()); + ivl_assert(*ps_obj, ps_obj->base()+ps_obj->width() <= ps_map.size()); + ps_map[ps_obj->base()] = ps_obj; + } + + // Check the collected NetPartSelect::PV objects to see if + // they cover the vector. + unsigned idx = 0; + unsigned device_count = 0; + while (idx < ps_map.size()) { + NetPartSelect*ps_obj = ps_map[idx]; + if (ps_obj == 0) + return; + + idx += ps_obj->width(); + device_count += 1; + } + + ivl_assert(*sig, idx == ps_map.size()); + + // Ah HAH! The NetPartSelect::PV objects exactly cover the + // target signal. We can replace all of them with a single + // concatenation. + + if (debug_elaborate) { + cerr << sig->get_fileline() << ": debug: " + << "Collapse " << device_count + << " NetPartSelect::PV devices into a concatenation." << endl; + } + + NetConcat*cat = new NetConcat(scope, scope->local_symbol(), + ps_map.size(), device_count); + des->add_node(cat); + cat->set_line(*sig); + + connect(cat->pin(0), sig->pin(0)); + + idx = 0; + unsigned concat_position = 1; + while (idx < ps_map.size()) { + assert(ps_map[idx]); + NetPartSelect*ps_obj = ps_map[idx]; + connect(cat->pin(concat_position), ps_obj->pin(0)); + concat_position += 1; + idx += ps_obj->width(); + delete ps_obj; + } +} diff --git a/netmisc.h b/netmisc.h index 6f98d30d4..f056c933f 100644 --- a/netmisc.h +++ b/netmisc.h @@ -241,4 +241,6 @@ extern uint64_t get_scaled_time_from_real(Design*des, NetScope*scope, NetECReal*val); +extern void collapse_partselect_pv_to_concat(Design*des, NetNet*sig); + #endif From d83728031da774d895e5d5910bbcdce1fed5c3ee Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 3 Apr 2011 17:41:52 -0700 Subject: [PATCH 03/19] Over-zealous assertion checking PartSelect::PV widths. --- netmisc.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/netmisc.cc b/netmisc.cc index f44b9f7a5..42657c7bc 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -819,8 +819,11 @@ void collapse_partselect_pv_to_concat(Design*des, NetNet*sig) if (obj_pin != 1) continue; + // Don't support overrun selects here. + if (ps_obj->base()+ps_obj->width() > ps_map.size()) + continue; + ivl_assert(*ps_obj, ps_obj->base() < ps_map.size()); - ivl_assert(*ps_obj, ps_obj->base()+ps_obj->width() <= ps_map.size()); ps_map[ps_obj->base()] = ps_obj; } From 298495be97ea4dced7e84ec8cee2636a33dd53ac Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 3 Apr 2011 17:22:12 -0700 Subject: [PATCH 04/19] Add vpiBitVar support is $display and other vpi functions. --- vpi/sys_display.c | 1 + vvp/compile.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/vpi/sys_display.c b/vpi/sys_display.c index 18729c4f1..5a5efd19d 100644 --- a/vpi/sys_display.c +++ b/vpi/sys_display.c @@ -1048,6 +1048,7 @@ static int sys_check_args(vpiHandle callh, vpiHandle argv, const PLI_BYTE8*name, case vpiNet: case vpiReg: case vpiIntegerVar: + case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: diff --git a/vvp/compile.cc b/vvp/compile.cc index f742a50a5..5c2aec1f4 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -289,6 +289,7 @@ vvp_net_t* vvp_net_lookup(const char*label) switch (vpi->vpi_type->type_code) { case vpiNet: case vpiReg: + case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: From 240880d81b7ad30ffa14496c82e783b123284bbc Mon Sep 17 00:00:00 2001 From: Pawel Szostek Date: Wed, 6 Apr 2011 17:27:58 +0200 Subject: [PATCH 05/19] Change indentation mechanism in debug dump for VHDL There has been added additional default attribute to all 'dump' function calls which is in all cases equal to 0. Now one can specify how much this debug dumping should be intended. This should allow people to dump smoothly whole designs (as it was now) as far as separate units. This is now the parent who specifies the base indentation for all components (children). For example, architecture "decides" how much their signals should be indented. --- vhdlpp/architec.h | 8 +++---- vhdlpp/debug.cc | 56 ++++++++++++++++++++++----------------------- vhdlpp/entity.h | 4 ++-- vhdlpp/expression.h | 18 +++++++-------- vhdlpp/vsignal.h | 2 +- 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/vhdlpp/architec.h b/vhdlpp/architec.h index 176bf0f05..ef0624f97 100644 --- a/vhdlpp/architec.h +++ b/vhdlpp/architec.h @@ -48,7 +48,7 @@ class Architecture : public LineInfo { virtual ~Statement() =0; virtual int emit(ostream&out, Entity*ent, Architecture*arc); - virtual void dump(ostream&out) const; + virtual void dump(ostream&out, int indent = 0) const; private: @@ -75,7 +75,7 @@ class Architecture : public LineInfo { int emit(ostream&out, Entity*entity); // The dump method writes a debug display to the given output. - void dump(ostream&out, perm_string of_entity) const; + void dump(ostream&out, perm_string of_entity, int indent = 0) const; private: perm_string name_; @@ -100,7 +100,7 @@ class SignalAssignment : public Architecture::Statement { ~SignalAssignment(); int emit(ostream&out, Entity*entity, Architecture*arc); - virtual void dump(ostream&out) const; + virtual void dump(ostream&out, int indent = 0) const; private: ExpName*lval_; @@ -115,7 +115,7 @@ class ComponentInstantiation : public Architecture::Statement { ~ComponentInstantiation(); int emit(ostream&out, Entity*entity, Architecture*arc); - virtual void dump(ostream&out) const; + virtual void dump(ostream&out, int indent = 0) const; private: perm_string iname_; diff --git a/vhdlpp/debug.cc b/vhdlpp/debug.cc index 295144804..6cbaaa703 100644 --- a/vhdlpp/debug.cc +++ b/vhdlpp/debug.cc @@ -55,16 +55,16 @@ void dump_design_entities(const char*path) } } -void ComponentBase::dump_ports(ostream&out) const +void ComponentBase::dump_ports(ostream&out, int indent) const { if (ports_.size() == 0) { - out << " No ports" << endl; + out << setw(indent) << "" << "No ports" << endl; } else { - out << " PORTS:" << endl; + out << setw(indent) << "" << "PORTS:" << endl; for (vector::const_iterator cur = ports_.begin() ; cur != ports_.end() ; ++cur) { InterfacePort*item = *cur; - out << setw(6) << "" << item->name + out << setw(indent+2) << "" << item->name << " : " << item->mode << ", type="; if (item->type) @@ -76,74 +76,74 @@ void ComponentBase::dump_ports(ostream&out) const } } -void Entity::dump(ostream&out) const +void Entity::dump(ostream&out, int indent) const { - out << "entity " << get_name() + out << setw(indent) << "" << "entity " << get_name() << " file=" << get_fileline() << endl; - dump_ports(out); + dump_ports(out, indent+2); for (map::const_iterator cur = arch_.begin() ; cur != arch_.end() ; ++cur) { - cur->second->dump(out, get_name()); + cur->second->dump(out, get_name(), indent); } } -void Architecture::dump(ostream&out, perm_string of_entity) const +void Architecture::dump(ostream&out, perm_string of_entity, int indent) const { - out << "architecture " << name_ + out << setw(indent) << "" << "architecture " << name_ << " of entity " << of_entity << " file=" << get_fileline() << endl; // Dump signal declarations for (map::const_iterator cur = signals_.begin() ; cur != signals_.end() ; ++cur) { - cur->second->dump(out); + cur->second->dump(out, indent+3); } // Dump component declarations for (map::const_iterator cur = components_.begin() ; cur != components_.end() ; ++cur) { - out << " component " << cur->first << " is" << endl; - cur->second->dump_ports(out); - out << " end component " << cur->first << endl; + out << setw(indent+3) << "" << "component " << cur->first << " is" << endl; + cur->second->dump_ports(out, 3); + out << setw(indent+3) << "" << "end component " << cur->first << endl; } for (list::const_iterator cur = statements_.begin() ; cur != statements_.end() ; ++cur) { - (*cur)->dump(out); + (*cur)->dump(out, indent+3); } } -void Architecture::Statement::dump(ostream&out) const +void Architecture::Statement::dump(ostream&out, int indent) const { - out << " Architecture::Statement at file=" << get_fileline() << endl; + out << setw(indent) << "" << "Architecture::Statement at file=" << get_fileline() << endl; } -void Signal::dump(ostream&out) const +void Signal::dump(ostream&out, int indent) const { - out << " signal " << name_ << " is " << *type_ << endl; + out << setw(indent) << "" << "signal " << name_ << " is " << *type_ << endl; } -void SignalAssignment::dump(ostream&out) const +void SignalAssignment::dump(ostream&out, int indent) const { - out << " SignalAssignment file=" << get_fileline() << endl; - lval_->dump(out, 4); - out << " <= ..." << endl; + out << setw(indent) << "" << "SignalAssignment file=" << get_fileline() << endl; + lval_->dump(out, indent+1); + out << setw(indent+2) << "" << "<= ..." << endl; for (list::const_iterator cur = rval_.begin() ; cur != rval_.end() ; ++cur) { - (*cur)->dump(out, 5); + (*cur)->dump(out, indent+2); } } -void ComponentInstantiation::dump(ostream&out) const +void ComponentInstantiation::dump(ostream&out, int indent) const { - out << " Component Instantiation file=" << get_fileline() << endl; + out << setw(indent) << "" << "Component Instantiation file=" << get_fileline() << endl; for (map::const_iterator cur = port_map_.begin() ; cur != port_map_.end() ; ++cur) { - out << " " << cur->first << " => ..." << endl; - cur->second->dump(out, 10); + out << setw(indent+2) <<""<< cur->first << " => ..." << endl; + cur->second->dump(out, indent+6); } } diff --git a/vhdlpp/entity.h b/vhdlpp/entity.h index 691d6f008..1edcc3970 100644 --- a/vhdlpp/entity.h +++ b/vhdlpp/entity.h @@ -62,7 +62,7 @@ class ComponentBase : public LineInfo { void set_interface(std::list*ports); public: - void dump_ports(ostream&out) const; + void dump_ports(ostream&out, int indent = 0) const; protected: // This is really only used by the Entity derived class. @@ -93,7 +93,7 @@ class Entity : public ComponentBase { int elaborate(); int emit(ostream&out); - void dump(ostream&out) const; + void dump(ostream&out, int indent = 0) const; private: std::maparch_; diff --git a/vhdlpp/expression.h b/vhdlpp/expression.h index 009525589..cf5ae5fdd 100644 --- a/vhdlpp/expression.h +++ b/vhdlpp/expression.h @@ -56,7 +56,7 @@ class Expression : public LineInfo { virtual bool is_primary(void) const; // Debug dump of the expression. - virtual void dump(ostream&out, int indent) const =0; + virtual void dump(ostream&out, int indent = 0) const =0; private: @@ -73,7 +73,7 @@ class ExpUnary : public Expression { protected: int emit_operand1(ostream&out, Entity*ent, Architecture*arc); - void dump_operand1(ostream&out, int indent) const; + void dump_operand1(ostream&out, int indent = 0) const; private: Expression*operand1_; @@ -94,7 +94,7 @@ class ExpBinary : public Expression { int emit_operand1(ostream&out, Entity*ent, Architecture*arc); int emit_operand2(ostream&out, Entity*ent, Architecture*arc); - void dump_operands(ostream&out, int indent) const; + void dump_operands(ostream&out, int indent = 0) const; private: Expression*operand1_; @@ -111,7 +111,7 @@ class ExpArithmetic : public ExpBinary { ~ExpArithmetic(); int emit(ostream&out, Entity*ent, Architecture*arc); - void dump(ostream&out, int indent) const; + void dump(ostream&out, int indent = 0) const; private: fun_t fun_; @@ -126,7 +126,7 @@ class ExpInteger : public Expression { int emit(ostream&out, Entity*ent, Architecture*arc); bool is_primary(void) const; bool evaluate(int64_t&val) const; - void dump(ostream&out, int indent) const; + void dump(ostream&out, int indent = 0) const; private: int64_t value_; @@ -142,7 +142,7 @@ class ExpLogical : public ExpBinary { ~ExpLogical(); int emit(ostream&out, Entity*ent, Architecture*arc); - void dump(ostream&out, int indent) const; + void dump(ostream&out, int indent = 0) const; private: fun_t fun_; @@ -162,7 +162,7 @@ class ExpName : public Expression { public: // Base methods int emit(ostream&out, Entity*ent, Architecture*arc); bool is_primary(void) const; - void dump(ostream&out, int indent) const; + void dump(ostream&out, int indent = 0) const; const char* name() const; private: @@ -177,7 +177,7 @@ class ExpUAbs : public ExpUnary { ~ExpUAbs(); int emit(ostream&out, Entity*ent, Architecture*arc); - void dump(ostream&out, int indent) const; + void dump(ostream&out, int indent = 0) const; }; class ExpUNot : public ExpUnary { @@ -187,7 +187,7 @@ class ExpUNot : public ExpUnary { ~ExpUNot(); int emit(ostream&out, Entity*ent, Architecture*arc); - void dump(ostream&out, int indent) const; + void dump(ostream&out, int indent = 0) const; }; #endif diff --git a/vhdlpp/vsignal.h b/vhdlpp/vsignal.h index 32109e500..0a604db6f 100644 --- a/vhdlpp/vsignal.h +++ b/vhdlpp/vsignal.h @@ -34,7 +34,7 @@ class Signal : public LineInfo { int emit(ostream&out, Entity*ent, Architecture*arc); - void dump(ostream&out) const; + void dump(ostream&out, int indent = 0) const; private: perm_string name_; From 93067149f1864f80f9a5ebf4565c4ea84497dcda Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sun, 27 Mar 2011 11:08:33 +0100 Subject: [PATCH 06/19] Rework of constant expression error reporting. This patch changes the method used to signal that a constant expression is being elaborated from flags stored in global variables to flags passed down the call chain. It also generates more informative error messages when variable references are found in a constant expression. --- PExpr.h | 63 +++++++----- elab_expr.cc | 265 +++++++++++++++++++++++++++++--------------------- elab_net.cc | 8 +- elab_scope.cc | 32 ++---- elab_sig.cc | 50 +++------- elaborate.cc | 19 ++-- eval_tree.cc | 7 -- net_design.cc | 18 +--- netmisc.cc | 17 +++- netmisc.h | 22 ++--- 10 files changed, 252 insertions(+), 249 deletions(-) diff --git a/PExpr.h b/PExpr.h index 337305e6b..774ed62a5 100644 --- a/PExpr.h +++ b/PExpr.h @@ -45,6 +45,11 @@ class PExpr : public LineInfo { public: enum width_mode_t { SIZED, EXPAND, LOSSLESS, UNSIZED }; + // Flag values that can be passed to elaborate_expr. + static const unsigned NO_FLAGS = 0x0; + static const unsigned NEED_CONST = 0x1; + static const unsigned SYS_TASK_ARG = 0x2; + PExpr(); virtual ~PExpr(); @@ -122,7 +127,7 @@ class PExpr : public LineInfo { // be incomplete. virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; // This method elaborates the expression as gates, but // restricted for use as l-values of continuous assignments. @@ -195,7 +200,7 @@ class PEConcat : public PExpr { virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_force) const; @@ -261,7 +266,7 @@ class PEFNumber : public PExpr { width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; virtual void dump(ostream&) const; @@ -301,7 +306,7 @@ class PEIdent : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; // Elaborate the PEIdent as a port to a module. This method // only applies to Ident expressions. @@ -330,7 +335,7 @@ class PEIdent : public PExpr { // invalid bits (xz) in either expression, then the defined // flag is set to *false*. bool calculate_parts_(Design*, NetScope*, long&msb, long&lsb, bool&defined) const; - NetExpr* calculate_up_do_base_(Design*, NetScope*) const; + NetExpr* calculate_up_do_base_(Design*, NetScope*, bool need_const) const; bool calculate_param_range_(Design*, NetScope*, const NetExpr*msb_ex, long&msb, const NetExpr*lsb_ex, long&lsb, @@ -352,7 +357,8 @@ class PEIdent : public PExpr { NetScope*found, const NetExpr*par_msb, const NetExpr*par_lsb, - unsigned expr_wid) const; + unsigned expr_wid, + unsigned flags) const; NetExpr*elaborate_expr_param_part_(Design*des, NetScope*scope, const NetExpr*par, @@ -365,25 +371,27 @@ class PEIdent : public PExpr { const NetExpr*par, NetScope*found, const NetExpr*par_msb, - const NetExpr*par_lsb) const; + const NetExpr*par_lsb, + bool need_const) const; NetExpr*elaborate_expr_param_idx_do_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found, const NetExpr*par_msb, - const NetExpr*par_lsb) const; + const NetExpr*par_lsb, + bool need_const) const; NetExpr*elaborate_expr_net(Design*des, NetScope*scope, NetNet*net, NetScope*found, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; NetExpr*elaborate_expr_net_word_(Design*des, NetScope*scope, NetNet*net, NetScope*found, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; NetExpr*elaborate_expr_net_part_(Design*des, NetScope*scope, NetESignal*net, @@ -392,15 +400,18 @@ class PEIdent : public PExpr { NetExpr*elaborate_expr_net_idx_up_(Design*des, NetScope*scope, NetESignal*net, - NetScope*found) const; + NetScope*found, + bool need_const) const; NetExpr*elaborate_expr_net_idx_do_(Design*des, NetScope*scope, NetESignal*net, - NetScope*found) const; + NetScope*found, + bool need_const) const; NetExpr*elaborate_expr_net_bit_(Design*des, NetScope*scope, NetESignal*net, - NetScope*found) const; + NetScope*found, + bool need_const) const; private: NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, @@ -423,7 +434,7 @@ class PENumber : public PExpr { width_mode_t&mode); virtual NetEConst*elaborate_expr(Design*des, NetScope*, - unsigned expr_wid, bool) const; + unsigned expr_wid, unsigned) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_force) const; @@ -456,7 +467,7 @@ class PEString : public PExpr { width_mode_t&mode); virtual NetEConst*elaborate_expr(Design*des, NetScope*, - unsigned expr_wid, bool) const; + unsigned expr_wid, unsigned) const; verinum* eval_const(Design*, NetScope*) const; private: @@ -480,7 +491,7 @@ class PEUnary : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; private: @@ -508,7 +519,7 @@ class PEBinary : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; protected: @@ -546,7 +557,7 @@ class PEBComp : public PEBinary { width_mode_t&mode); NetExpr* elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool sys_task_arg) const; + unsigned expr_wid, unsigned flags) const; private: unsigned l_width_; @@ -566,7 +577,7 @@ class PEBLogic : public PEBinary { width_mode_t&mode); NetExpr* elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool sys_task_arg) const; + unsigned expr_wid, unsigned flags) const; }; /* @@ -589,7 +600,7 @@ class PEBLeftWidth : public PEBinary { virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; }; class PEBPower : public PEBLeftWidth { @@ -633,12 +644,13 @@ class PETernary : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; private: NetExpr* elab_and_eval_alternative_(Design*des, NetScope*scope, - PExpr*expr, unsigned expr_wid) const; + PExpr*expr, unsigned expr_wid, + unsigned flags) const; private: PExpr*expr_; @@ -672,7 +684,7 @@ class PECallFunction : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); @@ -686,7 +698,8 @@ class PECallFunction : public PExpr { NetExpr* cast_to_width_(NetExpr*expr, unsigned wid) const; NetExpr* elaborate_sfunc_(Design*des, NetScope*scope, - unsigned expr_wid) const; + unsigned expr_wid, + unsigned flags) const; NetExpr* elaborate_access_func_(Design*des, NetScope*scope, ivl_nature_t, unsigned expr_wid) const; unsigned test_width_sfunc_(Design*des, NetScope*scope, @@ -705,7 +718,7 @@ class PEVoid : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, - bool sys_task_arg) const; + unsigned flags) const; }; #endif diff --git a/elab_expr.cc b/elab_expr.cc index 24a61469b..7b8a76594 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -82,7 +82,7 @@ static NetBranch* find_existing_implicit_branch(NetNet*sig, NetNet*gnd) NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_variable_type_t lv_type, unsigned lv_width, - PExpr*expr) + PExpr*expr, bool need_const) { int context_wid = -1; switch (lv_type) { @@ -99,7 +99,7 @@ NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, break; } - return elab_and_eval(des, scope, expr, context_wid); + return elab_and_eval(des, scope, expr, context_wid, need_const); } /* @@ -127,7 +127,7 @@ unsigned PExpr::test_width(Design*des, NetScope*, width_mode_t&) return 1; } -NetExpr* PExpr::elaborate_expr(Design*des, NetScope*, unsigned, bool) const +NetExpr* PExpr::elaborate_expr(Design*des, NetScope*, unsigned, unsigned) const { cerr << get_fileline() << ": internal error: I do not know how to" << " elaborate this expression. " << endl; @@ -224,8 +224,10 @@ unsigned PEBinary::test_width(Design*des, NetScope*scope, width_mode_t&mode) * types. */ NetExpr* PEBinary::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + ivl_assert(*this, left_); ivl_assert(*this, right_); @@ -249,8 +251,8 @@ NetExpr* PEBinary::elaborate_expr(Design*des, NetScope*scope, left_->cast_signed(signed_flag_); } - NetExpr*lp = left_->elaborate_expr(des, scope, l_width, false); - NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); + NetExpr*lp = left_->elaborate_expr(des, scope, l_width, flags); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, flags); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; @@ -505,8 +507,10 @@ unsigned PEBComp::test_width(Design*des, NetScope*scope, width_mode_t&) } NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + ivl_assert(*this, left_); ivl_assert(*this, right_); @@ -517,8 +521,8 @@ NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, if (type_is_vectorable(right_->expr_type()) && !right_->has_sign()) left_->cast_signed(false); - NetExpr*lp = left_->elaborate_expr(des, scope, l_width_, false); - NetExpr*rp = right_->elaborate_expr(des, scope, r_width_, false); + NetExpr*lp = left_->elaborate_expr(des, scope, l_width_, flags); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width_, flags); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; @@ -569,13 +573,14 @@ unsigned PEBLogic::test_width(Design*, NetScope*, width_mode_t&) } NetExpr*PEBLogic::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { ivl_assert(*this, left_); ivl_assert(*this, right_); - NetExpr*lp = elab_and_eval(des, scope, left_, -1); - NetExpr*rp = elab_and_eval(des, scope, right_, -1); + bool need_const = NEED_CONST & flags; + NetExpr*lp = elab_and_eval(des, scope, left_, -1, need_const); + NetExpr*rp = elab_and_eval(des, scope, right_, -1, need_const); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; @@ -626,7 +631,7 @@ unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode) // If the right operand is constant, we can use the // actual value. - NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, NO_FLAGS); if (rp) { eval_expr(rp, r_width); } else { @@ -707,8 +712,10 @@ unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode) } NetExpr*PEBLeftWidth::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + ivl_assert(*this, left_); // The left operand is always context determined, so propagate @@ -717,8 +724,8 @@ NetExpr*PEBLeftWidth::elaborate_expr(Design*des, NetScope*scope, unsigned r_width = right_->expr_width(); - NetExpr*lp = left_->elaborate_expr(des, scope, expr_wid, false); - NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); + NetExpr*lp = left_->elaborate_expr(des, scope, expr_wid, flags); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, flags); if (lp == 0 || rp == 0) { delete lp; delete rp; @@ -1074,7 +1081,8 @@ NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, unsigned wid) const * known function names. */ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, - unsigned expr_wid) const + unsigned expr_wid, + unsigned flags) const { perm_string name = peek_tail_name(path_); @@ -1090,7 +1098,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, } PExpr*expr = parms_[0]; - NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, true); + NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, flags); return cast_to_width_(sub, expr_wid); } @@ -1162,11 +1170,14 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, expression as much as possible, and use the reduced expression if one is created. */ + bool need_const = NEED_CONST & flags; + unsigned missing_parms = 0; for (unsigned idx = 0 ; idx < nparms ; idx += 1) { PExpr*expr = parms_[idx]; if (expr) { - NetExpr*tmp = elab_sys_task_arg(des, scope, name, idx, expr); + NetExpr*tmp = elab_sys_task_arg(des, scope, name, idx, + expr, need_const); fun->parm(idx, tmp); } else { @@ -1250,10 +1261,12 @@ NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope, } NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + if (peek_tail_name(path_)[0] == '$') - return elaborate_sfunc_(des, scope, expr_wid); + return elaborate_sfunc_(des, scope, expr_wid, flags); NetFuncDef*def = des->find_function(scope, path_); if (def == 0) { @@ -1268,7 +1281,7 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, // We do not currently support constant user function and // this is where things fail because of that, though we // don't know for sure so we need to display both messages. - if (need_constant_expr || is_param_expr) { + if (NEED_CONST & flags) { cerr << get_fileline() << ": sorry: constant user " "functions are not currently supported: " << path_ << "()." << endl << " or" << endl; @@ -1297,6 +1310,8 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, of the function being called. The scope of the called function is elaborated when the definition is elaborated. */ + bool need_const = NEED_CONST & flags; + unsigned missing_parms = 0; for (unsigned idx = 0 ; idx < parms.count() ; idx += 1) { PExpr*tmp = parms_[idx]; @@ -1304,7 +1319,7 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, parms[idx] = elaborate_rval_expr(des, scope, def->port(idx)->data_type(), (unsigned)def->port(idx)->vector_width(), - tmp); + tmp, need_const); if (NetEEvent*evt = dynamic_cast (parms[idx])) { cerr << evt->get_fileline() << ": error: An event '" << evt->event()->name() << "' can not be a user " @@ -1372,9 +1387,7 @@ unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&) /* If there is a repeat expression, then evaluate the constant value and set the repeat count. */ if (repeat_ && (scope != tested_scope_)) { - need_constant_expr = true; - NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1); - need_constant_expr = false; + NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1, true); if (tmp == 0) return 0; if (tmp->expr_type() == IVL_VT_REAL) { @@ -1426,8 +1439,10 @@ unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&) static int concat_depth = 0; NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + concat_depth += 1; if (debug_elaborate) { @@ -1459,7 +1474,7 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, assert(parms_[idx]); unsigned wid = parms_[idx]->expr_width(); - NetExpr*ex = parms_[idx]->elaborate_expr(des, scope, wid, false); + NetExpr*ex = parms_[idx]->elaborate_expr(des, scope, wid, flags); if (ex == 0) continue; ex->set_line(*parms_[idx]); @@ -1542,7 +1557,7 @@ unsigned PEFNumber::test_width(Design*, NetScope*, width_mode_t&) return expr_width_; } -NetExpr* PEFNumber::elaborate_expr(Design*, NetScope*, unsigned, bool) const +NetExpr* PEFNumber::elaborate_expr(Design*, NetScope*, unsigned, unsigned) const { NetECReal*tmp = new NetECReal(*value_); tmp->set_line(*this); @@ -1569,9 +1584,7 @@ bool PEIdent::calculate_parts_(Design*des, NetScope*scope, two bit select expressions, and both must be constant. Evaluate them and pass the results back to the caller. */ - need_constant_expr = true; - NetExpr*lsb_ex = elab_and_eval(des, scope, index_tail.lsb, -1); - need_constant_expr = false; + NetExpr*lsb_ex = elab_and_eval(des, scope, index_tail.lsb, -1, true); NetEConst*lsb_c = dynamic_cast(lsb_ex); if (lsb_c == 0) { cerr << index_tail.lsb->get_fileline() << ": error: " @@ -1589,9 +1602,7 @@ bool PEIdent::calculate_parts_(Design*des, NetScope*scope, lsb = lsb_c->value().as_long(); } - need_constant_expr = true; - NetExpr*msb_ex = elab_and_eval(des, scope, index_tail.msb, -1); - need_constant_expr = false; + NetExpr*msb_ex = elab_and_eval(des, scope, index_tail.msb, -1, true); NetEConst*msb_c = dynamic_cast(msb_ex); if (msb_c == 0) { cerr << index_tail.msb->get_fileline() << ": error: " @@ -1628,9 +1639,7 @@ bool PEIdent::calculate_up_do_width_(Design*des, NetScope*scope, /* Calculate the width expression (in the lsb_ position) first. If the expression is not constant, error but guess 1 so we can keep going and find more errors. */ - need_constant_expr = true; - NetExpr*wid_ex = elab_and_eval(des, scope, index_tail.lsb, -1); - need_constant_expr = false; + NetExpr*wid_ex = elab_and_eval(des, scope, index_tail.lsb, -1, true); NetEConst*wid_c = dynamic_cast(wid_ex); if (wid_c == 0) { @@ -1651,7 +1660,8 @@ bool PEIdent::calculate_up_do_width_(Design*des, NetScope*scope, * When we know that this is an indexed part select (up or down) this * method calculates the up/down base, as far at it can be calculated. */ -NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope) const +NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope, + bool need_const) const { const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); @@ -1660,7 +1670,7 @@ NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope) const ivl_assert(*this, index_tail.lsb != 0); ivl_assert(*this, index_tail.msb != 0); - NetExpr*tmp = elab_and_eval(des, scope, index_tail.msb, -1); + NetExpr*tmp = elab_and_eval(des, scope, index_tail.msb, -1, need_const); return tmp; } @@ -1831,7 +1841,7 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) * The signal name may be escaped, but that affects nothing here. */ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool sys_task_arg) const + unsigned expr_wid, unsigned flags) const { assert(scope); @@ -1841,10 +1851,10 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, const NetExpr*ex1, *ex2; - if (is_param_expr && path_.size() > 1) { - cerr << get_fileline() << ": error: parameter r-value expression " - "does not support hierarchical references `" << path_ - << "`." << endl; + if ((NEED_CONST & flags) && (path_.size() > 1)) { + cerr << get_fileline() << ": error: A hierarchical reference ('" + << path_ << "') is not allowed in a constant expression." + << endl; des->errors += 1; return 0; } @@ -1857,7 +1867,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, // the parameter value. if (par != 0) { NetExpr*tmp = elaborate_expr_param_(des, scope, par, found_in, - ex1, ex2, expr_wid); + ex1, ex2, expr_wid, flags); if (!tmp) return 0; @@ -1867,20 +1877,19 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, return tmp; } - // If this is a parameter expression, no other identifiers are valid. - if (is_param_expr) { - cerr << get_fileline() << ": error: identifier `" - << path_ << "` is not a parameter in " - << scope_path(scope) << "." << endl; - des->errors += 1; - return 0; - } - // If the identifier names a signal (a register or wire) // then create a NetESignal node to handle it. if (net != 0) { + if (NEED_CONST & flags) { + cerr << get_fileline() << ": error: A reference to a wire " + "or register ('" << path_ << "') is not allowed in " + "a constant expression." << endl; + des->errors += 1; + return 0; + } + NetExpr*tmp = elaborate_expr_net(des, scope, net, found_in, - expr_wid, sys_task_arg); + expr_wid, flags); if (!tmp) return 0; @@ -1890,9 +1899,17 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, return tmp; } - // If the identifier is a named event. - // is a variable reference. + // If the identifier is a named event + // then create a NetEEvent node to handle it. if (eve != 0) { + if (NEED_CONST & flags) { + cerr << get_fileline() << ": error: A reference to a named " + "event ('" << path_ << "') is not allowed in a " + "constant expression." << endl; + des->errors += 1; + return 0; + } + NetEEvent*tmp = new NetEEvent(eve); tmp->set_line(*this); return tmp; @@ -1987,7 +2004,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, } NetExpr*expr = elaborate_expr_net(des, scope, net, found_in, - expr_wid, false); + expr_wid, NO_FLAGS); NetESFunc*sys_expr = 0; if (method_name == "name") { @@ -2007,7 +2024,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, << " attached to " << use_path << "." << endl; des->errors += 1; return elaborate_expr_net(des, scope, net, found_in, - expr_wid, false); + expr_wid, NO_FLAGS); } sys_expr->set_line(*this); @@ -2023,10 +2040,12 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, // are not scopes. If this is not a system task argument, then // it cannot be a scope name, so give up. - if (! sys_task_arg) { + if ( !(SYS_TASK_ARG & flags) ) { // I cannot interpret this identifier. Error message. - cerr << get_fileline() << ": error: Unable to bind wire/reg/memory " - "`" << path_ << "' in `" << scope_path(scope) << "'"<< endl; + cerr << get_fileline() << ": error: Unable to bind " + << (NEED_CONST & flags ? "parameter" : "wire/reg/memory") + << " `" << path_ << "' in `" << scope_path(scope) << "'" + << endl; des->errors += 1; return 0; } @@ -2063,7 +2082,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, << nsc->basename() << " path=" << path_ << endl; - if (! sys_task_arg) { + if ( !(SYS_TASK_ARG & flags) ) { cerr << get_fileline() << ": error: Scope name " << nsc->basename() << " not allowed here." << endl; des->errors += 1; @@ -2248,7 +2267,8 @@ NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, const NetExpr*par, NetScope*, const NetExpr*par_msb, - const NetExpr*par_lsb) const + const NetExpr*par_lsb, + bool need_const) const { const NetEConst*par_ex = dynamic_cast (par); ivl_assert(*this, par_ex); @@ -2258,7 +2278,7 @@ NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, par_lsb, par_lsv, par_ex->value().len())) return 0; - NetExpr*base = calculate_up_do_base_(des, scope); + NetExpr*base = calculate_up_do_base_(des, scope, need_const); if (base == 0) return 0; unsigned long wid = 0; @@ -2327,7 +2347,8 @@ NetExpr* PEIdent::elaborate_expr_param_idx_do_(Design*des, NetScope*scope, const NetExpr*par, NetScope*, const NetExpr*par_msb, - const NetExpr*par_lsb) const + const NetExpr*par_lsb, + bool need_const) const { const NetEConst*par_ex = dynamic_cast (par); ivl_assert(*this, par_ex); @@ -2337,7 +2358,7 @@ NetExpr* PEIdent::elaborate_expr_param_idx_do_(Design*des, NetScope*scope, par_lsb, par_lsv, par_ex->value().len())) return 0; - NetExpr*base = calculate_up_do_base_(des, scope); + NetExpr*base = calculate_up_do_base_(des, scope, need_const); if (base == 0) return 0; unsigned long wid = 0; @@ -2414,8 +2435,10 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, - unsigned expr_wid) const + unsigned expr_wid, unsigned flags) const { + bool need_const = NEED_CONST & flags; + const name_component_t&name_tail = path_.back(); index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; if (!name_tail.index.empty()) @@ -2440,11 +2463,11 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, if (use_sel == index_component_t::SEL_IDX_UP) return elaborate_expr_param_idx_up_(des, scope, par, found_in, - par_msb, par_lsb); + par_msb, par_lsb, need_const); if (use_sel == index_component_t::SEL_IDX_DO) return elaborate_expr_param_idx_do_(des, scope, par, found_in, - par_msb, par_lsb); + par_msb, par_lsb, need_const); // NOTE TO SELF (continued): The code below should be // rewritten in the above format, as I get to it. @@ -2469,7 +2492,8 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, /* Handle the case where a parameter has a bit select attached to it. Generate a NetESelect object to select the bit as desired. */ - NetExpr*mtmp = elab_and_eval(des, scope, index_tail.msb, -1); + NetExpr*mtmp = elab_and_eval(des, scope, index_tail.msb, -1, + need_const); /* Let's first try to get constant values for both the parameter and the bit select. If they are @@ -2622,11 +2646,13 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, NetNet*net, NetScope*found_in, unsigned expr_wid, - bool sys_task_arg) const + unsigned flags) const { + bool need_const = NEED_CONST & flags; + const name_component_t&name_tail = path_.back(); - if (name_tail.index.empty() && !sys_task_arg) { + if (name_tail.index.empty() && !(SYS_TASK_ARG & flags)) { cerr << get_fileline() << ": error: Array " << path() << " Needs an array index here." << endl; des->errors += 1; @@ -2647,10 +2673,12 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, ivl_assert(*this, !index_front.lsb); } - NetExpr*word_index = index_front.sel == index_component_t::SEL_NONE - ? 0 - : elab_and_eval(des, scope, index_front.msb, -1); - if (word_index == 0 && !sys_task_arg) + NetExpr*word_index = 0; + if (index_front.sel != index_component_t::SEL_NONE) + word_index = elab_and_eval(des, scope, index_front.msb, -1, + need_const); + + if (word_index == 0 && !(SYS_TASK_ARG & flags)) return 0; if (NetEConst*word_addr = dynamic_cast(word_index)) { @@ -2711,16 +2739,20 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, } if (word_sel == index_component_t::SEL_PART) - return elaborate_expr_net_part_(des, scope, res, found_in, expr_wid); + return elaborate_expr_net_part_(des, scope, res, found_in, + expr_wid); if (word_sel == index_component_t::SEL_IDX_UP) - return elaborate_expr_net_idx_up_(des, scope, res, found_in); + return elaborate_expr_net_idx_up_(des, scope, res, found_in, + need_const); if (word_sel == index_component_t::SEL_IDX_DO) - return elaborate_expr_net_idx_do_(des, scope, res, found_in); + return elaborate_expr_net_idx_do_(des, scope, res, found_in, + need_const); if (word_sel == index_component_t::SEL_BIT) - return elaborate_expr_net_bit_(des, scope, res, found_in); + return elaborate_expr_net_bit_(des, scope, res, found_in, + need_const); ivl_assert(*this, word_sel == index_component_t::SEL_NONE); @@ -2837,9 +2869,10 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, * Part select indexed up, i.e. net[ +: ] */ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, - NetESignal*net, NetScope*) const + NetESignal*net, NetScope*, + bool need_const) const { - NetExpr*base = calculate_up_do_base_(des, scope); + NetExpr*base = calculate_up_do_base_(des, scope, need_const); unsigned long wid = 0; calculate_up_do_width_(des, scope, wid); @@ -2925,9 +2958,10 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, * Part select indexed down, i.e. net[ -: ] */ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, - NetESignal*net, NetScope*)const + NetESignal*net, NetScope*, + bool need_const) const { - NetExpr*base = calculate_up_do_base_(des, scope); + NetExpr*base = calculate_up_do_base_(des, scope, need_const); unsigned long wid = 0; calculate_up_do_width_(des, scope, wid); @@ -3009,7 +3043,8 @@ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, } NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, - NetESignal*net, NetScope*) const + NetESignal*net, NetScope*, + bool need_const) const { const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); @@ -3018,7 +3053,7 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, ivl_assert(*this, index_tail.msb != 0); ivl_assert(*this, index_tail.lsb == 0); - NetExpr*ex = elab_and_eval(des, scope, index_tail.msb, -1); + NetExpr*ex = elab_and_eval(des, scope, index_tail.msb, -1, need_const); // If the bit select is constant, then treat it similar // to the part select, so that I save the effort of @@ -3111,11 +3146,13 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, NetNet*net, NetScope*found_in, unsigned expr_wid, - bool sys_task_arg) const + unsigned flags) const { if (net->array_dimensions() > 0) return elaborate_expr_net_word_(des, scope, net, found_in, - expr_wid, sys_task_arg); + expr_wid, flags); + + bool need_const = NEED_CONST & flags; NetESignal*node = new NetESignal(net); node->set_line(*this); @@ -3143,13 +3180,16 @@ NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, expr_wid); if (use_sel == index_component_t::SEL_IDX_UP) - return elaborate_expr_net_idx_up_(des, scope, node, found_in); + return elaborate_expr_net_idx_up_(des, scope, node, found_in, + need_const); if (use_sel == index_component_t::SEL_IDX_DO) - return elaborate_expr_net_idx_do_(des, scope, node, found_in); + return elaborate_expr_net_idx_do_(des, scope, node, found_in, + need_const); if (use_sel == index_component_t::SEL_BIT) - return elaborate_expr_net_bit_(des, scope, node, found_in); + return elaborate_expr_net_bit_(des, scope, node, found_in, + need_const); // It's not anything else, so this must be a simple identifier // expression with no part or bit select. Return the signal @@ -3173,7 +3213,7 @@ unsigned PENumber::test_width(Design*, NetScope*, width_mode_t&mode) } NetEConst* PENumber::elaborate_expr(Design*, NetScope*, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned) const { assert(value_); verinum val = *value_; @@ -3198,7 +3238,7 @@ unsigned PEString::test_width(Design*, NetScope*, width_mode_t&) } NetEConst* PEString::elaborate_expr(Design*, NetScope*, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned) const { verinum val(value()); val = pad_to_width(val, expr_wid); @@ -3295,15 +3335,17 @@ bool NetETernary::test_operand_compat(ivl_variable_type_t l, * methods. If any elaboration fails, then give up and return 0. */ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { - assert(expr_); - assert(tru_); - assert(fal_); + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + + ivl_assert(*this, expr_); + ivl_assert(*this, tru_); + ivl_assert(*this, fal_); // Elaborate and evaluate the condition expression. Note that // it is always self-determined. - NetExpr*con = elab_and_eval(des, scope, expr_, -1); + NetExpr*con = elab_and_eval(des, scope, expr_, -1, NEED_CONST & flags); if (con == 0) return 0; @@ -3326,7 +3368,8 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, "elaborate TRUE clause of ternary." << endl; - return elab_and_eval_alternative_(des, scope, tru_, expr_wid); + return elab_and_eval_alternative_(des, scope, tru_, + expr_wid, flags); } // Condition is constant FALSE, so we only need the @@ -3337,20 +3380,21 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, "elaborate FALSE clause of ternary." << endl; - return elab_and_eval_alternative_(des, scope, fal_, expr_wid); + return elab_and_eval_alternative_(des, scope, fal_, + expr_wid, flags); } // X and Z conditions need to blend both results, so we // can't short-circuit. } - NetExpr*tru = elab_and_eval_alternative_(des, scope, tru_, expr_wid); + NetExpr*tru = elab_and_eval_alternative_(des, scope, tru_, expr_wid, flags); if (tru == 0) { delete con; return 0; } - NetExpr*fal = elab_and_eval_alternative_(des, scope, fal_, expr_wid); + NetExpr*fal = elab_and_eval_alternative_(des, scope, fal_, expr_wid, flags); if (fal == 0) { delete con; delete tru; @@ -3378,7 +3422,8 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, * self-determined. */ NetExpr* PETernary::elab_and_eval_alternative_(Design*des, NetScope*scope, - PExpr*expr, unsigned expr_wid) const + PExpr*expr, unsigned expr_wid, + unsigned flags) const { int context_wid = expr_wid; if (type_is_vectorable(expr->expr_type()) && !type_is_vectorable(expr_type_)) { @@ -3387,7 +3432,7 @@ NetExpr* PETernary::elab_and_eval_alternative_(Design*des, NetScope*scope, } else { expr->cast_signed(signed_flag_); } - NetExpr*tmp = expr->elaborate_expr(des, scope, expr_wid, false); + NetExpr*tmp = expr->elaborate_expr(des, scope, expr_wid, flags); if (tmp == 0) return 0; eval_expr(tmp, context_wid); @@ -3436,8 +3481,10 @@ unsigned PEUnary::test_width(Design*des, NetScope*scope, width_mode_t&mode) NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, - unsigned expr_wid, bool) const + unsigned expr_wid, unsigned flags) const { + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + unsigned sub_width = expr_wid; switch (op_) { // Reduction operators and ! always have a self determined width. @@ -3457,7 +3504,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, expr_->cast_signed(signed_flag_); break; } - NetExpr*ip = expr_->elaborate_expr(des, scope, sub_width, false); + NetExpr*ip = expr_->elaborate_expr(des, scope, sub_width, flags); if (ip == 0) return 0; ivl_assert(*expr_, expr_type_ != IVL_VT_NO_TYPE); @@ -3604,7 +3651,7 @@ NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, unsigned expr_wid) const return tmp; } -NetExpr* PEVoid::elaborate_expr(Design*, NetScope*, unsigned, bool) const +NetExpr* PEVoid::elaborate_expr(Design*, NetScope*, unsigned, unsigned) const { return 0; } diff --git a/elab_net.cc b/elab_net.cc index 9ff968c39..2a7d75400 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -222,9 +222,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, case index_component_t::SEL_IDX_DO: case index_component_t::SEL_IDX_UP: { - need_constant_expr = true; - NetExpr*tmp_ex = elab_and_eval(des, scope, index_tail.msb, -1); - need_constant_expr = false; + NetExpr*tmp_ex = elab_and_eval(des, scope, index_tail.msb, -1, true); NetEConst*tmp = dynamic_cast(tmp_ex); if (!tmp) { cerr << get_fileline() << ": error: indexed part select of " @@ -480,9 +478,7 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, } ivl_assert(*this, index_head.sel == index_component_t::SEL_BIT); - need_constant_expr = true; - NetExpr*tmp_ex = elab_and_eval(des, scope, index_head.msb, -1); - need_constant_expr = false; + NetExpr*tmp_ex = elab_and_eval(des, scope, index_head.msb, -1, true); NetEConst*tmp = dynamic_cast(tmp_ex); if (!tmp) { cerr << get_fileline() << ": error: array " << sig->name() diff --git a/elab_scope.cc b/elab_scope.cc index 4bf184661..76f38cacb 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -554,9 +554,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) // The initial value for the genvar does not need (nor can it // use) the genvar itself, so we can evaluate this expression // the same way any other parameter value is evaluated. - need_constant_expr = true; - NetExpr*init_ex = elab_and_eval(des, container, loop_init, -1); - need_constant_expr = false; + NetExpr*init_ex = elab_and_eval(des, container, loop_init, -1, true); NetEConst*init = dynamic_cast (init_ex); if (init == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar" @@ -620,9 +618,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) cerr << get_fileline() << ": debug: genvar init = " << genvar << endl; container->genvar_tmp = loop_index; container->genvar_tmp_val = genvar; - need_constant_expr = true; - NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1); - need_constant_expr = false; + NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1, true); NetEConst*test = dynamic_cast(test_ex); if (test == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar" @@ -668,9 +664,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) elaborate_subscope_(des, scope); // Calculate the step for the loop variable. - need_constant_expr = true; - NetExpr*step_ex = elab_and_eval(des, container, loop_step, -1); - need_constant_expr = false; + NetExpr*step_ex = elab_and_eval(des, container, loop_step, -1, true); NetEConst*step = dynamic_cast(step_ex); if (step == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar" @@ -700,9 +694,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else_flag) { - need_constant_expr = true; - NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1); - need_constant_expr = false; + NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1, true); NetEConst*test = dynamic_cast (test_ex); if (test == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar" @@ -792,9 +784,7 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else bool PGenerate::generate_scope_case_(Design*des, NetScope*container) { - need_constant_expr = true; - NetExpr*case_value_ex = elab_and_eval(des, container, loop_test, -1); - need_constant_expr = false; + NetExpr*case_value_ex = elab_and_eval(des, container, loop_test, -1, true); NetEConst*case_value_co = dynamic_cast(case_value_ex); if (case_value_co == 0) { cerr << get_fileline() << ": error: Cannot evaluate genvar case" @@ -824,9 +814,9 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container) bool match_flag = false; for (unsigned idx = 0 ; idx < item->item_test.size() && !match_flag ; idx +=1 ) { - need_constant_expr = true; - NetExpr*item_value_ex = elab_and_eval(des, container, item->item_test[idx], -1); - need_constant_expr = false; + NetExpr*item_value_ex = elab_and_eval(des, container, + item->item_test[idx], + -1, true); NetEConst*item_value_co = dynamic_cast(item_value_ex); if (item_value_co == 0) { cerr << get_fileline() << ": error: Cannot evaluate " @@ -1196,10 +1186,8 @@ void PGModule::elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const */ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*sc) const { - need_constant_expr = true; - NetExpr*mse = msb_ ? elab_and_eval(des, sc, msb_, -1) : 0; - NetExpr*lse = lsb_ ? elab_and_eval(des, sc, lsb_, -1) : 0; - need_constant_expr = false; + NetExpr*mse = msb_ ? elab_and_eval(des, sc, msb_, -1, true) : 0; + NetExpr*lse = lsb_ ? elab_and_eval(des, sc, lsb_, -1, true) : 0; NetEConst*msb = dynamic_cast (mse); NetEConst*lsb = dynamic_cast (lse); diff --git a/elab_sig.cc b/elab_sig.cc index 35312e442..edbdc2eae 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -35,14 +35,6 @@ # include "util.h" # include "ivl_assert.h" -/* - * Set the following to true when you need to process an expression - * that is being done in a constant context. This allows the - * elaboration to explicitly say we do not currently support constant - * user functions when the function is not found. - */ -bool need_constant_expr = false; - static bool get_const_argument(NetExpr*exp, verinum&res) { switch (exp->expr_type()) { @@ -472,14 +464,14 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const if (return_type_.range) { ivl_assert(*this, return_type_.range->size() == 2); - need_constant_expr = true; NetExpr*me = elab_and_eval(des, scope, - return_type_.range->at(0), -1); + return_type_.range->at(0), -1, + true); assert(me); NetExpr*le = elab_and_eval(des, scope, - return_type_.range->at(1), -1); + return_type_.range->at(1), -1, + true); assert(le); - need_constant_expr = false; long mnum = 0, lnum = 0; if ( ! get_const_argument(me, mnum) ) { @@ -545,14 +537,14 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const ivl_assert(*this, return_type_.range != 0); long use_wid; { - need_constant_expr = true; NetExpr*me = elab_and_eval(des, scope, - (*return_type_.range)[0], -1); + (*return_type_.range)[0], -1, + true); assert(me); NetExpr*le = elab_and_eval(des, scope, - (*return_type_.range)[1], -1); + (*return_type_.range)[1], -1, + true); assert(le); - need_constant_expr = false; long mnum = 0, lnum = 0; if ( ! get_const_argument(me, mnum) ) { @@ -848,10 +840,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const bool bad_lsb = false, bad_msb = false; /* If they exist get the port definition MSB and LSB */ if (port_set_ && port_msb_ != 0) { - /* We do not currently support constant user function. */ - need_constant_expr = true; - NetExpr*texpr = elab_and_eval(des, scope, port_msb_, -1); - need_constant_expr = false; + NetExpr*texpr = elab_and_eval(des, scope, port_msb_, -1, true); if (! eval_as_long(pmsb, texpr)) { cerr << port_msb_->get_fileline() << ": error: " @@ -865,10 +854,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const delete texpr; - /* We do not currently support constant user function. */ - need_constant_expr = true; - texpr = elab_and_eval(des, scope, port_lsb_, -1); - need_constant_expr = false; + texpr = elab_and_eval(des, scope, port_lsb_, -1, true); if (! eval_as_long(plsb, texpr)) { cerr << port_lsb_->get_fileline() << ": error: " @@ -892,10 +878,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const /* If they exist get the net/etc. definition MSB and LSB */ if (net_set_ && net_msb_ != 0 && !bad_msb && !bad_lsb) { - /* We do not currently support constant user function. */ - need_constant_expr = true; - NetExpr*texpr = elab_and_eval(des, scope, net_msb_, -1); - need_constant_expr = false; + NetExpr*texpr = elab_and_eval(des, scope, net_msb_, -1, true); if (! eval_as_long(nmsb, texpr)) { cerr << net_msb_->get_fileline() << ": error: " @@ -909,10 +892,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const delete texpr; - /* We do not currently support constant user function. */ - need_constant_expr = true; - texpr = elab_and_eval(des, scope, net_lsb_, -1); - need_constant_expr = false; + texpr = elab_and_eval(des, scope, net_lsb_, -1, true); if (! eval_as_long(nlsb, texpr)) { cerr << net_lsb_->get_fileline() << ": error: " @@ -1003,10 +983,8 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const if (lidx_ || ridx_) { assert(lidx_ && ridx_); - need_constant_expr = true; - NetExpr*lexp = elab_and_eval(des, scope, lidx_, -1); - NetExpr*rexp = elab_and_eval(des, scope, ridx_, -1); - need_constant_expr = false; + NetExpr*lexp = elab_and_eval(des, scope, lidx_, -1, true); + NetExpr*rexp = elab_and_eval(des, scope, ridx_, -1, true); if ((lexp == 0) || (rexp == 0)) { cerr << get_fileline() << ": internal error: There is " diff --git a/elaborate.cc b/elaborate.cc index a1e0e5a49..a37dd0f1c 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -227,10 +227,8 @@ unsigned PGBuiltin::calculate_array_count_(Design*des, NetScope*scope, gates, then I am expected to make more than one gate. Figure out how many are desired. */ if (msb_) { - need_constant_expr = true; - NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1); - NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1); - need_constant_expr = false; + NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1, true); + NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1, true); NetEConst*msb_con = dynamic_cast(msb_exp); NetEConst*lsb_con = dynamic_cast(lsb_exp); @@ -2057,9 +2055,8 @@ NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, { ivl_assert(*this, rval_); - need_constant_expr = is_constant_; - NetExpr*rv = elaborate_rval_expr(des, scope, lv_type, lv_width, rval()); - need_constant_expr = false; + NetExpr*rv = elaborate_rval_expr(des, scope, lv_type, lv_width, rval(), + is_constant_); if (!is_constant_ || !rv) return rv; @@ -3376,7 +3373,8 @@ NetProc* PEventStatement::elaborate_wait(Design*des, NetScope*scope, PExpr::width_mode_t mode; pe->test_width(des, scope, mode); - NetExpr*expr = pe->elaborate_expr(des, scope, pe->expr_width(), false); + NetExpr*expr = pe->elaborate_expr(des, scope, pe->expr_width(), + PExpr::NO_FLAGS); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate" " wait condition expression." << endl; @@ -4204,9 +4202,8 @@ bool Module::elaborate(Design*des, NetScope*scope) const for (specparam_it_t cur = specparams.begin() ; cur != specparams.end() ; ++ cur ) { - need_constant_expr = true; - NetExpr*val = elab_and_eval(des, scope, (*cur).second, -1); - need_constant_expr = false; + NetExpr*val = elab_and_eval(des, scope, (*cur).second, -1, + true); NetScope::spec_val_t value; if (NetECReal*val_cr = dynamic_cast (val)) { diff --git a/eval_tree.cc b/eval_tree.cc index 5fc9831c2..2d6f2e607 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -1946,12 +1946,5 @@ NetExpr* NetESFunc::eval_tree() NetExpr* NetEUFunc::eval_tree() { - if (need_constant_expr) { - cerr << get_fileline() << ": sorry: constant user " - "functions are not currently supported: " - << func_->basename() << "()." << endl; - - } - return 0; } diff --git a/net_design.cc b/net_design.cc index 31296aa63..59c7ebf55 100644 --- a/net_design.cc +++ b/net_design.cc @@ -332,7 +332,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) /* Evaluate the msb expression, if it is present. */ PExpr*msb_expr = (*cur).second.msb_expr; if (msb_expr) { - (*cur).second.msb = elab_and_eval(des, this, msb_expr, -1); + (*cur).second.msb = elab_and_eval(des, this, msb_expr, -1, true); if (! eval_as_long(msb, (*cur).second.msb)) { cerr << (*cur).second.val->get_fileline() << ": error: Unable to evaluate msb expression " @@ -348,7 +348,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) /* Evaluate the lsb expression, if it is present. */ PExpr*lsb_expr = (*cur).second.lsb_expr; if (lsb_expr) { - (*cur).second.lsb = elab_and_eval(des, this, lsb_expr, -1); + (*cur).second.lsb = elab_and_eval(des, this, lsb_expr, -1, true); if (! eval_as_long(lsb, (*cur).second.lsb)) { cerr << (*cur).second.val->get_fileline() << ": error: Unable to evaluate lsb expression " @@ -369,7 +369,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) if (range_flag) lv_width = (msb >= lsb) ? 1 + msb - lsb : 1 + lsb - msb; - NetExpr*expr = elab_and_eval(des, val_scope, val_expr, lv_width); + NetExpr*expr = elab_and_eval(des, val_scope, val_expr, lv_width, true); if (! expr) return; @@ -488,7 +488,7 @@ void NetScope::evaluate_parameter_real_(Design*des, param_ref_t cur) PExpr*val_expr = (*cur).second.val_expr; NetScope*val_scope = (*cur).second.val_scope; - NetExpr*expr = elab_and_eval(des, val_scope, val_expr, -1); + NetExpr*expr = elab_and_eval(des, val_scope, val_expr, -1, true); if (! expr) return; @@ -631,14 +631,6 @@ void NetScope::evaluate_parameter_(Design*des, param_ref_t cur) cur->second.val_expr = 0; } -/* - * Set the following to true when evaluating the parameter expressions. - * This causes PEIdent::elaborate_expr() to report an error if an - * identifier in an expression is anything other than a non-hierarchical - * parameter name. - */ -bool is_param_expr = false; - void NetScope::evaluate_parameters(Design*des) { for (map::const_iterator cur = children_.begin() @@ -649,13 +641,11 @@ void NetScope::evaluate_parameters(Design*des) cerr << ":0" << ": debug: " << "Evaluate parameters in " << scope_path(this) << endl; - is_param_expr = true; for (param_ref_t cur = parameters.begin() ; cur != parameters.end() ; ++ cur) { evaluate_parameter_(des, cur); } - is_param_expr = false; } void Design::residual_defparams() diff --git a/netmisc.cc b/netmisc.cc index 42657c7bc..6bb216e85 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -470,7 +470,8 @@ static const char*width_mode_name(PExpr::width_mode_t mode) } } -NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width) +NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, + int context_width, bool need_const) { PExpr::width_mode_t mode = PExpr::SIZED; if ((context_width == -2) && !gn_strict_expr_width_flag) @@ -510,7 +511,11 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width) } } - NetExpr*tmp = pe->elaborate_expr(des, scope, expr_width, false); + unsigned flags = PExpr::NO_FLAGS; + if (need_const) + flags |= PExpr::NEED_CONST; + + NetExpr*tmp = pe->elaborate_expr(des, scope, expr_width, flags); if (tmp == 0) return 0; eval_expr(tmp, context_width); @@ -524,7 +529,7 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width) } NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, - unsigned arg_idx, PExpr*pe) + unsigned arg_idx, PExpr*pe, bool need_const) { PExpr::width_mode_t mode = PExpr::SIZED; pe->test_width(des, scope, mode); @@ -539,7 +544,11 @@ NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, << ", mode=" << width_mode_name(mode) << endl; } - NetExpr*tmp = pe->elaborate_expr(des, scope, pe->expr_width(), true); + unsigned flags = PExpr::SYS_TASK_ARG; + if (need_const) + flags |= PExpr::NEED_CONST; + + NetExpr*tmp = pe->elaborate_expr(des, scope, pe->expr_width(), flags); if (tmp == 0) return 0; eval_expr(tmp, -1); diff --git a/netmisc.h b/netmisc.h index f056c933f..20d72461f 100644 --- a/netmisc.h +++ b/netmisc.h @@ -134,17 +134,6 @@ extern NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid); */ extern unsigned count_lval_width(const class NetAssign_*first); -/* - * This is temporarily used to indicate that a user function elaboration - * fail is likely the result of missing constant user function support. - */ -extern bool need_constant_expr; - -/* - * This is used to indicate that we are evaluating a parameter expression. - */ -extern bool is_param_expr; - /* * This function elaborates an expression, and tries to evaluate it * right away. If the expression can be evaluated, this returns a @@ -160,14 +149,16 @@ extern bool is_param_expr; class PExpr; extern NetExpr* elab_and_eval(Design*des, NetScope*scope, - PExpr*pe, int context_width); + PExpr*pe, int context_width, + bool need_const =false); /* * This function is a variant of elab_and_eval that elaborates and * evaluates the arguments of a system task. */ -extern NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, - unsigned arg_idx, PExpr*pe); +extern NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, + perm_string name, unsigned arg_idx, + PExpr*pe, bool need_const =false); /* * This function elaborates an expression as if it is for the r-value * of an assignment, The lv_type and lv_width are the type and width @@ -176,7 +167,8 @@ extern NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, */ extern NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_variable_type_t lv_type, - unsigned lv_width, PExpr*expr); + unsigned lv_width, PExpr*expr, + bool need_const =false); /* * This procedure evaluates an expression and if the evaluation is From 86801bef52745b4ebc90c00ca8b834502682e4f1 Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sat, 2 Apr 2011 22:31:19 +0100 Subject: [PATCH 07/19] Fix for compiler crash when function arguments are unknown. When a user or system function is called on the RHS of a continuous assignment, and one of the function arguments is an undeclared identifier, the compiler reports the error correctly but then crashes. This patch fixes the crash. --- elab_expr.cc | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/elab_expr.cc b/elab_expr.cc index 7b8a76594..4e7f396dd 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1172,14 +1172,19 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, bool need_const = NEED_CONST & flags; + unsigned parm_errors = 0; unsigned missing_parms = 0; for (unsigned idx = 0 ; idx < nparms ; idx += 1) { PExpr*expr = parms_[idx]; if (expr) { NetExpr*tmp = elab_sys_task_arg(des, scope, name, idx, expr, need_const); - fun->parm(idx, tmp); - + if (tmp) { + fun->parm(idx, tmp); + } else { + parm_errors += 1; + fun->parm(idx, 0); + } } else { missing_parms += 1; fun->parm(idx, 0); @@ -1194,6 +1199,9 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, des->errors += 1; } + if (missing_parms || parm_errors) + return 0; + NetExpr*tmp = pad_to_width(fun, expr_wid, *this); tmp->cast_signed(signed_flag_); @@ -1312,6 +1320,7 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, bool need_const = NEED_CONST & flags; + unsigned parm_errors = 0; unsigned missing_parms = 0; for (unsigned idx = 0 ; idx < parms.count() ; idx += 1) { PExpr*tmp = parms_[idx]; @@ -1320,6 +1329,10 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, def->port(idx)->data_type(), (unsigned)def->port(idx)->vector_width(), tmp, need_const); + if (parms[idx] == 0) { + parm_errors += 1; + continue; + } if (NetEEvent*evt = dynamic_cast (parms[idx])) { cerr << evt->get_fileline() << ": error: An event '" << evt->event()->name() << "' can not be a user " @@ -1347,6 +1360,8 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, des->errors += 1; } + if (missing_parms || parm_errors) + return 0; /* Look for the return value signal for the called function. This return value is a magic signal in the scope From c5b83bf2ee8a8775388ad0ef3ed7093d1af977a4 Mon Sep 17 00:00:00 2001 From: Cary R Date: Mon, 4 Apr 2011 10:47:20 -0700 Subject: [PATCH 08/19] Add vpiBitVar to the memory cleanup code. Support for vpiBitVar also needed to be added to the memory (valgrind) cleanup code. --- vvp/vpi_scope.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vvp/vpi_scope.cc b/vvp/vpi_scope.cc index e3449679c..45afce324 100644 --- a/vvp/vpi_scope.cc +++ b/vvp/vpi_scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 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 @@ -81,6 +81,7 @@ static void delete_sub_scopes(struct __vpiScope *scope) case vpiShortIntVar: case vpiIntVar: case vpiByteVar: + case vpiBitVar: signal_delete((scope->intern)[idx]); break; case vpiParameter: From 186a677f4accbca55ae0e2d11d0100cedffd3bd8 Mon Sep 17 00:00:00 2001 From: Larry Doolittle Date: Tue, 5 Apr 2011 22:11:30 -0700 Subject: [PATCH 09/19] Add HAVE_LROUND to config.h.in Needed by verinum.cc --- config.h.in | 1 + 1 file changed, 1 insertion(+) diff --git a/config.h.in b/config.h.in index cce688618..3fd7953c9 100644 --- a/config.h.in +++ b/config.h.in @@ -42,6 +42,7 @@ # undef HAVE_LIBREADLINE # undef HAVE_LIBZ # undef HAVE_LIBBZ2 +# undef HAVE_LROUND # undef HAVE_SYS_WAIT_H # undef WORDS_BIGENDIAN From 9a4816685508a10fbf3039b7c91e8ec5fae61ef7 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Wed, 6 Apr 2011 21:27:24 +0100 Subject: [PATCH 10/19] tgt-vhdl: Improve temporary signal name generation to avoid collisions Fixes regression of simple_gen test. Also extended ivl_lpm_size API call to support all LPM types. This simplifies some of the VHDL LPM generation code a little. --- t-dll-api.cc | 30 ++++++++++++++++++++++++++++++ tgt-vhdl/lpm.cc | 39 +++++++++++++-------------------------- tgt-vhdl/scope.cc | 11 ++++++++++- tgt-vhdl/vhdl_syntax.cc | 5 +++++ tgt-vhdl/vhdl_syntax.hh | 1 + 5 files changed, 59 insertions(+), 27 deletions(-) diff --git a/t-dll-api.cc b/t-dll-api.cc index 206943f1a..179a42a62 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -1424,6 +1424,36 @@ extern "C" unsigned ivl_lpm_size(ivl_lpm_t net) return net->u_.repeat.count; case IVL_LPM_CONCAT: return net->u_.concat.inputs; + case IVL_LPM_ABS: + case IVL_LPM_CAST_INT: + case IVL_LPM_CAST_INT2: + case IVL_LPM_CAST_REAL: + case IVL_LPM_RE_AND: + case IVL_LPM_RE_OR: + case IVL_LPM_RE_XOR: + case IVL_LPM_RE_NAND: + case IVL_LPM_RE_NOR: + case IVL_LPM_RE_XNOR: + case IVL_LPM_SIGN_EXT: + case IVL_LPM_FF: + return 1; + case IVL_LPM_ADD: + case IVL_LPM_CMP_EEQ: + case IVL_LPM_CMP_EQ: + case IVL_LPM_CMP_GE: + case IVL_LPM_CMP_GT: + case IVL_LPM_CMP_NE: + case IVL_LPM_CMP_NEE: + case IVL_LPM_DIVIDE: + case IVL_LPM_MOD: + case IVL_LPM_MULT: + case IVL_LPM_POW: + case IVL_LPM_SUB: + case IVL_LPM_SHIFTL: + case IVL_LPM_SHIFTR: + case IVL_LPM_PART_VP: + case IVL_LPM_PART_PV: + return 2; default: assert(0); return 0; diff --git a/tgt-vhdl/lpm.cc b/tgt-vhdl/lpm.cc index c68d6bebe..3c7c5bca4 100644 --- a/tgt-vhdl/lpm.cc +++ b/tgt-vhdl/lpm.cc @@ -41,26 +41,6 @@ static vhdl_expr *part_select_base(vhdl_scope *scope, ivl_lpm_t lpm) return off->cast(&integer); } -static vhdl_expr *concat_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm) -{ - vhdl_type *result_type = - vhdl_type::type_for(ivl_lpm_width(lpm), ivl_lpm_signed(lpm) != 0); - vhdl_binop_expr *expr = - new vhdl_binop_expr(VHDL_BINOP_CONCAT, result_type); - - for (int i = ivl_lpm_size(lpm) - 1; i >= 0; i--) { - vhdl_expr *e = readable_ref(scope, ivl_lpm_data(lpm, i)); - if (NULL == e) { - delete expr; - return NULL; - } - - expr->add_expr(e); - } - - return expr; -} - static vhdl_expr *binop_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op) { unsigned out_width = ivl_lpm_width(lpm); @@ -68,14 +48,21 @@ static vhdl_expr *binop_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop vhdl_type::type_for(out_width, ivl_lpm_signed(lpm) != 0); vhdl_binop_expr *expr = new vhdl_binop_expr(op, result_type); - for (int i = 0; i < 2; i++) { + for (unsigned i = 0; i < ivl_lpm_size(lpm); i++) { vhdl_expr *e = readable_ref(scope, ivl_lpm_data(lpm, i)); - if (NULL == e) { - delete expr; + if (NULL == e) return NULL; - } - expr->add_expr(e->cast(result_type)); + // It's possible that the inputs are a mixture of signed and unsigned + // in which case we must cast them to the output type + e = e->cast(vhdl_type::type_for(e->get_type()->get_width(), + ivl_lpm_signed(lpm) != 0)); + + // Bit of a hack: the LPM inputs are in the wrong order for concatenation + if (op == VHDL_BINOP_CONCAT) + expr->add_expr_front(e); + else + expr->add_expr(e); } if (op == VHDL_BINOP_MULT) { @@ -256,7 +243,7 @@ static vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm) case IVL_LPM_MOD: return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_MOD); case IVL_LPM_CONCAT: - return concat_lpm_to_expr(scope, lpm); + return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_CONCAT); case IVL_LPM_CMP_GE: return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_GEQ); case IVL_LPM_CMP_GT: diff --git a/tgt-vhdl/scope.cc b/tgt-vhdl/scope.cc index 56d313933..190b18590 100644 --- a/tgt-vhdl/scope.cc +++ b/tgt-vhdl/scope.cc @@ -239,7 +239,16 @@ void draw_nexus(ivl_nexus_t nexus) vhdl_type *type = vhdl_type::type_for(lpm_temp_width, ivl_lpm_signed(lpm) != 0); ostringstream ss; - ss << "LPM" << ivl_lpm_basename(lpm); + ss << "LPM"; + if (nexus == ivl_lpm_q(lpm)) + ss << "_q"; + else { + for (unsigned d = 0; d < ivl_lpm_size(lpm); d++) { + if (nexus == ivl_lpm_data(lpm, d)) + ss << "_d" << d; + } + } + ss << ivl_lpm_basename(lpm); if (!vhdl_scope->have_declared(ss.str())) vhdl_scope->add_decl(new vhdl_signal_decl(ss.str().c_str(), type)); diff --git a/tgt-vhdl/vhdl_syntax.cc b/tgt-vhdl/vhdl_syntax.cc index 4f231e1ee..747717e24 100644 --- a/tgt-vhdl/vhdl_syntax.cc +++ b/tgt-vhdl/vhdl_syntax.cc @@ -940,6 +940,11 @@ void vhdl_binop_expr::add_expr(vhdl_expr *e) operands_.push_back(e); } +void vhdl_binop_expr::add_expr_front(vhdl_expr *e) +{ + operands_.push_front(e); +} + void vhdl_binop_expr::find_vars(vhdl_var_set_t& read) { for (list::const_iterator it = operands_.begin(); diff --git a/tgt-vhdl/vhdl_syntax.hh b/tgt-vhdl/vhdl_syntax.hh index 32745b097..b591d5ebf 100644 --- a/tgt-vhdl/vhdl_syntax.hh +++ b/tgt-vhdl/vhdl_syntax.hh @@ -126,6 +126,7 @@ public: ~vhdl_binop_expr(); void add_expr(vhdl_expr *e); + void add_expr_front(vhdl_expr *e); void emit(std::ostream &of, int level) const; void find_vars(vhdl_var_set_t& read); private: From c81645ff46c42cd7a7b6c57a12ee23ce8c28a9e5 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Wed, 6 Apr 2011 22:12:16 +0100 Subject: [PATCH 11/19] tgt-vhdl: Fix expression generation corner case and bug in xnor reduction operator Certain types of expressions involving only constants would produce ambiguous VHDL output. Fixed by qualifying one of the arguments. E.g. ('0' or '1') = '1' Which is ambiguous becomes (std_logic'('0') or '1') = '1' This fixes the xnor_test test. Reduce XNOR was implemented incorrectly because of trivial typo --- tgt-vhdl/expr.cc | 13 +++++++++++++ tgt-vhdl/lpm.cc | 2 +- tgt-vhdl/support.cc | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/tgt-vhdl/expr.cc b/tgt-vhdl/expr.cc index 2b8322683..555a50f43 100644 --- a/tgt-vhdl/expr.cc +++ b/tgt-vhdl/expr.cc @@ -320,6 +320,19 @@ static vhdl_expr *translate_binary(ivl_expr_t e) int rwidth = rhs->get_type()->get_width(); int result_width = ivl_expr_width(e); + // There's a funny corner-case where both the LHS and RHS are constant + // single bit numbers and the VHDL compiler can't decide between the + // std_ulogic and bit overloads of various operators + const bool lnumber = ivl_expr_type(ivl_expr_oper1(e)) == IVL_EX_NUMBER; + const bool rnumber = ivl_expr_type(ivl_expr_oper2(e)) == IVL_EX_NUMBER; + if (lwidth == 1 && rwidth == 1 && lnumber && rnumber) { + // It's sufficient to qualify only one side + vhdl_fcall *lqual = new vhdl_fcall("std_logic'", lhs->get_type()); + lqual->add_expr(lhs); + + lhs = lqual; + } + // For === and !== we need to compare std_logic_vectors // rather than signeds vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR, result_width-1, 0); diff --git a/tgt-vhdl/lpm.cc b/tgt-vhdl/lpm.cc index 3c7c5bca4..7153ec06b 100644 --- a/tgt-vhdl/lpm.cc +++ b/tgt-vhdl/lpm.cc @@ -271,7 +271,7 @@ static vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm) case IVL_LPM_RE_XOR: return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XOR, false); case IVL_LPM_RE_XNOR: - return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XOR, true); + return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XNOR, true); case IVL_LPM_SIGN_EXT: return sign_extend_lpm_to_expr(scope, lpm); case IVL_LPM_ARRAY: diff --git a/tgt-vhdl/support.cc b/tgt-vhdl/support.cc index 6f16ba762..3a06efea7 100644 --- a/tgt-vhdl/support.cc +++ b/tgt-vhdl/support.cc @@ -144,7 +144,7 @@ void support_function::emit(std::ostream &of, int level) const emit_reduction(of, level, "and", '1'); break; case SF_REDUCE_XOR: - emit_reduction(of, level, "xnor", '0'); + emit_reduction(of, level, "xor", '0'); break; case SF_REDUCE_XNOR: emit_reduction(of, level, "xnor", '0'); From 0bc746dab0bc3a4562f86b676f2ccb841b4daf0f Mon Sep 17 00:00:00 2001 From: Cary R Date: Mon, 4 Apr 2011 10:43:58 -0700 Subject: [PATCH 12/19] Add message that a UDP with a range is not currently supported. For now Icarus doesn't support a UDP instantiation with a range. Instead of generating a warning about the port count being wrong this patch adds code to calculate the range and print a message if a range greater than one is found. --- PGate.h | 5 +++- elaborate.cc | 81 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/PGate.h b/PGate.h index e45f95ec3..3a4b9ced4 100644 --- a/PGate.h +++ b/PGate.h @@ -1,7 +1,7 @@ #ifndef __PGate_H #define __PGate_H /* - * Copyright (c) 1998-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2011 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 @@ -238,6 +238,9 @@ class PGModule : public PGate { friend class delayed_elaborate_scope_mod_instances; void elaborate_mod_(Design*, Module*mod, NetScope*scope) const; void elaborate_udp_(Design*, PUdp *udp, NetScope*scope) const; + unsigned calculate_instance_count_(Design*, NetScope*, + long&high, long&low, + perm_string name) const; void elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const; void elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*sc) const; bool elaborate_sig_mod_(Design*des, NetScope*scope, Module*mod) const; diff --git a/elaborate.cc b/elaborate.cc index a37dd0f1c..c572f4ef8 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -263,8 +263,8 @@ unsigned PGBuiltin::calculate_array_count_(Design*des, NetScope*scope, if (debug_elaborate) { cerr << get_fileline() << ": debug: PGBuiltin: Make array " - << "[" << high << ":" << low << "]" - << " of " << count << " gates for " << get_name() << endl; + << "[" << high << ":" << low << "]" << " of " + << count << " gates for " << get_name() << endl; } } @@ -684,8 +684,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const unsigned instance_width = 1; perm_string name = get_name(); - if (name == "") - name = scope->local_symbol(); + if (name == "") name = scope->local_symbol(); /* Calculate the array bounds and instance count for the gate, as described in the Verilog source. If there is none, then @@ -693,8 +692,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const long low=0, high=0; unsigned array_count = calculate_array_count_(des, scope, high, low); - if (array_count == 0) - return; + if (array_count == 0) return; unsigned output_count = calculate_output_count_(); @@ -1771,6 +1769,64 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const } +unsigned PGModule::calculate_instance_count_(Design*des, NetScope*scope, + long&high, long&low, + perm_string name) const +{ + unsigned count = 1; + high = 0; + low = 0; + + /* If the Verilog source has a range specification for the UDP, then + * I am expected to make more than one gate. Figure out how many are + * desired. */ + if (msb_) { + need_constant_expr = true; + NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1); + NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1); + need_constant_expr = false; + + NetEConst*msb_con = dynamic_cast(msb_exp); + NetEConst*lsb_con = dynamic_cast(lsb_exp); + + if (msb_con == 0) { + cerr << get_fileline() << ": error: Unable to evaluate " + "expression " << *msb_ << endl; + des->errors += 1; + return 0; + } + + if (lsb_con == 0) { + cerr << get_fileline() << ": error: Unable to evaluate " + "expression " << *lsb_ << endl; + des->errors += 1; + return 0; + } + + verinum msb = msb_con->value(); + verinum lsb = lsb_con->value(); + + delete msb_exp; + delete lsb_exp; + + if (msb.as_long() > lsb.as_long()) + count = msb.as_long() - lsb.as_long() + 1; + else + count = lsb.as_long() - msb.as_long() + 1; + + low = lsb.as_long(); + high = msb.as_long(); + + if (debug_elaborate) { + cerr << get_fileline() << ": debug: PGModule: Make range " + << "[" << high << ":" << low << "]" << " of " + << count << " UDPs for " << name << endl; + } + } + + return count; +} + /* * From a UDP definition in the source, make a NetUDP * object. Elaborate the pin expressions as netlists, then connect @@ -1801,6 +1857,19 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const } } + long low = 0, high = 0; + unsigned inst_count = calculate_instance_count_(des, scope, high, low, + my_name); + if (inst_count == 0) return; + + if (inst_count != 1) { + cerr << get_fileline() << ": sorry: UDPs with a range (" + << my_name << " [" << high << ":" << low << "]) are " + << "not supported." << endl; + des->errors += 1; + return; + } + assert(udp); NetUDP*net = new NetUDP(scope, my_name, udp->ports.count(), udp); net->set_line(*this); From 428755ce627daceedffd19d13b6ab07e9092491f Mon Sep 17 00:00:00 2001 From: Cary R Date: Wed, 6 Apr 2011 19:48:41 -0700 Subject: [PATCH 13/19] Fix for compilation problem created by constant expr. rework This patch fixes a compilation problem in the UDP range code created since it and the constant expression rework were done at the same time. --- elaborate.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/elaborate.cc b/elaborate.cc index c572f4ef8..e01a3f980 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -1781,10 +1781,8 @@ unsigned PGModule::calculate_instance_count_(Design*des, NetScope*scope, * I am expected to make more than one gate. Figure out how many are * desired. */ if (msb_) { - need_constant_expr = true; - NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1); - NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1); - need_constant_expr = false; + NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1, true); + NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1, true); NetEConst*msb_con = dynamic_cast(msb_exp); NetEConst*lsb_con = dynamic_cast(lsb_exp); From 1e9f9685cce3170d2a689ea1bbccc0bde69fd84e Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Tue, 5 Apr 2011 20:43:54 +0100 Subject: [PATCH 14/19] First step towards supporting constant user functions. This patch allows the compiler to perform early elaboration of functions if they are encountered in expressions that are elaborated before the function would normally be elaborated. This makes the function available for constant evaluation. Suitable error messages are generated if a function that is used in a constant expression is not a valid constant function. --- dup_expr.cc | 3 +- elab_expr.cc | 115 ++++++++++++++++++++++++++++++++++++++++---------- elab_scope.cc | 8 ++++ elab_sig.cc | 5 +++ elaborate.cc | 6 +++ eval_tree.cc | 24 +++++++++++ net_design.cc | 14 +++++- net_scope.cc | 4 ++ netlist.cc | 4 +- netlist.h | 32 ++++++++++++-- 10 files changed, 184 insertions(+), 31 deletions(-) diff --git a/dup_expr.cc b/dup_expr.cc index d541161e9..cccdff8c6 100644 --- a/dup_expr.cc +++ b/dup_expr.cc @@ -239,7 +239,8 @@ NetEUFunc* NetEUFunc::dup_expr() const tmp_parms[idx] = parms_[idx]->dup_expr(); } - tmp = new NetEUFunc(scope_, func_, result_sig_->dup_expr(), tmp_parms); + tmp = new NetEUFunc(scope_, func_, result_sig_->dup_expr(), tmp_parms, + need_const_); ivl_assert(*this, tmp); tmp->set_line(*this); diff --git a/elab_expr.cc b/elab_expr.cc index 4e7f396dd..7194909a6 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1286,16 +1286,9 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, return elaborate_access_func_(des, scope, access_nature, expr_wid); - // We do not currently support constant user function and - // this is where things fail because of that, though we - // don't know for sure so we need to display both messages. - if (NEED_CONST & flags) { - cerr << get_fileline() << ": sorry: constant user " - "functions are not currently supported: " - << path_ << "()." << endl << " or" << endl; - } - cerr << get_fileline() << ": error: No function " << path_ - << " in this context (" << scope_path(scope) << ")." << endl; + cerr << get_fileline() << ": error: No function named `" << path_ + << "' found in this context (" << scope_path(scope) << ")." + << endl; des->errors += 1; return 0; } @@ -1304,6 +1297,29 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, NetScope*dscope = def->scope(); ivl_assert(*this, dscope); + bool need_const = NEED_CONST & flags; + + // It is possible to get here before the called function has been + // fully elaborated. If this is the case, elaborate it now. This + // ensures we know whether or not it is a constant function. + if (dscope->elab_stage() < 3) { + dscope->need_const_func(need_const); + const PFunction*pfunc = dscope->func_pform(); + ivl_assert(*this, pfunc); + pfunc->elaborate(des, dscope); + } + + if (dscope->parent() != scope->parent() || !dscope->is_const_func()) { + if (scope->need_const_func()) { + cerr << get_fileline() << ": error: A function invoked by " + "a constant function must be a constant function " + "local to the current module." << endl; + des->errors += 1; + return 0; + } + scope->is_const_func(false); + } + if (! check_call_matches_definition_(des, dscope)) return 0; @@ -1318,8 +1334,6 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, of the function being called. The scope of the called function is elaborated when the definition is elaborated. */ - bool need_const = NEED_CONST & flags; - unsigned parm_errors = 0; unsigned missing_parms = 0; for (unsigned idx = 0 ; idx < parms.count() ; idx += 1) { @@ -1331,8 +1345,8 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, tmp, need_const); if (parms[idx] == 0) { parm_errors += 1; - continue; - } + continue; + } if (NetEEvent*evt = dynamic_cast (parms[idx])) { cerr << evt->get_fileline() << ": error: An event '" << evt->event()->name() << "' can not be a user " @@ -1360,6 +1374,26 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, des->errors += 1; } + if (need_const && !dscope->is_const_func()) { + + // If this is the first time the function has been called in + // a constant context, force the function to be re-elaborated. + // This will generate the necessary error messages to allow + // the user to diagnose the fault. + if (!dscope->need_const_func()) { + dscope->set_elab_stage(2); + dscope->need_const_func(true); + const PFunction*pfunc = dscope->func_pform(); + ivl_assert(*this, pfunc); + pfunc->elaborate(des, dscope); + } + + cerr << get_fileline() << ": error: `" << dscope->basename() + << "' is not a constant function." << endl; + des->errors += 1; + return 0; + } + if (missing_parms || parm_errors) return 0; @@ -1373,7 +1407,7 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, if (NetNet*res = dscope->find_signal(dscope->basename())) { NetESignal*eres = new NetESignal(res); - NetEUFunc*func = new NetEUFunc(scope, dscope, eres, parms); + NetEUFunc*func = new NetEUFunc(scope, dscope, eres, parms, need_const); func->set_line(*this); NetExpr*tmp = pad_to_width(func, expr_wid, *this); @@ -1866,12 +1900,22 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, const NetExpr*ex1, *ex2; - if ((NEED_CONST & flags) && (path_.size() > 1)) { - cerr << get_fileline() << ": error: A hierarchical reference ('" - << path_ << "') is not allowed in a constant expression." - << endl; - des->errors += 1; - return 0; + if (path_.size() > 1) { + if (NEED_CONST & flags) { + cerr << get_fileline() << ": error: A hierarchical reference" + " (`" << path_ << "') is not allowed in a constant" + " expression." << endl; + des->errors += 1; + return 0; + } + if (scope->need_const_func()) { + cerr << get_fileline() << ": error: A hierarchical reference" + " (`" << path_ << "') is not allowed in a constant" + " function." << endl; + des->errors += 1; + return 0; + } + scope->is_const_func(false); } NetScope*found_in = symbol_search(this, des, scope, path_, @@ -1897,11 +1941,21 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, if (net != 0) { if (NEED_CONST & flags) { cerr << get_fileline() << ": error: A reference to a wire " - "or register ('" << path_ << "') is not allowed in " + "or reg (`" << path_ << "') is not allowed in " "a constant expression." << endl; des->errors += 1; return 0; } + if (net->scope() != scope) { + if (scope->need_const_func()) { + cerr << get_fileline() << ": error: A reference to a " + "non-local wire or reg (`" << path_ << "') is " + "not allowed in a constant function." << endl; + des->errors += 1; + return 0; + } + scope->is_const_func(false); + } NetExpr*tmp = elaborate_expr_net(des, scope, net, found_in, expr_wid, flags); @@ -1919,11 +1973,21 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, if (eve != 0) { if (NEED_CONST & flags) { cerr << get_fileline() << ": error: A reference to a named " - "event ('" << path_ << "') is not allowed in a " + "event (`" << path_ << "') is not allowed in a " "constant expression." << endl; des->errors += 1; return 0; } + if (eve->scope() != scope) { + if (scope->need_const_func()) { + cerr << get_fileline() << ": error: A reference to a " + "non-local named event (`" << path_ << "') is " + "not allowed in a constant function." << endl; + des->errors += 1; + return 0; + } + scope->is_const_func(false); + } NetEEvent*tmp = new NetEEvent(eve); tmp->set_line(*this); @@ -2061,6 +2125,11 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, << (NEED_CONST & flags ? "parameter" : "wire/reg/memory") << " `" << path_ << "' in `" << scope_path(scope) << "'" << endl; + if (scope->need_const_func()) { + cerr << get_fileline() << ": : `" << scope->basename() + << "' is being used as a constant function, so may " + "only reference local variables." << endl; + } des->errors += 1; return 0; } diff --git a/elab_scope.cc b/elab_scope.cc index 76f38cacb..984a93af9 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1362,6 +1362,14 @@ void PFunction::elaborate_scope(Design*des, NetScope*scope) const { assert(scope->type() == NetScope::FUNC); + // Save a reference to the pform representation of the function + // in case we need to perform early elaboration. + scope->set_func_pform(this); + + // Assume the function is a constant function until we + // find otherwise. + scope->is_const_func(true); + // Scan the parameters in the function, and store the information // needed to evaluate the parameter expressions. diff --git a/elab_sig.cc b/elab_sig.cc index edbdc2eae..15013c9b8 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -437,6 +437,11 @@ bool PGenerate::elaborate_sig_(Design*des, NetScope*scope) const */ void PFunction::elaborate_sig(Design*des, NetScope*scope) const { + if (scope->elab_stage() > 1) + return; + + scope->set_elab_stage(2); + perm_string fname = scope->basename(); assert(scope->type() == NetScope::FUNC); diff --git a/elaborate.cc b/elaborate.cc index e01a3f980..d09f7a072 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -3801,6 +3801,11 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const void PFunction::elaborate(Design*des, NetScope*scope) const { + if (scope->elab_stage() > 2) + return; + + scope->set_elab_stage(3); + NetFuncDef*def = scope->func_def(); if (def == 0) { cerr << get_fileline() << ": internal error: " @@ -3816,6 +3821,7 @@ void PFunction::elaborate(Design*des, NetScope*scope) const if (st == 0) { cerr << statement_->get_fileline() << ": error: Unable to elaborate " "statement in function " << scope->basename() << "." << endl; + scope->is_const_func(true); // error recovery des->errors += 1; return; } diff --git a/eval_tree.cc b/eval_tree.cc index 2d6f2e607..6bc35b4c0 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -1946,5 +1946,29 @@ NetExpr* NetESFunc::eval_tree() NetExpr* NetEUFunc::eval_tree() { + // If we know the function cannot be evaluated as a constant, + // give up now. + if (!func()->is_const_func()) + return 0; + + // Variables inside static functions can be accessed from outside + // the function, so we can't be sure they are constant unless the + // function was called in a constant context. + if (!func()->is_auto() && !need_const_) + return 0; + + // Run through the input parameters to check they are constants. + for (unsigned idx = 0; idx < parm_count(); idx += 1) { + if (dynamic_cast (parm(idx))) + continue; + if (dynamic_cast (parm(idx))) + continue; + return 0; + } + + if (need_const_) { + cerr << get_fileline() << ": sorry: Constant user functions are " + "not yet supported." << endl; + } return 0; } diff --git a/net_design.cc b/net_design.cc index 59c7ebf55..4b4ba5692 100644 --- a/net_design.cc +++ b/net_design.cc @@ -33,6 +33,7 @@ # include "compiler.h" # include "netmisc.h" # include "PExpr.h" +# include "PTask.h" # include # include "ivl_assert.h" @@ -720,9 +721,18 @@ NetFuncDef* Design::find_function(NetScope*scope, const pform_name_t&name) std::list eval_path = eval_scope_path(this, scope, name); NetScope*func = find_scope(scope, eval_path, NetScope::FUNC); - if (func && (func->type() == NetScope::FUNC)) + if (func && (func->type() == NetScope::FUNC)) { + // If a function is used in a parameter definition or in + // a signal declaration, it is possible to get here before + // the function's signals have been elaborated. If this is + // the case, elaborate them now. + if (func->elab_stage() < 2) { + const PFunction*pfunc = func->func_pform(); + assert(pfunc); + pfunc->elaborate_sig(this, func); + } return func->func_def(); - + } return 0; } diff --git a/net_scope.cc b/net_scope.cc index 50f2d646f..2509cbc61 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -43,6 +43,8 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t) { events_ = 0; lcounter_ = 0; + need_const_func_ = false; + is_const_func_ = false; is_auto_ = false; is_cell_ = false; @@ -72,6 +74,8 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t) default: /* BEGIN_END and FORK_JOIN, do nothing */ break; } + func_pform_ = 0; + elab_stage_ = 1; lineno_ = 0; def_lineno_ = 0; genvar_tmp_val = 0; diff --git a/netlist.cc b/netlist.cc index 9adca5dcc..d59be9650 100644 --- a/netlist.cc +++ b/netlist.cc @@ -1988,8 +1988,8 @@ const NetExpr* NetSTask::parm(unsigned idx) const } NetEUFunc::NetEUFunc(NetScope*scope, NetScope*def, NetESignal*res, - svector&p) -: scope_(scope), func_(def), result_sig_(res), parms_(p) + svector&p, bool nc) +: scope_(scope), func_(def), result_sig_(res), parms_(p), need_const_(nc) { expr_width(result_sig_->expr_width()); } diff --git a/netlist.h b/netlist.h index 042be10ae..2a9a3e06f 100644 --- a/netlist.h +++ b/netlist.h @@ -73,6 +73,7 @@ class NetTaskDef; class NetEvTrig; class NetEvWait; class PExpr; +class PFunction; class netenum_t; struct target; @@ -790,6 +791,29 @@ class NetScope : public Attrib { unsigned get_def_lineno() const { return def_lineno_; }; bool in_func() const; + + /* Provide a link back to the pform to allow early elaboration of + constant functions. */ + void set_func_pform(const PFunction*pfunc) { func_pform_ = pfunc; }; + const PFunction*func_pform() const { return func_pform_; }; + + /* Allow tracking of elaboration stages. The three stages are: + 1 - scope elaboration + 2 - signal elaboration + 3 - statement elaboration + This is only used for functions, to support early elaboration. + */ + void set_elab_stage(unsigned stage) { elab_stage_ = stage; }; + unsigned elab_stage() const { return elab_stage_; }; + + /* Is this a function called in a constant expression. */ + void need_const_func(bool need_const) { need_const_func_ = need_const; }; + bool need_const_func() const { return need_const_func_; }; + + /* Is this a constant function. */ + void is_const_func(bool is_const) { is_const_func_ = is_const; }; + bool is_const_func() const { return is_const_func_; }; + /* Is the task or function automatic. */ void is_auto(bool is_auto__) { is_auto_ = is_auto__; }; bool is_auto() const { return is_auto_; }; @@ -904,7 +928,6 @@ class NetScope : public Attrib { param_ref_t find_parameter(perm_string name); - struct spec_val_t { ivl_variable_type_t type; union { @@ -954,6 +977,8 @@ class NetScope : public Attrib { NetTaskDef*task_; NetFuncDef*func_; }; + const PFunction*func_pform_; + unsigned elab_stage_; // Enumerations. The enum_sets_ is a list of all the // enumerations present in this scope. The enum_names_ is a @@ -966,7 +991,7 @@ class NetScope : public Attrib { map children_; unsigned lcounter_; - bool is_auto_, is_cell_; + bool need_const_func_, is_const_func_, is_auto_, is_cell_; }; /* @@ -3095,7 +3120,7 @@ class NetTaskDef { class NetEUFunc : public NetExpr { public: - NetEUFunc(NetScope*, NetScope*, NetESignal*, svector&); + NetEUFunc(NetScope*, NetScope*, NetESignal*, svector&, bool); ~NetEUFunc(); const NetESignal*result_sig() const; @@ -3119,6 +3144,7 @@ class NetEUFunc : public NetExpr { NetScope*func_; NetESignal*result_sig_; svector parms_; + bool need_const_; private: // not implemented NetEUFunc(const NetEUFunc&); From 2b95e9b463c109d5efb1dfd766f58970a99d2aa6 Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 7 Apr 2011 12:03:16 -0700 Subject: [PATCH 15/19] Forward port the LEX/YACC definition changes from V0.9 It's best to use the LEX and YACC definitions instead of hard coding flex/bison so they can be overridden if needed. --- driver/Makefile.in | 6 ++++-- ivlpp/Makefile.in | 3 ++- vpi/Makefile.in | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/driver/Makefile.in b/driver/Makefile.in index 346ce4ab5..f31d76037 100644 --- a/driver/Makefile.in +++ b/driver/Makefile.in @@ -38,6 +38,8 @@ CC = @CC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ +LEX = @LEX@ +YACC = @YACC@ MAN = @MAN@ PS2PDF = @PS2PDF@ @@ -78,10 +80,10 @@ iverilog@EXEEXT@: $O $(CC) $(LDFLAGS) $O -o iverilog@EXEEXT@ @EXTRALIBS@ cflexor.c: cflexor.lex - flex -s -Pcf -ocflexor.c $(srcdir)/cflexor.lex + $(LEX) -s -Pcf -ocflexor.c $(srcdir)/cflexor.lex cfparse.h cfparse.c: cfparse.y - bison --verbose -t -d -o cfparse.c --name-prefix=cf $(srcdir)/cfparse.y + $(YACC) --verbose -t -d -o cfparse.c --name-prefix=cf $(srcdir)/cfparse.y %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o diff --git a/ivlpp/Makefile.in b/ivlpp/Makefile.in index f65f1114f..62a799e98 100644 --- a/ivlpp/Makefile.in +++ b/ivlpp/Makefile.in @@ -36,6 +36,7 @@ CC = @CC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ +LEX = @LEX@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. @@ -69,7 +70,7 @@ ivlpp@EXEEXT@: $O $(CC) $(LDFLAGS) $O -o ivlpp@EXEEXT@ @EXTRALIBS@ lexor.c: lexor.lex - flex -olexor.c $(srcdir)/lexor.lex + $(LEX) -olexor.c $(srcdir)/lexor.lex install: all installdirs $(libdir)/ivl$(suffix)/ivlpp@EXEEXT@ diff --git a/vpi/Makefile.in b/vpi/Makefile.in index 166c8169e..c15bb3bbe 100644 --- a/vpi/Makefile.in +++ b/vpi/Makefile.in @@ -118,12 +118,12 @@ system.vpi: $O $(OPP) ../vvp/libvpi.a $(CXX) @shared@ -o $@ $O $(OPP) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) sys_readmem_lex.c: sys_readmem_lex.lex - flex -t -Preadmem $(srcdir)/sys_readmem_lex.lex > sys_readmem_lex.c + $(LEX) -t -Preadmem $(srcdir)/sys_readmem_lex.lex > sys_readmem_lex.c sdf_lexor.o: sdf_lexor.c sdf_parse.h sdf_lexor.c: sdf_lexor.lex - flex -t -Psdf $(srcdir)/sdf_lexor.lex > sdf_lexor.c + $(LEX) -t -Psdf $(srcdir)/sdf_lexor.lex > sdf_lexor.c sdf_parse.c sdf_parse.h: $(srcdir)/sdf_parse.y $(YACC) --verbose -d -p sdf -o sdf_parse.c $(srcdir)/sdf_parse.y From 7a473166d9814c5ef904d47a46e06643deb113c8 Mon Sep 17 00:00:00 2001 From: Cary R Date: Sun, 10 Apr 2011 17:16:25 -0700 Subject: [PATCH 16/19] Add the stochastic (queue) tasks/function This patch adds full support for the stochastic tasks/functions except the mean inter-arrival and average wait statistics are not currently available. These will be added in a later patch. This implementation goes a bit beyond the standard and supports the following: 1. The job and inform arguments support 32 bit four state values. 2. The id for all routines, the job and inform arguments for $q_add(), the statistic code for $q_exam() along with the queue type and maximum length arguments for $q_initialize() can be less than or equal to 32 bits. The argument will be sign extended if needed to fill the internal 32 bit value. 3. The job and inform arguments to $q_remove() and the status argument for all the routines must be 32 bits, but do not have to be an integer variable (e.g. a 32 bit register or part select is OK). 4. An undefined bit in the id argument for any of the routines will return a status of 2 (undefined queue id). Undefined bits are not automatically converted to zero. 5. Undefined bits in the $q_initialize() queue type and maximum length arguments or the $q_exam() statistic code argument are also flagged as an error (are not converted to zero). 6. The $q_full() function returns 2 on error, the other routines that return a value $q_remove() job/inform arguments and the $q_exam() statistic value argument will usually return x on error. 7. An invalid statistic code will set the $q_exam() status to 8. 8. The $q_exam() statistic value argument can be 32 bits or larger. This allows returning large statistical time values. 9. All time values are internally saved in simulation time units. They will be converted to the calling module's time unit (with rounding) before they are returned. 10. If a $q_exam() statistical value is too large to fit into the variable the maximum positive value will be returned and the status code will be set to 9 (value is too large). 11. If a statistical value is currently undefined $q_exam() will return 10 (no statistical information) (e.g. using code 5 on an empty queue). --- vpi/Makefile.in | 8 +- vpi/sys_icarus.c | 27 +- vpi/sys_queue.c | 1336 ++++++++++++++++++++++++++++++++++++++++++++++ vpi/sys_table.c | 2 + vpi/system.sft | 1 + 5 files changed, 1344 insertions(+), 30 deletions(-) create mode 100644 vpi/sys_queue.c diff --git a/vpi/Makefile.in b/vpi/Makefile.in index c15bb3bbe..31a6e54c7 100644 --- a/vpi/Makefile.in +++ b/vpi/Makefile.in @@ -53,10 +53,10 @@ LDFLAGS = @LDFLAGS@ # Object files for system.vpi O = sys_table.o sys_convert.o sys_deposit.o sys_display.o sys_fileio.o \ - sys_finish.o sys_icarus.o sys_plusargs.o sys_random.o sys_random_mti.o \ - sys_readmem.o sys_readmem_lex.o sys_scanf.o sys_sdf.o sys_time.o \ - sys_vcd.o sys_vcdoff.o vcd_priv.o mt19937int.o sys_priv.o sdf_lexor.o \ - sdf_parse.o stringheap.o vams_simparam.o + sys_finish.o sys_icarus.o sys_plusargs.o sys_queue.o sys_random.o \ + sys_random_mti.o sys_readmem.o sys_readmem_lex.o sys_scanf.o sys_sdf.o \ + sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o mt19937int.o sys_priv.o \ + sdf_lexor.o sdf_parse.o stringheap.o vams_simparam.o OPP = vcd_priv2.o ifeq (@HAVE_LIBZ@,yes) diff --git a/vpi/sys_icarus.c b/vpi/sys_icarus.c index 5d6d61f12..58e290dc6 100644 --- a/vpi/sys_icarus.c +++ b/vpi/sys_icarus.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2010 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2008-2011 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -190,31 +190,6 @@ void sys_special_register(void) res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); - tf_data.tfname = "$q_initialize"; - tf_data.user_data = "$q_initialize"; - res = vpi_register_systf(&tf_data); - vpip_make_systf_system_defined(res); - - tf_data.tfname = "$q_add"; - tf_data.user_data = "$q_add"; - res = vpi_register_systf(&tf_data); - vpip_make_systf_system_defined(res); - - tf_data.tfname = "$q_remove"; - tf_data.user_data = "$q_remove"; - res = vpi_register_systf(&tf_data); - vpip_make_systf_system_defined(res); - - tf_data.tfname = "$q_full"; - tf_data.user_data = "$q_full"; - res = vpi_register_systf(&tf_data); - vpip_make_systf_system_defined(res); - - tf_data.tfname = "$q_exam"; - tf_data.user_data = "$q_exam"; - res = vpi_register_systf(&tf_data); - vpip_make_systf_system_defined(res); - tf_data.tfname = "$dumpports"; tf_data.user_data = "$dumpports"; res = vpi_register_systf(&tf_data); diff --git a/vpi/sys_queue.c b/vpi/sys_queue.c new file mode 100644 index 000000000..2752fdbdf --- /dev/null +++ b/vpi/sys_queue.c @@ -0,0 +1,1336 @@ +/* + * Copyright (C) 2011 Cary R. (cygcary@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "sys_priv.h" +#include +#include +#include + +/* + * The two queue types. + */ +#define IVL_QUEUE_FIFO 1 +#define IVL_QUEUE_LIFO 2 + +/* + * The statistical codes that can be passed to $q_exam(). + */ +#define IVL_QUEUE_LENGTH 1 +#define IVL_QUEUE_MEAN 2 +#define IVL_QUEUE_MAX_LENGTH 3 +#define IVL_QUEUE_SHORTEST 4 +#define IVL_QUEUE_LONGEST 5 +#define IVL_QUEUE_AVERAGE 6 + +/* + * All the values that can be returned by the queue tasks/function. + */ +#define IVL_QUEUE_OK 0 +#define IVL_QUEUE_FULL 1 +#define IVL_QUEUE_UNDEFINED_ID 2 +#define IVL_QUEUE_EMPTY 3 +#define IVL_QUEUE_UNSUPPORTED_TYPE 4 +#define IVL_QUEUE_INVALID_LENGTH 5 +#define IVL_QUEUE_DUPLICATE_ID 6 +#define IVL_QUEUE_OUT_OF_MEMORY 7 +/* Icarus specific status codes. */ +#define IVL_QUEUE_UNDEFINED_STAT_CODE 8 +#define IVL_QUEUE_VALUE_OVERFLOWED 9 +#define IVL_QUEUE_NO_STATISTICS 10 + +/* + * The data structure used for an individual queue element. It hold four + * state result for the jobs and inform fields along with the time that + * the element was added in base time units. + */ +typedef struct t_ivl_queue_elem { + uint64_t time; + s_vpi_vecval job; + s_vpi_vecval inform; +} s_ivl_queue_elem, *p_ivl_queue_elem; + +/* + * This structure is used to represent a specific queue. The time + * information is in base simulation units. + */ +typedef struct t_ivl_queue_base { + uint64_t shortest_wait_time; + p_ivl_queue_elem queue; + PLI_INT32 id; + PLI_INT32 length; + PLI_INT32 type; + PLI_INT32 head; + PLI_INT32 elems; + PLI_INT32 max_len; + PLI_INT32 have_statistics; +// HERE: Still need information for the other statistical routines. +} s_ivl_queue_base, *p_ivl_queue_base; + +/* + * For now we keep the queues in a vector since there are likely not too many + * of them. We may need something more efficient later. + */ +static p_ivl_queue_base base = NULL; +static int64_t base_len = 0; + +/* + * This routine is called at the end of simulation to free the queue memory. + */ +static PLI_INT32 cleanup_queue(p_cb_data cause) +{ + PLI_INT32 idx; + (void) cause; /* Unused argument. */ + for (idx = 0; idx < base_len; idx += 1) free(base[idx].queue); + free(base); + base = NULL; + base_len = 0; + return 0; +} + +/* + * Add a new queue to the list, return 1 if there is not enough memory, + * otherwise return 0. + */ +static unsigned create_queue(PLI_INT32 id, PLI_INT32 type, PLI_INT32 length) +{ + p_ivl_queue_base new_base; + p_ivl_queue_elem queue; + + /* Allocate space for the new queue base. */ + base_len += 1; + new_base = (p_ivl_queue_base) realloc(base, + base_len*sizeof(s_ivl_queue_base)); + + /* If we ran out of memory then fix the length and return a fail. */ + if (new_base == NULL) { + base_len -= 1; + return 1; + } + base = new_base; + + /* Allocate space for the queue elements. */ + queue = (p_ivl_queue_elem) malloc(length*sizeof(s_ivl_queue_elem)); + + /* If we ran out of memory then fix the length and return a fail. */ + if (queue == NULL) { + base_len -= 1; + return 1; + } + + /* The memory was allocated so configure it. */ + base[base_len-1].queue = queue; + base[base_len-1].id = id; + base[base_len-1].length = length; + base[base_len-1].type = type; + base[base_len-1].head = 0; + base[base_len-1].elems = 0; + base[base_len-1].max_len = 0; + base[base_len-1].shortest_wait_time = UINT64_MAX; + base[base_len-1].have_statistics = 0; + return 0; +} + +/* + * Check to see if the given queue is full. + */ +static unsigned is_queue_full(int64_t idx) +{ + if (base[idx].elems >= base[idx].length) return 1; + + return 0; +} + +/* + * Add the job and inform to the queue. Return 1 if the queue is full, + * otherwise return 0. + */ +static unsigned add_to_queue(int64_t idx, p_vpi_vecval job, + p_vpi_vecval inform) +{ + PLI_INT32 length = base[idx].length; + PLI_INT32 type = base[idx].type; + PLI_INT32 head = base[idx].head; + PLI_INT32 elems = base[idx].elems; + PLI_INT32 loc; + s_vpi_time cur_time; + uint64_t time; + + assert(elems <= length); + + /* If the queue is full we can't add anything. */ + if (elems == length) return 1; + + /* Increment the number of element since one will be added.*/ + base[idx].elems += 1; + + /* Save the job and inform to the queue. */ + if (type == IVL_QUEUE_LIFO) { + assert(head == 0); /* For a LIFO head must always be zero. */ + loc = elems; + } else { + assert(type == IVL_QUEUE_FIFO); + loc = head + elems; + if (loc >= length) loc -= length; + } + base[idx].queue[loc].job.aval = job->aval; + base[idx].queue[loc].job.bval = job->bval; + base[idx].queue[loc].inform.aval = inform->aval; + base[idx].queue[loc].inform.bval = inform->bval; + + /* Save the current time with this entry for the statistics. */ + cur_time.type = vpiSimTime; + vpi_get_time(NULL, &cur_time); + time = cur_time.high; + time <<= 32; + time |= cur_time.low; + base[idx].queue[loc].time = time; + + /* Increment the maximum length if needed. */ + if (base[idx].max_len == elems) base[idx].max_len += 1; + + return 0; +} + +/* + * Get the job and inform values from the queue. Return 1 if the queue is + * empty, otherwise return 0. + */ +static unsigned remove_from_queue(int64_t idx, p_vpi_vecval job, + p_vpi_vecval inform) +{ + PLI_INT32 type = base[idx].type; + PLI_INT32 head = base[idx].head; + PLI_INT32 elems = base[idx].elems - 1; + PLI_INT32 loc; + s_vpi_time cur_time; + uint64_t time; + + assert(elems >= -1); + + /* If the queue is empty we can't remove anything. */ + if (elems < 0) return 1; + + /* Decrement the number of element in the queue structure since one + * will be removed.*/ + base[idx].elems -= 1; + + /* Remove the job and inform from the queue. */ + if (type == IVL_QUEUE_LIFO) { + assert(head == 0); /* For a LIFO head must always be zero. */ + loc = elems; + } else { + assert(type == IVL_QUEUE_FIFO); + loc = head; + if (head + 1 == base[idx].length) base[idx].head = 0; + else base[idx].head += 1; + } + job->aval = base[idx].queue[loc].job.aval; + job->bval = base[idx].queue[loc].job.bval; + inform->aval = base[idx].queue[loc].inform.aval; + inform->bval = base[idx].queue[loc].inform.bval; + + /* Get the current simulation time. */ + cur_time.type = vpiSimTime; + vpi_get_time(NULL, &cur_time); + time = cur_time.high; + time <<= 32; + time |= cur_time.low; + + /* Set the shortest wait time if needed. */ + assert(time >= base[idx].queue[loc].time); + time -= base[idx].queue[loc].time; + if (time < base[idx].shortest_wait_time) { + base[idx].shortest_wait_time = time; + } + base[idx].have_statistics = 1; + + return 0; +} + +/* + * Return the current queue length. + */ +static PLI_INT32 get_current_queue_length(int64_t idx) +{ + return base[idx].elems; +} + +/* + * Return the maximum queue length. + */ +static PLI_INT32 get_maximum_queue_length(int64_t idx) +{ + return base[idx].max_len; +} + +/* + * Return the longest wait time in the queue in base simulation units. + * Make sure to check that there are elements in the queue before calling + * this routine. The caller will need to scale the time as appropriate. + */ +static uint64_t get_longest_queue_time(int64_t idx) +{ + s_vpi_time cur_time; + uint64_t time; + + /* Get the current simulation time. */ + cur_time.type = vpiSimTime; + vpi_get_time(NULL, &cur_time); + time = cur_time.high; + time <<= 32; + time |= cur_time.low; + + /* Subtract the element with the longest time (the head) from the + * current time. */ + time -= base[idx].queue[base[idx].head].time; + + return time; +} + +/* + * Check to see if there are statistics. + */ +static PLI_INT32 have_statistics(int64_t idx) +{ + return base[idx].have_statistics; +} + +/* + * Return the mean inter-arrival time for the queue. + */ +#if 0 +static uint64_t get_mean_interarrival_time(int64_t idx) +{ +// HERE: Need to save the information and calculate the mean interarrival time. + return 0; +} +#endif + +/* + * Return the shortest amount of time an element has waited in the queue. + */ +static uint64_t get_shortest_wait_time(int64_t idx) +{ + return base[idx].shortest_wait_time; +} + +/* + * Return the average wait time in the queue. + */ +#if 0 +static uint64_t get_average_wait_time(int64_t idx) +{ +// HERE: Need to save the information and calculate the average wait time. + return 0; +} +#endif + +/* + * Check to see if the given id already exists. Return the index for the + * queue if it exists, otherwise return -1. + */ +static int64_t get_id_index(PLI_INT32 id) +{ + int64_t idx; + + for (idx = 0; idx < base_len; idx += 1) { + if (id == base[idx].id) return idx; + } + + return -1; +} + +/* + * Check to see if the given value is bit based and has 32 or fewer bits. + */ +static unsigned is_32_or_smaller_obj(vpiHandle obj) +{ + PLI_INT32 const_type; + unsigned rtn = 0; + + assert(obj); + + switch(vpi_get(vpiType, obj)) { + case vpiConstant: + case vpiParameter: + const_type = vpi_get(vpiConstType, obj); + if ((const_type != vpiRealConst) && + (const_type != vpiStringConst)) rtn = 1; + break; + + /* These can have valid 32 bit or smaller numeric values. */ + case vpiIntegerVar: + case vpiMemoryWord: + case vpiNet: + case vpiPartSelect: + case vpiReg: + rtn = 1; + break; + } + + /* The object must be 32 bits or smaller. */ + if (vpi_get(vpiSize, obj) > 32) rtn = 0; + + return rtn; +} + +/* + * Check to see if the argument is a variable that is exactly 32 bits in size. + */ +static void check_var_arg_32(vpiHandle arg, vpiHandle callh, + const char *name, const char *desc) +{ + assert(arg); + + switch (vpi_get(vpiType, arg)) { + case vpiMemoryWord: + case vpiPartSelect: + case vpiReg: // Check that we have exactly 32 bits. + if (vpi_get(vpiSize, arg) != 32) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s (variable) argument must be 32 bits.\n", + name, desc); + vpi_control(vpiFinish, 1); + } + case vpiIntegerVar: + break; + default: + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s argument must be a 32 bit variable.\n", + name, desc); + vpi_control(vpiFinish, 1); + } +} + +/* + * Check to see if the argument is a variable of atleast 32 bits. + */ +static void check_var_arg_large(vpiHandle arg, vpiHandle callh, + const char *name, const char *desc) +{ + assert(arg); + + switch (vpi_get(vpiType, arg)) { + case vpiMemoryWord: + case vpiPartSelect: + case vpiReg: // Check that we have at least 32 bits. + if (vpi_get(vpiSize, arg) < 32) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s (variable) argument must have at least " + "32 bits.\n", name, desc); + vpi_control(vpiFinish, 1); + } + case vpiIntegerVar: + case vpiTimeVar: + break; + default: + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s argument must be a variable.\n", name, desc); + vpi_control(vpiFinish, 1); + } +} + +/* + * Check that the given number of arguments are numeric. + */ +static unsigned check_numeric_args(vpiHandle argv, unsigned count, + vpiHandle callh, const char *name) +{ + unsigned idx; + + /* Check that the first count arguments are numeric. Currently + * only three are needed/supported. */ + for (idx = 0; idx < count; idx += 1) { + char *loc; + vpiHandle arg = vpi_scan(argv); + + /* Get the name for this argument. */ + switch (idx) { + case 0: loc = "first"; break; + case 1: loc = "second"; break; + case 2: loc = "third"; break; + default: assert(0); + } + + /* Check that there actually is an argument. */ + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a %s (<= 32 bit numeric) argument.\n", + name, loc); + vpi_control(vpiFinish, 1); + return 1; + } + + /* Check that it is no more than 32 bits. */ + if (! is_32_or_smaller_obj(arg)) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s argument must be numeric (<= 32 bits).\n", + name, loc); + vpi_control(vpiFinish, 1); + } + } + + return 0; +} + +/* + * Check to see if the given argument is valid (does not have any X/Z bits). + * Return zero if it is valid and a positive value if it is invalid. + */ +static unsigned get_valid_32(vpiHandle arg, PLI_INT32 *value) +{ + PLI_INT32 size, mask; + s_vpi_value val; + + size = vpi_get(vpiSize, arg); + /* The compiletf routine should have already verified that this is + * <= 32 bits. */ + assert((size <= 32) && (size > 0)); + + /* Create a mask so that we only check the appropriate bits. */ + mask = UINT32_MAX >> (32 - size); + + /* Get the value and return the possible integer value in the value + * variable. Return the b-value bits to indicate if the value is + * undefined (has X/Z bit). */ + val.format = vpiVectorVal; + vpi_get_value(arg, &val); + + *value = val.value.vector->aval & mask; + /* If the argument is signed and less than 32 bit we need to sign + * extend the value. */ + if (vpi_get(vpiSigned, arg) && (size < 32)) { + if ((*value) & (1 << (size - 1))) *value |= ~mask; + } + return (val.value.vector->bval & mask); +} + +static void get_four_state(vpiHandle arg, p_vpi_vecval vec) +{ + PLI_INT32 size, mask; + s_vpi_value val; + + size = vpi_get(vpiSize, arg); + /* The compiletf routine should have already verified that this is + * <= 32 bits. */ + assert((size <= 32) && (size > 0)); + + /* Create a mask so that we only use the appropriate bits. */ + mask = UINT32_MAX >> (32 - size); + + /* Get the bits for the argument and save them in the return value. */ + val.format = vpiVectorVal; + vpi_get_value(arg, &val); + vec->aval = val.value.vector->aval & mask; + vec->bval = val.value.vector->bval & mask; + + /* If the argument is signed and less than 32 bit we need to sign + * extend the value. */ + if (vpi_get(vpiSigned, arg) && (size < 32)) { + if (vec->aval & (1 << (size - 1))) vec->aval |= ~mask; + if (vec->bval & (1 << (size - 1))) vec->bval |= ~mask; + } +} + +/* + * Fill the passed variable with x. + */ +static void fill_variable_with_x(vpiHandle var) +{ + s_vpi_value val; + PLI_INT32 words = ((vpi_get(vpiSize, var) - 1) / 32) + 1; + PLI_INT32 idx; + p_vpi_vecval val_ptr = (p_vpi_vecval) malloc(words*sizeof(s_vpi_vecval)); + + assert(val_ptr); + + /* Fill the vector with X. */ + for (idx = 0; idx < words; idx += 1) { + val_ptr[idx].aval = 0xffffffff; + val_ptr[idx].bval = 0xffffffff; + } + + /* Put the vector to the variable. */ + val.format = vpiVectorVal; + val.value.vector = val_ptr; + vpi_put_value(var, &val, 0, vpiNoDelay); + free(val_ptr); +} + +/* + * Fill the passed variable with the passed value if it fits. If it doesn't + * fit then set all bits to one and return that the value is too big instead + * of the normal OK. The value is a time and needs to be scaled to the + * calling module's timescale. + */ +static PLI_INT32 fill_variable_with_scaled_time(vpiHandle var, uint64_t time) +{ + s_vpi_value val; + PLI_INT32 size = vpi_get(vpiSize, var); + PLI_INT32 is_signed = vpi_get(vpiSigned, var); + PLI_INT32 words = ((size - 1) / 32) + 1; + uint64_t max_val = 0; + uint64_t scale = 1; + uint64_t frac; + PLI_INT32 rtn, idx, units, prec; + p_vpi_vecval val_ptr = (p_vpi_vecval) malloc(words*sizeof(s_vpi_vecval)); + + assert(val_ptr); + assert(size >= 32); + assert(words > 0); + + /* Scale the variable to match the calling module's timescale. */ + prec = vpi_get(vpiTimePrecision, 0); + units = vpi_get(vpiTimeUnit, vpi_handle(vpiModule, var)); + assert(units >= prec); + while (units > prec) { + scale *= 10; + units -= 1; + } + frac = time % scale; + time /= scale; + if ((scale > 1) && (frac >= scale/2)) time += 1; + + /* Find the maximum value + 1 that can be put into the variable. */ + if (size < 64) { + max_val = 1; + max_val <<= (size - is_signed); + } + + /* If the time is too big to fit then return the maximum positive + * value and that the value overflowed. Otherwise, return the time + * and OK. */ + if (max_val && (time >= max_val)) { + /* For a single word only the MSB is cleared if signed. */ + if (words == 1) { + if (is_signed) { + val_ptr[0].aval = 0x7fffffff; + } else { + val_ptr[0].aval = 0xffffffff; + } + val_ptr[0].bval = 0x00000000; + /* For two words the lower word is filled with 1 and the top + * word has a size dependet fill if signed. */ + } else { + assert(words == 2); + val_ptr[0].aval = 0xffffffff; + val_ptr[0].bval = 0x00000000; + if (is_signed) { + val_ptr[1].aval = ~(UINT32_MAX >> (size - 32)); + } else { + val_ptr[1].aval = 0xffffffff; + } + val_ptr[1].bval = 0x00000000; + } + rtn = IVL_QUEUE_VALUE_OVERFLOWED; + } else { + /* Fill the vector with 0. */ + for (idx = 0; idx < words; idx += 1) { + val_ptr[idx].aval = 0x00000000; + val_ptr[idx].bval = 0x00000000; + } + /* Add the time to the vector. */ + switch (words) { + default: + val_ptr[1].aval = (time >> 32) & 0xffffffff; + case 1: + val_ptr[0].aval = time & 0xffffffff; + } + rtn = IVL_QUEUE_OK; + } + + /* Put the vector to the variable. */ + val.format = vpiVectorVal; + val.value.vector = val_ptr; + vpi_put_value(var, &val, 0, vpiNoDelay); + free(val_ptr); + + return rtn; +} + +/* + * Check that the given $q_initialize() call has valid arguments. + */ +static PLI_INT32 sys_q_initialize_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires four arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* Check that the first three arguments (the id, type and maximum + * length) are numeric. */ + if (check_numeric_args(argv, 3, callh, name)) return 0; + + /* The fourth argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a fourth (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "fourth"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "four arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_initialize(). + */ +static PLI_INT32 sys_q_initialize_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle status; + PLI_INT32 id, type, length; + s_vpi_value val; + unsigned invalid_id, invalid_type, invalid_length; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the queue type. */ + invalid_type = get_valid_32(vpi_scan(argv), &type); + + /* Get the queue maximum length. */ + invalid_length = get_valid_32(vpi_scan(argv), &length); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* If the id is invalid then return. */ + if (invalid_id) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Verify that the type is valid. */ + if (invalid_type || ((type != IVL_QUEUE_FIFO) && + (type != IVL_QUEUE_LIFO))) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNSUPPORTED_TYPE; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Verify that the queue length is greater than zero. */ + if (invalid_length || (length <= 0)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_INVALID_LENGTH; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Check that this is not a duplicate queue id. */ + if (get_id_index(id) >= 0) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_DUPLICATE_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Create the queue and fail if we do not have enough memory. */ + if (create_queue(id, type, length)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OUT_OF_MEMORY; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* The queue was initialized correctly so return OK. */ + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OK; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Check that the given $q_add() call has valid arguments. + */ +static PLI_INT32 sys_q_add_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires four arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* Check that the first three arguments (the id, job and information) + * are numeric. */ + if (check_numeric_args(argv, 3, callh, name)) return 0; + + /* The fourth argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a fourth (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "fourth"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "four arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_add(). + */ +static PLI_INT32 sys_q_add_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle status; + PLI_INT32 id; + int64_t idx; + s_vpi_vecval job, inform; + s_vpi_value val; + unsigned invalid_id; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the job. */ + get_four_state(vpi_scan(argv), &job); + + /* Get the value. */ + get_four_state(vpi_scan(argv), &inform); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* Verify that the id is valid. */ + idx = get_id_index(id); + if (invalid_id || (idx < 0)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Add the data to the queue if it is not already full. */ + if (add_to_queue(idx, &job, &inform)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_FULL; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* The data was added to the queue so return OK. */ + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OK; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Check that the given $q_remove() call has valid arguments. + */ +static PLI_INT32 sys_q_remove_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires four arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* The first argument (the id) must be numeric. */ + if (! is_32_or_smaller_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's first argument must be numeric (<= 32 bits).\n", + name); + vpi_control(vpiFinish, 1); + } + + /* The second argument (the job id) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a second (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the job id argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "second"); + + /* The third argument (the information id) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a third (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the information id argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "third"); + + /* The fourth argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a fourth (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "fourth"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "four arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_remove(). + */ +static PLI_INT32 sys_q_remove_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle job, inform, status; + PLI_INT32 id, idx; + s_vpi_vecval job_val, inform_val; + s_vpi_value val; + unsigned invalid_id; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the job variable. */ + job = vpi_scan(argv); + + /* Get the inform variable. */ + inform = vpi_scan(argv); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* Verify that the id is valid. */ + idx = get_id_index(id); + if (invalid_id || (idx < 0)) { + fill_variable_with_x(job); + fill_variable_with_x(inform); + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Remove the data from the queue if it is not already empty. */ + if (remove_from_queue(idx, &job_val, &inform_val)) { + fill_variable_with_x(job); + fill_variable_with_x(inform); + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_EMPTY; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + val.format = vpiVectorVal; + val.value.vector = &job_val; + vpi_put_value(job, &val, 0, vpiNoDelay); + val.format = vpiVectorVal; + val.value.vector = &inform_val; + vpi_put_value(inform, &val, 0, vpiNoDelay); + + /* The data was added to the queue so return OK. */ + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OK; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Check that the given $q_full() call has valid arguments. + */ +static PLI_INT32 sys_q_full_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires two arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* The first argument (the id) must be numeric. */ + if (! is_32_or_smaller_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's first argument must be numeric (<= 32 bits).\n", + name); + vpi_control(vpiFinish, 1); + } + + /* The second argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a second (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "second"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "two arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_full(). + */ +static PLI_INT32 sys_q_full_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle status; + PLI_INT32 id, idx; + s_vpi_value val; + unsigned invalid_id; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* Verify that the id is valid. */ + idx = get_id_index(id); + if (invalid_id || (idx < 0)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + val.format = vpiIntVal; + val.value.integer = 2; /* An error value. */ + vpi_put_value(callh, &val, 0, vpiNoDelay); + return 0; + } + + /* Get the queue state and return it. */ + val.format = vpiIntVal; + if (is_queue_full(idx)) val.value.integer = 1; + else val.value.integer = 0; + vpi_put_value(callh, &val, 0, vpiNoDelay); + + /* The queue state was passed back so return OK. */ + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OK; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Check that the given $q_exam() call has valid arguments. + */ +static PLI_INT32 sys_q_exam_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires four arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* Check that the first two arguments (the id and code) are numeric. */ + if (check_numeric_args(argv, 2, callh, name)) return 0; + + /* The third argument (the value) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a third (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the value argument is a variable with at least + * 32 bits. */ + check_var_arg_large(arg, callh, name, "third"); + + /* The fourth argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a fourth (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "fourth"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "two arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_exam(). + */ +static PLI_INT32 sys_q_exam_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle value, status; + PLI_INT32 id, code, idx, rtn; + s_vpi_value val; + unsigned invalid_id, invalid_code; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the code. */ + invalid_code = get_valid_32(vpi_scan(argv), &code); + + /* Get the value variable. */ + value = vpi_scan(argv); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* Verify that the id is valid. */ + idx = get_id_index(id); + if (invalid_id || (idx < 0)) { + fill_variable_with_x(value); + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Verify that the code is valid. */ + if (invalid_code || (code <= 0) || (code > 6)) { + fill_variable_with_x(value); + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_STAT_CODE; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + rtn = IVL_QUEUE_OK; + + /* Calculate the requested queue information. */ + switch (code) { + case IVL_QUEUE_LENGTH: + val.format = vpiIntVal; + val.value.integer = get_current_queue_length(idx); + vpi_put_value(value, &val, 0, vpiNoDelay); + break; + case IVL_QUEUE_MEAN: + if (have_statistics(idx) == 0) { + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } else { +// HERE: For now this is not supported. The get routine always returns 0. +#if 0 + uint64_t time = get_mean_interarrival_time(idx); + rtn = fill_variable_with_scaled_time(value, time); +#endif + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } + break; + case IVL_QUEUE_MAX_LENGTH: + val.format = vpiIntVal; + val.value.integer = get_maximum_queue_length(idx); + vpi_put_value(value, &val, 0, vpiNoDelay); + break; + case IVL_QUEUE_SHORTEST: + if (have_statistics(idx) == 0) { + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } else { + uint64_t time = get_shortest_wait_time(idx); + rtn = fill_variable_with_scaled_time(value, time); + } + break; + case IVL_QUEUE_LONGEST: + if (get_current_queue_length(idx) == 0) { + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } else { + uint64_t time = get_longest_queue_time(idx); + rtn = fill_variable_with_scaled_time(value, time); + } + break; + case IVL_QUEUE_AVERAGE: + if (have_statistics(idx) == 0) { + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } else { +// HERE: For now this is not supported. The get routine always returns 0. +#if 0 + uint64_t time = get_average_wait_time(idx); + rtn = fill_variable_with_scaled_time(value, time); +#endif + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } + break; + default: + assert(0); + } + + /* The queue information was passed back so now return the status. */ + val.format = vpiIntVal; + val.value.integer = rtn; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Routine to register the system tasks/functions provided in this file. + */ +void sys_queue_register() +{ + s_vpi_systf_data tf_data; + s_cb_data cb; + vpiHandle res; + + tf_data.type = vpiSysTask; + tf_data.tfname = "$q_initialize"; + tf_data.calltf = sys_q_initialize_calltf; + tf_data.compiletf = sys_q_initialize_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$q_initialize"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysTask; + tf_data.tfname = "$q_add"; + tf_data.calltf = sys_q_add_calltf; + tf_data.compiletf = sys_q_add_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$q_add"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysTask; + tf_data.tfname = "$q_remove"; + tf_data.calltf = sys_q_remove_calltf; + tf_data.compiletf = sys_q_remove_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$q_remove"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiSysFuncInt; + tf_data.tfname = "$q_full"; + tf_data.calltf = sys_q_full_calltf; + tf_data.compiletf = sys_q_full_compiletf; + tf_data.sizetf = 0; /* Not needed for a vpiSysFuncInt. */ + tf_data.user_data = "$q_full"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysTask; + tf_data.tfname = "$q_exam"; + tf_data.calltf = sys_q_exam_calltf; + tf_data.compiletf = sys_q_exam_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$q_exam"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + /* Create a callback to clear all the queue memory when the + * simulator finishes. */ + cb.time = NULL; + cb.reason = cbEndOfSimulation; + cb.cb_rtn = cleanup_queue; + cb.user_data = 0x0; + cb.obj = 0x0; + + vpi_register_cb(&cb); +} diff --git a/vpi/sys_table.c b/vpi/sys_table.c index 637319e0f..9116d34c6 100644 --- a/vpi/sys_table.c +++ b/vpi/sys_table.c @@ -29,6 +29,7 @@ extern void sys_finish_register(); extern void sys_deposit_register(); extern void sys_display_register(); extern void sys_plusargs_register(); +extern void sys_queue_register(); extern void sys_random_register(); extern void sys_random_mti_register(); extern void sys_readmem_register(); @@ -198,6 +199,7 @@ void (*vlog_startup_routines[])() = { sys_deposit_register, sys_display_register, sys_plusargs_register, + sys_queue_register, sys_random_register, sys_random_mti_register, sys_readmem_register, diff --git a/vpi/system.sft b/vpi/system.sft index f0eb86f6a..b772dc85c 100644 --- a/vpi/system.sft +++ b/vpi/system.sft @@ -15,6 +15,7 @@ $dist_chi_square vpiSysFuncInt $dist_t vpiSysFuncInt $dist_erlang vpiSysFuncInt $clog2 vpiSysFuncInt +$q_full vpiSysFuncInt $abstime vpiSysFuncReal $simparam vpiSysFuncReal From 691113208f3e25c932e14e01c2e5aaa07139583f Mon Sep 17 00:00:00 2001 From: Cary R Date: Tue, 12 Apr 2011 09:45:47 -0700 Subject: [PATCH 17/19] Add support for calculating the queue mean inter-arrival time. This patch adds support for calculating the queue mean inter-arrival time. This is just the latest add time minus the first add time divided by the number of intervals (the number of adds minus one). --- vpi/sys_queue.c | 49 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/vpi/sys_queue.c b/vpi/sys_queue.c index 2752fdbdf..44f21f9f8 100644 --- a/vpi/sys_queue.c +++ b/vpi/sys_queue.c @@ -70,6 +70,10 @@ typedef struct t_ivl_queue_elem { */ typedef struct t_ivl_queue_base { uint64_t shortest_wait_time; + uint64_t first_add_time; + uint64_t latest_add_time; + uint64_t number_of_adds; +// HERE: Still need information for the average wait time statistic. p_ivl_queue_elem queue; PLI_INT32 id; PLI_INT32 length; @@ -78,7 +82,6 @@ typedef struct t_ivl_queue_base { PLI_INT32 elems; PLI_INT32 max_len; PLI_INT32 have_statistics; -// HERE: Still need information for the other statistical routines. } s_ivl_queue_base, *p_ivl_queue_base; /* @@ -141,6 +144,9 @@ static unsigned create_queue(PLI_INT32 id, PLI_INT32 type, PLI_INT32 length) base[base_len-1].elems = 0; base[base_len-1].max_len = 0; base[base_len-1].shortest_wait_time = UINT64_MAX; + base[base_len-1].first_add_time = 0U; + base[base_len-1].latest_add_time = 0U; + base[base_len-1].number_of_adds = 0U; base[base_len-1].have_statistics = 0; return 0; } @@ -203,6 +209,12 @@ static unsigned add_to_queue(int64_t idx, p_vpi_vecval job, /* Increment the maximum length if needed. */ if (base[idx].max_len == elems) base[idx].max_len += 1; + /* Update the inter-arrivial statistics. */ + assert(base[idx].number_of_adds < UINT64_MAX); + base[idx].number_of_adds += 1; + if (base[idx].number_of_adds == 1) base[idx].first_add_time = time; + base[idx].latest_add_time = time; + return 0; } @@ -305,21 +317,29 @@ static uint64_t get_longest_queue_time(int64_t idx) /* * Check to see if there are statistics. */ -static PLI_INT32 have_statistics(int64_t idx) +static unsigned have_statistics(int64_t idx) { - return base[idx].have_statistics; + return (base[idx].have_statistics != 0); } /* - * Return the mean inter-arrival time for the queue. + * Check to see if we have inter-arrival statistics. + */ +static unsigned have_interarrival_statistic(int64_t idx) +{ + return (base[idx].number_of_adds >= 2U); +} + +/* + * Return the mean inter-arrival time for the queue. This is just the + * latest add time minus the first add time divided be the number of time + * deltas (the number of adds - 1). */ -#if 0 static uint64_t get_mean_interarrival_time(int64_t idx) { -// HERE: Need to save the information and calculate the mean interarrival time. - return 0; + return ((base[idx].latest_add_time - base[idx].first_add_time) / + (base[idx].number_of_adds - 1U)); } -#endif /* * Return the shortest amount of time an element has waited in the queue. @@ -1202,30 +1222,29 @@ static PLI_INT32 sys_q_exam_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) /* Calculate the requested queue information. */ switch (code) { + /* The current queue length. */ case IVL_QUEUE_LENGTH: val.format = vpiIntVal; val.value.integer = get_current_queue_length(idx); vpi_put_value(value, &val, 0, vpiNoDelay); break; + /* The mean inter-arrival time. */ case IVL_QUEUE_MEAN: - if (have_statistics(idx) == 0) { + if (have_interarrival_statistic(idx) == 0) { fill_variable_with_x(value); rtn = IVL_QUEUE_NO_STATISTICS; } else { -// HERE: For now this is not supported. The get routine always returns 0. -#if 0 uint64_t time = get_mean_interarrival_time(idx); rtn = fill_variable_with_scaled_time(value, time); -#endif - fill_variable_with_x(value); - rtn = IVL_QUEUE_NO_STATISTICS; } break; + /* The maximum queue length. */ case IVL_QUEUE_MAX_LENGTH: val.format = vpiIntVal; val.value.integer = get_maximum_queue_length(idx); vpi_put_value(value, &val, 0, vpiNoDelay); break; + /* The shortest queue wait time ever. */ case IVL_QUEUE_SHORTEST: if (have_statistics(idx) == 0) { fill_variable_with_x(value); @@ -1235,6 +1254,7 @@ static PLI_INT32 sys_q_exam_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) rtn = fill_variable_with_scaled_time(value, time); } break; + /* The longest wait time for elements still in the queue. */ case IVL_QUEUE_LONGEST: if (get_current_queue_length(idx) == 0) { fill_variable_with_x(value); @@ -1244,6 +1264,7 @@ static PLI_INT32 sys_q_exam_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) rtn = fill_variable_with_scaled_time(value, time); } break; + /* The average queue wait time. */ case IVL_QUEUE_AVERAGE: if (have_statistics(idx) == 0) { fill_variable_with_x(value); From 2517ba904149b426f6dcfa92e1f6c86ff78723be Mon Sep 17 00:00:00 2001 From: Cary R Date: Sun, 10 Apr 2011 20:58:34 -0700 Subject: [PATCH 18/19] Fix a few problems with the power operator. The power operator defines 2**-1 and -2**-1 to be zero. This patch fixes both the procedural and continuous assignments to work correctly. It also fixes a problem in the compiler power code so that the one constant value always has at least two bits. --- verinum.cc | 12 +++++++----- vvp/arith.cc | 9 ++++++--- vvp/vthread.cc | 7 +++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/verinum.cc b/verinum.cc index 0c4629e4a..143c0cac9 100644 --- a/verinum.cc +++ b/verinum.cc @@ -1089,15 +1089,17 @@ verinum pow(const verinum&left, const verinum&right) long pow_count = right.as_long(); // We need positive and negative one in a few places. - verinum one (verinum::V0, left.len(), left.has_len()); + unsigned len = left.len(); + // Positive one must be at least two bits wide! + verinum one (verinum::V0, (len<2) ? 2 : len, left.has_len()); one.has_sign(left.has_sign()); one.set(0, verinum::V1); - verinum m_one (verinum::V1, left.len(), left.has_len()); + verinum m_one (verinum::V1, len, left.has_len()); m_one.has_sign(true); // If either the right or left values are undefined we return 'bx. if (!right.is_defined() || !left.is_defined()) { - result = verinum(verinum::Vx, left.len(), left.has_len()); + result = verinum(verinum::Vx, len, left.has_len()); result.has_sign(left.has_sign()); // If the right value is zero we need to set the result to 1. } else if (pow_count == 0) { @@ -1105,7 +1107,7 @@ verinum pow(const verinum&left, const verinum&right) } else if (pow_count < 0) { // 0 ** is 'bx. if (left.is_zero()) { - result = verinum(verinum::Vx, left.len(), left.has_len()); + result = verinum(verinum::Vx, len, left.has_len()); result.has_sign(left.has_sign()); // 1 ** is 1. } else if (left == one) { @@ -1119,7 +1121,7 @@ verinum pow(const verinum&left, const verinum&right) } // Everything else is 0. } else { - result = verinum(verinum::V0, left.len(), left.has_len()); + result = verinum(verinum::V0, len, left.has_len()); result.has_sign(left.has_sign()); } } diff --git a/vvp/arith.cc b/vvp/arith.cc index 842e58d75..7ab6f3c80 100644 --- a/vvp/arith.cc +++ b/vvp/arith.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 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 @@ -460,11 +460,14 @@ void vvp_arith_pow::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, return; } - double ad, bd; + double ad, bd, resd; vector4_to_value(op_a_, ad, true); vector4_to_value(op_b_, bd, true); + /* 2**-1 and -2**-1 are defined to be zero. */ + if ((bd == -1) && (fabs(ad) == 2.0)) resd = 0.0; + else resd = pow(ad, bd); - res4 = vvp_vector4_t(wid_, pow(ad, bd)); + res4 = vvp_vector4_t(wid_, resd); } else { vvp_vector2_t a2 (op_a_); vvp_vector2_t b2 (op_b_); diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 73938264a..e4c0b2b22 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -4050,10 +4050,13 @@ bool of_POW_S(vthread_t thr, vvp_code_t cp) } /* Calculate the result using the double pow() function. */ - double xd, yd; + double xd, yd, resd; vector4_to_value(xv, xd, true); vector4_to_value(yv, yd, true); - vvp_vector4_t res = vvp_vector4_t(wid, pow(xd, yd)); + /* 2**-1 and -2**-1 are defined to be zero. */ + if ((yd == -1.0) && (fabs(xd) == 2.0)) resd = 0.0; + else resd = pow(xd, yd); + vvp_vector4_t res = vvp_vector4_t(wid, resd); /* Copy the result. */ for (unsigned jdx = 0; jdx < wid; jdx += 1) From f34a3020a6952f4e0066f5abf177100b5620a7eb Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Mon, 11 Apr 2011 22:20:52 +0100 Subject: [PATCH 19/19] Fix for lossless width estimation in shift operations. If the right hand operand of a shift is a signed vector value, it is coerced to an unsigned value. This needs to be allowed for when estimating the width expansion caused by a shift in a lossless expression. --- elab_expr.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elab_expr.cc b/elab_expr.cc index 7194909a6..3a5ee321a 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -625,7 +625,7 @@ unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode) long r_val = LONG_MAX; if (r_width < sizeof(long)*8) { r_val = (1L << r_width) - 1L; - if (right_->has_sign()) + if ((op_ == 'p') && right_->has_sign()) r_val >>= 1; }