diff --git a/PExpr.h b/PExpr.h index 13aa0e22f..29945701a 100644 --- a/PExpr.h +++ b/PExpr.h @@ -245,6 +245,10 @@ class PEConcat : public PExpr { virtual NetNet* elaborate_lnet(Design*des, NetScope*scope) const; virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope) const; + + virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, + ivl_type_t type, unsigned flags) const; + virtual NetExpr*elaborate_expr(Design*des, NetScope*, unsigned expr_wid, unsigned flags) const; diff --git a/Statement.cc b/Statement.cc index f0a395fd8..e0ed46d24 100644 --- a/Statement.cc +++ b/Statement.cc @@ -336,9 +336,13 @@ PForce::~PForce() delete expr_; } -PForeach::PForeach(perm_string av, perm_string ix, Statement*s) -: array_var_(av), index_var_(ix), statement_(s) +PForeach::PForeach(perm_string av, const list&ix, Statement*s) +: array_var_(av), index_vars_(ix.size()), statement_(s) { + size_t idx = 0; + for (list::const_iterator cur = ix.begin() + ; cur != ix.end() ; ++cur) + index_vars_[idx++] = *cur; } PForeach::~PForeach() diff --git a/Statement.h b/Statement.h index a3f17a1fa..a9dcf1297 100644 --- a/Statement.h +++ b/Statement.h @@ -445,7 +445,7 @@ class PForce : public Statement { class PForeach : public Statement { public: - explicit PForeach(perm_string var, perm_string ix, Statement*stmt); + explicit PForeach(perm_string var, const std::list&ix, Statement*stmt); ~PForeach(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; @@ -453,9 +453,13 @@ class PForeach : public Statement { virtual void elaborate_sig(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; + private: + NetProc* elaborate_static_array_(Design*des, NetScope*scope, + const std::vector&dims) const; + private: perm_string array_var_; - perm_string index_var_; + std::vector index_vars_; Statement*statement_; }; diff --git a/elab_expr.cc b/elab_expr.cc index 30ade72ac..d27ad8adf 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -30,6 +30,7 @@ # include "netlist.h" # include "netclass.h" # include "netenum.h" +# include "netparray.h" # include "netvector.h" # include "discipline.h" # include "netmisc.h" @@ -2497,6 +2498,24 @@ unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&) // Keep track of the concatenation/repeat depth. static int concat_depth = 0; +NetExpr* PEConcat::elaborate_expr(Design*, NetScope*, + ivl_type_t type, unsigned /*flags*/) const +{ + switch (type->base_type()) { + case IVL_VT_QUEUE: + if (parms_.size() == 0) { + NetENull*tmp = new NetENull; + tmp->set_line(*this); + return tmp; + } + default: + cerr << get_fileline() << ": internal error: " + << "I don't know how to elaborate(ivl_type_t)" + << " this expression: " << *this << endl; + return 0; + } +} + NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { @@ -3113,8 +3132,10 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, - ivl_type_t ntype, unsigned) const + ivl_type_t ntype, unsigned flags) const { + bool need_const = NEED_CONST & flags; + NetNet* net = 0; const NetExpr*par = 0; NetEvent* eve = 0; @@ -3126,6 +3147,10 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, ivl_assert(*this, use_scope); } + if (NetExpr* tmp = elaborate_expr_class_member_(des, scope, 0, flags)) { + return tmp; + } + /* NetScope*found_in = */ symbol_search(this, des, use_scope, path_, net, par, eve, ex1, ex2); @@ -3185,7 +3210,69 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, } ivl_assert(*this, ntype->type_compatible(net->net_type())); - NetESignal*tmp = new NetESignal(net); + const name_component_t&use_comp = path_.back(); + + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_expr: " + << "Typed ident " << net->name() + << " with " << use_comp.index.size() << " indices" + << " and " << net->unpacked_dimensions() << " expected." + << endl; + } + + if (net->unpacked_dimensions() != use_comp.index.size()) { + cerr << get_fileline() << ": sorry: " + << "Net " << net->name() + << " expects " << net->unpacked_dimensions() + << ", but got " << use_comp.index.size() << "." + << endl; + des->errors += 1; + + NetESignal*tmp = new NetESignal(net); + tmp->set_line(*this); + return tmp; + } + + if (net->unpacked_dimensions() == 0) { + NetESignal*tmp = new NetESignal(net); + tmp->set_line(*this); + return tmp; + } + + // Convert a set of index expressions to a single expression + // that addresses the canonical element. + listunpacked_indices; + list unpacked_indices_const; + indices_flags idx_flags; + indices_to_expressions(des, scope, this, + use_comp.index, net->unpacked_dimensions(), + need_const, + idx_flags, + unpacked_indices, + unpacked_indices_const); + + NetExpr*canon_index = 0; + + if (idx_flags.invalid) { + // Nothing to do + + } else if (idx_flags.undefined) { + cerr << get_fileline() << ": warning: " + << "returning 'bx for undefined array access " + << net->name() << as_indices(unpacked_indices) + << "." << endl; + + } else if (idx_flags.variable) { + ivl_assert(*this, unpacked_indices.size() == net->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(net, unpacked_indices); + + } else { + ivl_assert(*this, unpacked_indices_const.size() == net->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(net, unpacked_indices_const); + } + + ivl_assert(*this, canon_index); + NetESignal*tmp = new NetESignal(net, canon_index); tmp->set_line(*this); return tmp; @@ -3207,7 +3294,7 @@ NetExpr* PEIdent::elaborate_expr_class_member_(Design*des, NetScope*scope, if (path_.size() != 1) return 0; - const netclass_t*class_type = scope->parent()->class_def(); + const netclass_t*class_type = find_class_containing_scope(*this, scope); if (class_type == 0) return 0; @@ -3216,10 +3303,13 @@ NetExpr* PEIdent::elaborate_expr_class_member_(Design*des, NetScope*scope, if (pidx < 0) return 0; - NetNet*this_net = scope->find_signal(perm_string::literal("@")); + NetScope*scope_method = find_method_containing_scope(*this, scope); + ivl_assert(*this, scope_method); + + NetNet*this_net = scope_method->find_signal(perm_string::literal("@")); if (this_net == 0) { cerr << get_fileline() << ": internal error: " - << "Unable to find 'this' port of " << scope_path(scope) + << "Unable to find 'this' port of " << scope_path(scope_method) << "." << endl; return 0; } @@ -3229,7 +3319,8 @@ NetExpr* PEIdent::elaborate_expr_class_member_(Design*des, NetScope*scope, << "Found member " << member_name << " is a member of class " << class_type->get_name() << ", context scope=" << scope_path(scope) - << ", so synthesizing a NetEProperty." << endl; + << ", type=" << *class_type->get_prop_type(pidx) + << ", so making a NetEProperty." << endl; } property_qualifier_t qual = class_type->get_prop_qual(pidx); @@ -3245,6 +3336,13 @@ NetExpr* PEIdent::elaborate_expr_class_member_(Design*des, NetScope*scope, return class_static_property_expression(this, class_type, member_name); } + ivl_type_t tmp_type = class_type->get_prop_type(pidx); + if (/* const netuarray_t*tmp_ua =*/ dynamic_cast(tmp_type)) { + cerr << get_fileline() << ": sorry: " + << "Unpacked array properties not supported yet." << endl; + des->errors += 1; + } + NetEProperty*tmp = new NetEProperty(this_net, member_name); tmp->set_line(*this); return tmp; @@ -3295,6 +3393,12 @@ NetExpr* PEIdent::elaborate_expr_method_(Design*des, NetScope*scope, return 0; } + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_expr_method_: " + << "Give up trying to find method " << member_name + << " of " << path_ << "." << endl; + } + return 0; } diff --git a/elab_lval.cc b/elab_lval.cc index f5ab1320e..6d647652c 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -27,6 +27,7 @@ # include "netstruct.h" # include "netclass.h" # include "netdarray.h" +# include "netparray.h" # include "netvector.h" # include "compiler.h" # include @@ -259,6 +260,12 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, } ivl_assert(*this, reg); + + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval: " + << "Lval reg = " << reg->name() << endl; + } + // We are processing the tail of a string of names. For // example, the verilog may be "a.b.c", so we are processing // "c" at this point. (Note that if method_name is not nil, @@ -385,23 +392,68 @@ NetAssign_* PEIdent::elaborate_lval_method_class_member_(Design*des, if (path_.size() != 1) return 0; - const netclass_t*class_type = scope->parent()->class_def(); + const netclass_t*class_type = find_class_containing_scope(*this, scope); if (class_type == 0) return 0; - perm_string member_name = peek_tail_name(path_); + const name_component_t&name_comp = path_.back(); + + perm_string member_name = name_comp.name; int pidx = class_type->property_idx_from_name(member_name); if (pidx < 0) return 0; - NetNet*this_net = scope->find_signal(perm_string::literal("@")); + NetScope*scope_method = find_method_containing_scope(*this, scope); + ivl_assert(*this, scope_method); + + NetNet*this_net = scope_method->find_signal(perm_string::literal("@")); if (this_net == 0) { cerr << get_fileline() << ": internal error: " - << "Unable to find 'this' port of " << scope_path(scope) + << "Unable to find 'this' port of " << scope_path(scope_method) << "." << endl; return 0; } + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: " + << "Ident " << member_name + << " is a property of class " << class_type->get_name() << endl; + } + + NetExpr*canon_index = 0; + if (name_comp.index.size() > 0) { + ivl_type_t property_type = class_type->get_prop_type(pidx); + + if (const netsarray_t* stype = dynamic_cast (property_type)) { + list indices_const; + list indices_expr; + indices_flags flags; + indices_to_expressions(des, scope, this, + name_comp.index, name_comp.index.size(), + false, flags, + indices_expr, indices_const); + + if (flags.undefined) { + cerr << get_fileline() << ": warning: " + << "ignoring undefined l-value array access " + << member_name + << " (" << path_ << ")" + << "." << endl; + } else if (flags.variable) { + canon_index = normalize_variable_unpacked(*this, stype, indices_expr); + + } else { + canon_index = normalize_variable_unpacked(stype, indices_const); + } + + + } else { + cerr << get_fileline() << ": error: " + << "Index expressions don't apply to this type of property." << endl; + des->errors += 1; + } + } + // Detect assignment to constant properties. Note that the // initializer constructor MAY assign to constant properties, // as this is how the property gets its value. @@ -436,8 +488,16 @@ NetAssign_* PEIdent::elaborate_lval_method_class_member_(Design*des, } } + ivl_type_t tmp_type = class_type->get_prop_type(pidx); + if (const netuarray_t*tmp_ua = dynamic_cast(tmp_type)) { + cerr << get_fileline() << ": sorry: " + << "Unpacked array properties (l-values) not supported yet." << endl; + des->errors += 1; + } + NetAssign_*this_lval = new NetAssign_(this_net); this_lval->set_property(member_name); + if (canon_index) this_lval->set_word(canon_index); return this_lval; } diff --git a/elab_scope.cc b/elab_scope.cc index 87593c192..5c3db1c34 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -434,17 +434,19 @@ static void elaborate_scope_class(Design*des, NetScope*scope, PClass*pclass) class_scope->set_line(pclass); class_scope->set_class_def(use_class); use_class->set_class_scope(class_scope); + use_class->set_definition_scope(scope); // Collect the properties, elaborate them, and add them to the // elaborated class definition. for (map::iterator cur = use_type->properties.begin() ; cur != use_type->properties.end() ; ++ cur) { - if (debug_scopes) { - cerr << pclass->get_fileline() << ": elaborate_scope_class: " - << " Property " << cur->first << endl; - } ivl_type_s*tmp = cur->second.type->elaborate_type(des, scope); ivl_assert(*pclass, tmp); + if (debug_scopes) { + cerr << pclass->get_fileline() << ": elaborate_scope_class: " + << " Property " << cur->first + << " type=" << *tmp << endl; + } use_class->set_property(cur->first, cur->second.qual, tmp); } diff --git a/elab_sig.cc b/elab_sig.cc index e88689a14..9d5df8245 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -836,48 +836,6 @@ void PWhile::elaborate_sig(Design*des, NetScope*scope) const statement_->elaborate_sig(des, scope); } -static bool evaluate_ranges(Design*des, NetScope*scope, - vector&llist, - const list&rlist) -{ - bool bad_msb = false, bad_lsb = false; - - for (list::const_iterator cur = rlist.begin() - ; cur != rlist.end() ; ++cur) { - long use_msb, use_lsb; - - NetExpr*texpr = elab_and_eval(des, scope, cur->first, -1, true); - if (! eval_as_long(use_msb, texpr)) { - cerr << cur->first->get_fileline() << ": error: " - "Range expressions must be constant." << endl; - cerr << cur->first->get_fileline() << " : " - "This MSB expression violates the rule: " - << *cur->first << endl; - des->errors += 1; - bad_msb = true; - } - - delete texpr; - - texpr = elab_and_eval(des, scope, cur->second, -1, true); - if (! eval_as_long(use_lsb, texpr)) { - cerr << cur->second->get_fileline() << ": error: " - "Range expressions must be constant." << endl; - cerr << cur->second->get_fileline() << " : " - "This LSB expression violates the rule: " - << *cur->second << endl; - des->errors += 1; - bad_lsb = true; - } - - delete texpr; - - llist.push_back(netrange_t(use_msb, use_lsb)); - } - - return bad_msb | bad_lsb; -} - static netclass_t* locate_class_type(Design*, NetScope*scope, class_type_t*class_type) { @@ -1222,13 +1180,12 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const } ivl_assert(*this, use_type); if (debug_elaborate) { - cerr << get_fileline() << ": debug: " + cerr << get_fileline() << ": PWire::elaborate_sig: " << "Create class instance signal " << wtype - << " " << name_ << endl; + << " " << packed_dimensions << name_ << unpacked_dimensions << endl; } - // (No arrays of classes) - list use_unpacked; - sig = new NetNet(scope, name_, wtype, use_unpacked, use_type); + + sig = new NetNet(scope, name_, wtype, unpacked_dimensions, use_type); } else if (struct_type_t*struct_type = dynamic_cast(set_data_type_)) { // If this is a struct type, then build the net with the @@ -1289,7 +1246,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const if (debug_elaborate) { cerr << get_fileline() << ": debug: Create signal " << wtype - << " parray=" << use_type->packed_dimensions() + << " parray=" << use_type->static_dimensions() << " " << name_ << unpacked_dimensions << " in scope " << scope_path(scope) << endl; } diff --git a/elab_type.cc b/elab_type.cc index 1167c1902..2f3dab573 100644 --- a/elab_type.cc +++ b/elab_type.cc @@ -22,6 +22,7 @@ # include "netclass.h" # include "netdarray.h" # include "netenum.h" +# include "netparray.h" # include "netscalar.h" # include "netstruct.h" # include "netvector.h" @@ -203,9 +204,21 @@ ivl_type_s* uarray_type_t::elaborate_type_raw(Design*des, NetScope*scope) const ivl_type_t btype = base_type->elaborate_type(des, scope); - assert(dims->size() == 1); + assert(dims->size() >= 1); list::const_iterator cur = dims->begin(); - assert(cur->first == 0 && cur->second==0); - ivl_type_s*res = new netdarray_t(btype); + + // Special case: if the dimension is nil:nil, this is a + // dynamic array. Note that we only know how to handle dynamic + // arrays with 1 dimension at a time. + if (cur->first==0 && cur->second==0) { + assert(dims->size()==1); + ivl_type_s*res = new netdarray_t(btype); + return res; + } + + vector dimensions; + bool bad_range = evaluate_ranges(des, scope, dimensions, *dims); + + ivl_type_s*res = new netuarray_t(dimensions, btype); return res; } diff --git a/elaborate.cc b/elaborate.cc index 4413f22de..ec5f0544e 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -40,6 +40,7 @@ # include "netlist.h" # include "netvector.h" # include "netdarray.h" +# include "netparray.h" # include "netclass.h" # include "netmisc.h" # include "util.h" @@ -2290,6 +2291,14 @@ NetAssign_* PAssign_::elaborate_lval(Design*des, NetScope*scope) const NetAssign_*lv = new NetAssign_(tmp); return lv; } + + if (debug_elaborate) { + cerr << get_fileline() << ": PAssign_::elaborate_lval: " + << "lval_ = " << *lval_ << endl; + cerr << get_fileline() << ": PAssign_::elaborate_lval: " + << "lval_ expr type = " << typeid(*lval_).name() << endl; + } + return lval_->elaborate_lval(des, scope, false, false); } @@ -2520,6 +2529,22 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const rv = elaborate_rval_(des, scope, use_lv_type); + } else if (const netuarray_t*utype = dynamic_cast(lv_net_type)) { + ivl_assert(*this, lv->more==0); + if (debug_elaborate) { + if (lv->word()) + cerr << get_fileline() << ": PAssign::elaborate: " + << "lv->word() = " << *lv->word() << endl; + else + cerr << get_fileline() << ": PAssign::elaborate: " + << "lv->word() = " << endl; + } + ivl_type_t use_lv_type = lv_net_type; + ivl_assert(*this, lv->word()); + use_lv_type = utype->element_type(); + + rv = elaborate_rval_(des, scope, use_lv_type); + } else { /* Elaborate the r-value expression, then try to evaluate it. */ rv = elaborate_rval_(des, scope, lv_net_type, lv->expr_type(), count_lval_width(lv)); @@ -4580,6 +4605,20 @@ NetForce* PForce::elaborate(Design*des, NetScope*scope) const return dev; } +static void find_property_in_class(const LineInfo&loc, const NetScope*scope, perm_string name, const netclass_t*&found_in, int&property) +{ + found_in = find_class_containing_scope(loc, scope); + property = -1; + + if (found_in==0) return; + + property = found_in->property_idx_from_name(name); + if (property < 0) { + found_in = 0; + return; + } +} + /* * The foreach statement can be written as a for statement like so: * @@ -4591,24 +4630,104 @@ NetForce* PForce::elaborate(Design*des, NetScope*scope) const */ NetProc* PForeach::elaborate(Design*des, NetScope*scope) const { - // Get the signal for the index variable. - pform_name_t index_name; - index_name.push_back(name_component_t(index_var_)); - NetNet*idx_sig = des->find_signal(scope, index_name); - ivl_assert(*this, idx_sig); - - NetESignal*idx_exp = new NetESignal(idx_sig); - idx_exp->set_line(*this); - - // Get the signal for the array variable + // Locate the signal for the array variable pform_name_t array_name; array_name.push_back(name_component_t(array_var_)); NetNet*array_sig = des->find_signal(scope, array_name); + + // And if necessary, look for the class property that is + // referenced. + const netclass_t*class_scope = 0; + int class_property = -1; + if (array_sig == 0) + find_property_in_class(*this, scope, array_var_, class_scope, class_property); + + if (debug_elaborate && array_sig) { + cerr << get_fileline() << ": PForeach::elaborate: " + << "Found array_sig in " << scope_path(array_sig->scope()) << "." << endl; + } + + if (debug_elaborate && class_scope) { + cerr << get_fileline() << ": PForeach::elaborate: " + << "Found array_sig property (" << class_property + << ") in class " << class_scope->get_name() + << " as " << *class_scope->get_prop_type(class_property) << "." << endl; + } + + if (class_scope!=0 && class_property >= 0) { + ivl_type_t ptype = class_scope->get_prop_type(class_property); + const netsarray_t*atype = dynamic_cast (ptype); + if (atype == 0) { + cerr << get_fileline() << ": error: " + << "I can't handle the type of " << array_var_ + << " as a foreach loop." << endl; + des->errors += 1; + return 0; + } + + const std::vector&dims = atype->static_dimensions(); + if (dims.size() < index_vars_.size()) { + cerr << get_fileline() << ": error: " + << "class " << class_scope->get_name() + << " property " << array_var_ + << " has too few dimensions for foreach dimension list." << endl; + des->errors += 1; + return 0; + } + + return elaborate_static_array_(des, scope, dims); + } + + if (array_sig == 0) { + cerr << get_fileline() << ": error:" + << " Unable to find foreach array " << array_name + << " in scope " << scope_path(scope) + << "." << endl; + des->errors += 1; + return 0; + } + ivl_assert(*this, array_sig); + if (debug_elaborate) { + cerr << get_fileline() << ": PForeach::elaborate: " + << "Scan array " << array_sig->name() + << " of " << array_sig->data_type() + << " with " << array_sig->unpacked_dimensions() << " unpacked" + << " and " << array_sig->packed_dimensions() + << " packed dimensions." << endl; + } + + // Classic arrays are processed this way. + if (array_sig->data_type()==IVL_VT_BOOL) + return elaborate_static_array_(des, scope, array_sig->unpacked_dims()); + if (array_sig->data_type()==IVL_VT_LOGIC) + return elaborate_static_array_(des, scope, array_sig->unpacked_dims()); + if (array_sig->unpacked_dimensions() >= index_vars_.size()) + return elaborate_static_array_(des, scope, array_sig->unpacked_dims()); + + + // At this point, we know that the array is dynamic so we + // handle that slightly differently, using run-time tests. + + if (index_vars_.size() != 1) { + cerr << get_fileline() << ": sorry: " + << "Multi-index foreach loops not supported." << endl; + des->errors += 1; + } + + // Get the signal for the index variable. + pform_name_t index_name; + index_name.push_back(name_component_t(index_vars_[0])); + NetNet*idx_sig = des->find_signal(scope, index_name); + ivl_assert(*this, idx_sig); + NetESignal*array_exp = new NetESignal(array_sig); array_exp->set_line(*this); + NetESignal*idx_exp = new NetESignal(idx_sig); + idx_exp->set_line(*this); + // Make an initialization expression for the index. NetESFunc*init_expr = new NetESFunc("$low", IVL_VT_BOOL, 32, 1); init_expr->set_line(*this); @@ -4639,6 +4758,69 @@ NetProc* PForeach::elaborate(Design*des, NetScope*scope) const return stmt; } +/* + * This is a variant of the PForeach::elaborate() method that handles + * the case that the array has static dimensions. We can use constants + * and possibly do some optimizations. + */ +NetProc* PForeach::elaborate_static_array_(Design*des, NetScope*scope, + const vector&dims) const +{ + if (debug_elaborate) { + cerr << get_fileline() << ": PForeach::elaborate_static_array_: " + << "Handle as array with static dimensions." << endl; + } + + ivl_assert(*this, index_vars_.size() > 0); + ivl_assert(*this, dims.size() == index_vars_.size()); + + NetProc*sub = statement_->elaborate(des, scope); + NetForLoop*stmt = 0; + + for (int idx_idx = index_vars_.size()-1 ; idx_idx >= 0 ; idx_idx -= 1) { + const netrange_t&idx_range = dims[idx_idx]; + + // Get the $high and $low constant values for this slice + // of the array. + NetEConst*hig_expr = make_const_val_s(idx_range.get_msb()); + NetEConst*low_expr = make_const_val_s(idx_range.get_lsb()); + if (idx_range.get_msb() < idx_range.get_lsb()) { + NetEConst*tmp = hig_expr; + hig_expr = low_expr; + low_expr = tmp; + } + + hig_expr->set_line(*this); + low_expr->set_line(*this); + + pform_name_t idx_name; + idx_name.push_back(name_component_t(index_vars_[idx_idx])); + NetNet*idx_sig = des->find_signal(scope, idx_name); + ivl_assert(*this, idx_sig); + + // Make the condition expression <= $high(slice) + NetESignal*idx_expr = new NetESignal(idx_sig); + idx_expr->set_line(*this); + + NetEBComp*cond_expr = new NetEBComp('L', idx_expr, hig_expr); + cond_expr->set_line(*this); + + // Make the step statement: += 1 + NetAssign_*idx_lv = new NetAssign_(idx_sig); + NetEConst*step_val = make_const_val_s(1); + NetAssign*step = new NetAssign(idx_lv, '+', step_val); + step->set_line(*this); + + stmt = new NetForLoop(idx_sig, low_expr, cond_expr, sub, step); + stmt->set_line(*this); + stmt->wrap_up(); + + sub = stmt; + } + + return stmt? stmt : sub; +} + /* * elaborate the for loop as the equivalent while loop. This eases the * task for the target code generator. The structure is: diff --git a/emit.cc b/emit.cc index df661b909..c71e05c1e 100644 --- a/emit.cc +++ b/emit.cc @@ -409,14 +409,13 @@ void NetRepeat::emit_recurse(struct target_t*tgt) const void netclass_t::emit_scope(struct target_t*tgt) const { class_scope_->emit_scope(tgt); - class_scope_->emit_defs(tgt); } void NetScope::emit_scope(struct target_t*tgt) const { if (debug_emit) { cerr << "NetScope::emit_scope: " - << "Emit scope basename=" << basename() << endl; + << "Emit scope " << scope_path(this) << endl; } tgt->scope(this); @@ -461,12 +460,20 @@ bool NetScope::emit_defs(struct target_t*tgt) const { bool flag = true; + if (debug_emit) { + cerr << "NetScope::emit_defs: " + << "Emit definitions for " << scope_path(this) << endl; + } + switch (type_) { case PACKAGE: case MODULE: for (map::const_iterator cur = children_.begin() ; cur != children_.end() ; ++ cur ) flag &= cur->second->emit_defs(tgt); + for (map::const_iterator cur = classes_.begin() + ; cur != classes_.end() ; ++ cur) + flag &= cur->second->emit_defs(tgt); break; case FUNC: @@ -485,6 +492,11 @@ bool NetScope::emit_defs(struct target_t*tgt) const return flag; } +bool netclass_t::emit_defs(struct target_t*tgt) const +{ + return class_scope_->emit_defs(tgt); +} + int Design::emit(struct target_t*tgt) const { int rc = 0; diff --git a/net_proc.cc b/net_proc.cc index c10bff7ab..93c746b6d 100644 --- a/net_proc.cc +++ b/net_proc.cc @@ -139,7 +139,7 @@ void NetForLoop::wrap_up() NetBlock*internal_block = new NetBlock(NetBlock::SEQU, 0); internal_block->set_line(*this); - internal_block->append(statement_); + if (statement_) internal_block->append(statement_); internal_block->append(step_statement_); NetWhile*wloop = new NetWhile(condition_, internal_block); diff --git a/net_scope.cc b/net_scope.cc index f7b4b925a..e20173ab9 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -654,6 +654,13 @@ netclass_t*NetScope::find_class(perm_string name) if (type_==MODULE) return 0; + if (up_==0 && type_==CLASS) { + assert(class_def_); + + NetScope*def_parent = class_def_->definition_scope(); + return def_parent->find_class(name); + } + // If there is no further to look, ... if (up_ == 0) return 0; diff --git a/netclass.cc b/netclass.cc index f302585d1..9c2d56c29 100644 --- a/netclass.cc +++ b/netclass.cc @@ -24,7 +24,7 @@ using namespace std; netclass_t::netclass_t(perm_string name, netclass_t*sup) -: name_(name), super_(sup), class_scope_(0) +: name_(name), super_(sup), class_scope_(0), definition_scope_(0) { } @@ -56,6 +56,12 @@ void netclass_t::set_class_scope(NetScope*class_scope) class_scope_ = class_scope; } +void netclass_t::set_definition_scope(NetScope*definition_scope) +{ + assert(definition_scope_ == 0); + definition_scope_ = definition_scope; +} + ivl_variable_type_t netclass_t::base_type() const { return IVL_VT_CLASS; diff --git a/netclass.h b/netclass.h index da7fb3e96..4b94293f9 100644 --- a/netclass.h +++ b/netclass.h @@ -43,9 +43,19 @@ class netclass_t : public ivl_type_s { bool set_property(perm_string pname, property_qualifier_t qual, ivl_type_s*ptype); // Set the scope for the class. The scope has no parents and - // is used for the elaboration of methods (tasks/functions). + // is used for the elaboration of methods + // (tasks/functions). In other words, this is the class itself. void set_class_scope(NetScope*cscope); + // Set the scope for the class definition. This is the scope + // where the class definition was encountered, and may be used + // to locate symbols that the class definition may inherit + // from its context. This can be nil, or a package or module + // where a class is defined. + void set_definition_scope(NetScope*dscope); + + NetScope*definition_scope(void); + // As an ivl_type_s object, the netclass is always an // ivl_VT_CLASS object. ivl_variable_type_t base_type() const; @@ -74,7 +84,8 @@ class netclass_t : public ivl_type_s { bool test_for_missing_initializers(void) const; - // Map the name of a property to its index. + // Map the name of a property to its index. Return <0 if the + // name is not a property in the class. int property_idx_from_name(perm_string pname) const; // The task method scopes from the method name. @@ -94,6 +105,7 @@ class netclass_t : public ivl_type_s { void elaborate(Design*des, PClass*pclass); void emit_scope(struct target_t*tgt) const; + bool emit_defs(struct target_t*tgt) const; void dump_scope(ostream&fd) const; @@ -115,6 +127,14 @@ class netclass_t : public ivl_type_s { // This holds task/function definitions for methods. NetScope*class_scope_; + + // This holds the context for the class type definition. + NetScope*definition_scope_; }; +inline NetScope*netclass_t::definition_scope(void) +{ + return definition_scope_; +} + #endif /* IVL_netclass_H */ diff --git a/netmisc.cc b/netmisc.cc index 585388f06..99dfa0bf5 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -22,6 +22,7 @@ # include # include # include "netlist.h" +# include "netparray.h" # include "netvector.h" # include "netmisc.h" # include "PExpr.h" @@ -526,10 +527,8 @@ static void make_strides(const vector&dims, * word. If any of the indices are out of bounds, return nil instead * of an expression. */ -NetExpr* normalize_variable_unpacked(const NetNet*net, list&indices) +static NetExpr* normalize_variable_unpacked(const vector&dims, list&indices) { - const vector&dims = net->unpacked_dims(); - // Make strides for each index. The stride is the distance (in // words) to the next element in the canonical array. vector stride (dims.size()); @@ -559,10 +558,20 @@ NetExpr* normalize_variable_unpacked(const NetNet*net, list&indices) return canonical_expr; } -NetExpr* normalize_variable_unpacked(const NetNet*net, list&indices) +NetExpr* normalize_variable_unpacked(const NetNet*net, list&indices) { const vector&dims = net->unpacked_dims(); + return normalize_variable_unpacked(dims, indices); +} +NetExpr* normalize_variable_unpacked(const netsarray_t*stype, list&indices) +{ + const vector&dims = stype->static_dimensions(); + return normalize_variable_unpacked(dims, indices); +} + +NetExpr* normalize_variable_unpacked(const LineInfo&loc, const vector&dims, list&indices) +{ // Make strides for each index. The stride is the distance (in // words) to the next element in the canonical array. vector stride (dims.size()); @@ -602,7 +611,7 @@ NetExpr* normalize_variable_unpacked(const NetNet*net, list&indices) if (use_stride != 1) min_wid += num_bits(use_stride); - tmp = pad_to_width(tmp, min_wid, *net); + tmp = pad_to_width(tmp, min_wid, loc); // Now generate the math to calculate the canonical address. NetExpr*tmp_scaled = 0; @@ -641,11 +650,23 @@ NetExpr* normalize_variable_unpacked(const NetNet*net, list&indices) // If we don't have an expression at this point, all the indices were // constant zero. But this variant of normalize_variable_unpacked() // is only used when at least one index is not a constant. - ivl_assert(*net, canonical_expr); + ivl_assert(loc, canonical_expr); return canonical_expr; } +NetExpr* normalize_variable_unpacked(const NetNet*net, list&indices) +{ + const vector&dims = net->unpacked_dims(); + return normalize_variable_unpacked(*net, dims, indices); +} + +NetExpr* normalize_variable_unpacked(const LineInfo&loc, const netsarray_t*stype, list&indices) +{ + const vector&dims = stype->static_dimensions(); + return normalize_variable_unpacked(loc, dims, indices); +} + NetEConst* make_const_x(unsigned long wid) { verinum xxx (verinum::Vx, wid); @@ -667,6 +688,14 @@ NetEConst* make_const_val(unsigned long value) return res; } +NetEConst* make_const_val_s(long value) +{ + verinum tmp (value, integer_width); + tmp.has_sign(true); + NetEConst*res = new NetEConst(tmp); + return res; +} + NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid) { verinum xxx (verinum::Vx, wid); @@ -877,6 +906,48 @@ NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, return tmp; } +bool evaluate_ranges(Design*des, NetScope*scope, + vector&llist, + const list&rlist) +{ + bool bad_msb = false, bad_lsb = false; + + for (list::const_iterator cur = rlist.begin() + ; cur != rlist.end() ; ++cur) { + long use_msb, use_lsb; + + NetExpr*texpr = elab_and_eval(des, scope, cur->first, -1, true); + if (! eval_as_long(use_msb, texpr)) { + cerr << cur->first->get_fileline() << ": error: " + "Range expressions must be constant." << endl; + cerr << cur->first->get_fileline() << " : " + "This MSB expression violates the rule: " + << *cur->first << endl; + des->errors += 1; + bad_msb = true; + } + + delete texpr; + + texpr = elab_and_eval(des, scope, cur->second, -1, true); + if (! eval_as_long(use_lsb, texpr)) { + cerr << cur->second->get_fileline() << ": error: " + "Range expressions must be constant." << endl; + cerr << cur->second->get_fileline() << " : " + "This LSB expression violates the rule: " + << *cur->second << endl; + des->errors += 1; + bad_lsb = true; + } + + delete texpr; + + llist.push_back(netrange_t(use_msb, use_lsb)); + } + + return bad_msb | bad_lsb; +} + void eval_expr(NetExpr*&expr, int context_width) { assert(expr); @@ -1425,3 +1496,36 @@ NetPartSelect* detect_partselect_lval(Link&pin) return found_ps; } + +const netclass_t* find_class_containing_scope(const LineInfo&loc, const NetScope*scope) +{ + while (scope && scope->type() != NetScope::CLASS) + scope = scope->parent(); + + if (scope == 0) + return 0; + + const netclass_t*found_in = scope->class_def(); + ivl_assert(loc, found_in); + return found_in; +} +/* + * Find the scope that contains this scope, that is the method for a + * class scope. Look for the scope whose PARENT is the scope for a + * class. This is going to be a method. + */ +NetScope* find_method_containing_scope(const LineInfo&, NetScope*scope) +{ + NetScope*up = scope->parent(); + + while (up && up->type() != NetScope::CLASS) { + scope = up; + up = up->parent(); + } + + if (up == 0) return 0; + + // Should I check if this scope is a TASK or FUNC? + + return scope; +} diff --git a/netmisc.h b/netmisc.h index 6b5db586f..e7ee8aad3 100644 --- a/netmisc.h +++ b/netmisc.h @@ -21,6 +21,8 @@ # include "netlist.h" +class netsarray_t; + /* * Search for a symbol using the "start" scope as the starting * point. If the path includes a scope part, then locate the @@ -184,7 +186,10 @@ extern void indices_to_expressions(Design*des, NetScope*scope, list&indices,list&indices_const); extern NetExpr*normalize_variable_unpacked(const NetNet*net, list&indices); +extern NetExpr*normalize_variable_unpacked(const netsarray_t*net, list&indices); + extern NetExpr*normalize_variable_unpacked(const NetNet*net, list&indices); +extern NetExpr*normalize_variable_unpacked(const LineInfo&loc, const netsarray_t*net, list&indices); /* * This function takes as input a NetNet signal and adds a constant @@ -204,6 +209,7 @@ extern NetNet*sub_net_from(Design*des, NetScope*scope, long val, NetNet*sig); extern NetEConst*make_const_x(unsigned long wid); extern NetEConst*make_const_0(unsigned long wid); extern NetEConst*make_const_val(unsigned long val); +extern NetEConst*make_const_val_s(long val); /* * Make A const net @@ -277,6 +283,9 @@ extern NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, unsigned lv_width, PExpr*expr, bool need_const =false); +extern bool evaluate_ranges(Design*des, NetScope*scope, + std::vector&llist, + const std::list&rlist); /* * This procedure evaluates an expression and if the evaluation is * successful the original expression is replaced with the new one. @@ -300,6 +309,13 @@ extern hname_t eval_path_component(Design*des, NetScope*scope, const name_component_t&comp, bool&error_flag); +/* + * If this scope is contained within a class scope (i.e. a method of a + * class) then return the class definition that contains it. + */ +extern const netclass_t*find_class_containing_scope(const LineInfo&loc,const NetScope*scope); +extern NetScope* find_method_containing_scope(const LineInfo&log, NetScope*scope); + /* * Return true if the data type is a type that is normally available * in vector for. IVL_VT_BOOL and IVL_VT_LOGIC are vectorable, diff --git a/netparray.cc b/netparray.cc index 326b549fc..2adcac16e 100644 --- a/netparray.cc +++ b/netparray.cc @@ -22,6 +22,10 @@ using namespace std; +netsarray_t::~netsarray_t() +{ +} + netparray_t::~netparray_t() { } @@ -34,8 +38,8 @@ long netparray_t::packed_width(void) const { long cur_width = element_type()->packed_width(); - for (vector::const_iterator cur = packed_dims_.begin() - ; cur != packed_dims_.end() ; ++cur) { + for (vector::const_iterator cur = static_dimensions().begin() + ; cur != static_dimensions().end() ; ++cur) { cur_width *= cur->width(); } @@ -44,14 +48,20 @@ long netparray_t::packed_width(void) const vector netparray_t::slice_dimensions() const { + const vector&packed_dims = static_dimensions(); + vector elem_dims = element_type()->slice_dimensions(); - vector res (packed_dims_.size() + elem_dims.size()); + vector res (packed_dims.size() + elem_dims.size()); - for (size_t idx = 0 ; idx < packed_dims_.size() ; idx += 1) - res[idx] = packed_dims_[idx]; + for (size_t idx = 0 ; idx < packed_dims.size() ; idx += 1) + res[idx] = packed_dims[idx]; for (size_t idx = 0 ; idx < elem_dims.size() ; idx += 1) - res[idx+packed_dims_.size()] = elem_dims[idx]; + res[idx+packed_dims.size()] = elem_dims[idx]; return res; } + +netuarray_t::~netuarray_t() +{ +} diff --git a/netparray.h b/netparray.h index 96e8735af..2e0142a65 100644 --- a/netparray.h +++ b/netparray.h @@ -23,10 +23,39 @@ # include "nettypes.h" # include +/* + * Arrays with static dimensions (packed and unpacked) share this + * common base type. + */ +class netsarray_t : public netarray_t { + + public: + explicit netsarray_t(const std::vector&packed, + ivl_type_t etype); + ~netsarray_t(); + + public: + // Virtual methods from the ivl_type_s type... + + public: + inline const std::vector& static_dimensions() const + { return dims_; } + + private: + std::vector dims_; + +}; + +inline netsarray_t::netsarray_t(const std::vector&pd, + ivl_type_t etype) +: netarray_t(etype), dims_(pd) +{ +} + /* * Packed arrays. */ -class netparray_t : public netarray_t { +class netparray_t : public netsarray_t { public: explicit netparray_t(const std::vector&packed, @@ -38,18 +67,28 @@ class netparray_t : public netarray_t { long packed_width(void) const; std::vector slice_dimensions() const; - public: - inline const std::vector& packed_dimensions() const - { return packed_dims_; } - - private: - std::vector packed_dims_; - }; inline netparray_t::netparray_t(const std::vector&pd, ivl_type_t etype) -: netarray_t(etype), packed_dims_(pd) +: netsarray_t(pd, etype) +{ +} + +/* + * Unpacked arrays are very similar, but lack packed slices. + */ +class netuarray_t : public netsarray_t { + + public: + explicit netuarray_t(const std::vector&packed, + ivl_type_t etype); + ~netuarray_t(); +}; + +inline netuarray_t::netuarray_t(const std::vector&pd, + ivl_type_t etype) +: netsarray_t(pd, etype) { } diff --git a/parse.y b/parse.y index 032a80039..28f83bf4b 100644 --- a/parse.y +++ b/parse.y @@ -3457,11 +3457,14 @@ expr_primary | '{' '}' { // This is the empty queue syntax. if (gn_system_verilog()) { - yyerror(@1, "sorry: Expty queue expressions not supported."); + list empty_list; + PEConcat*tmp = new PEConcat(empty_list); + FILE_NAME(tmp, @1); + $$ = tmp; } else { yyerror(@1, "error: Concatenations are not allowed to be empty."); + $$ = 0; } - $$ = 0; } /* Cast expressions are primaries */ diff --git a/pform.cc b/pform.cc index 62adb07bc..f637f9b06 100644 --- a/pform.cc +++ b/pform.cc @@ -739,19 +739,18 @@ PForeach* pform_make_foreach(const struct vlltype&loc, perm_string use_name = lex_strings.make(name); delete[]name; - perm_string use_index = loop_vars->front(); - loop_vars->pop_front(); - - if (! loop_vars->empty()) { - cerr << loc.get_fileline() << ": sorry: " - << "Multi-dimension foreach indices not supported." << endl; + if (loop_vars==0 || loop_vars->empty()) { + cerr << loc.get_fileline() << ": error: " + << "No loop variables at all in foreach index." << endl; error_count += 1; } - delete loop_vars; - PForeach*fe = new PForeach(use_name, use_index, stmt); + ivl_assert(loc, loop_vars); + PForeach*fe = new PForeach(use_name, *loop_vars, stmt); FILE_NAME(fe, loc); + delete loop_vars; + return fe; } diff --git a/pform_dump.cc b/pform_dump.cc index c7560ea9a..f50749f31 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -973,8 +973,13 @@ void PForeach::dump(ostream&fd, unsigned ind) const { fd << setw(ind) << "" << "foreach " << "variable=" << array_var_ - << ", index=" << index_var_ - << " /* " << get_fileline() << " */" << endl; + << ", indices=["; + for (size_t idx = 0 ; idx < index_vars_.size() ; idx += 1) { + if (idx > 0) fd << ","; + fd << index_vars_[idx]; + } + + fd << "] /* " << get_fileline() << " */" << endl; statement_->dump(fd, ind+3); } diff --git a/t-dll-expr.cc b/t-dll-expr.cc index 9b748d955..9974187b0 100644 --- a/t-dll-expr.cc +++ b/t-dll-expr.cc @@ -651,6 +651,11 @@ void dll_target::expr_ufunc(const NetEUFunc*net) FILE_NAME(expr, net); expr->u_.ufunc_.def = lookup_scope_(net->func()); + if (expr->u_.ufunc_.def == 0) { + cerr << net->get_fileline() << ": internal error: " + << "dll_target::expr_ufunc: " + << "Unable to match scope " << scope_path(net->func()) << endl; + } ivl_assert(*net, expr->u_.ufunc_.def); ivl_assert(*net, expr->u_.ufunc_.def->type_ == IVL_SCT_FUNCTION); diff --git a/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index 7d8ed5d25..82c4bd428 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -519,7 +519,27 @@ static struct vector_info draw_binary_expr_eq_class(ivl_expr_t expr) } if (ivl_expr_type(re) == IVL_EX_NULL && ivl_expr_type(le)==IVL_EX_SIGNAL) { - fprintf(vvp_out, " %%test_nul v%p_0;\n", ivl_expr_signal(le)); + ivl_signal_t sig = ivl_expr_signal(le); + + if (ivl_signal_dimensions(sig) == 0) { + fprintf(vvp_out, " %%test_nul v%p_0;\n", sig); + } else { + ivl_expr_t word_ex = ivl_expr_oper1(le); + int word_ix = allocate_word(); + draw_eval_expr_into_integer(word_ex, word_ix); + fprintf(vvp_out, " %%test_nul/a v%p, %d;\n", sig, word_ix); + clr_word(word_ix); + } + fprintf(vvp_out, " %%mov %u, 4, 1;\n", res.base); + if (ivl_expr_opcode(expr) == 'n') + fprintf(vvp_out, " %%inv %u, 1;\n", res.base); + return res; + } + + if (ivl_expr_type(re) == IVL_EX_NULL && ivl_expr_value(le)==IVL_VT_CLASS) { + draw_eval_object(le); + fprintf(vvp_out, " %%test_nul/obj;\n"); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); fprintf(vvp_out, " %%mov %u, 4, 1;\n", res.base); if (ivl_expr_opcode(expr) == 'n') fprintf(vvp_out, " %%inv %u, 1;\n", res.base); diff --git a/tgt-vvp/eval_object.c b/tgt-vvp/eval_object.c index 01019a0a6..883eeeb57 100644 --- a/tgt-vvp/eval_object.c +++ b/tgt-vvp/eval_object.c @@ -195,10 +195,25 @@ static int eval_object_shallowcopy(ivl_expr_t ex) return 0; } -static int eval_object_signal(ivl_expr_t ex) +static int eval_object_signal(ivl_expr_t expr) { - ivl_signal_t sig = ivl_expr_signal(ex); - fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); + ivl_signal_t sig = ivl_expr_signal(expr); + + /* Simple case: This is a simple variable. Generate a load + statement to load the string into the stack. */ + if (ivl_signal_dimensions(sig) == 0) { + fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); + return 0; + } + + /* There is a word select expression, so load the index into a + register and load from the array. */ + ivl_expr_t word_ex = ivl_expr_oper1(expr); + int word_ix = allocate_word(); + draw_eval_expr_into_integer(word_ex, word_ix); + fprintf(vvp_out, " %%load/obja v%p, %d;\n", sig, word_ix); + clr_word(word_ix); + return 0; } diff --git a/tgt-vvp/stmt_assign.c b/tgt-vvp/stmt_assign.c index c05bd1fe1..17d59694c 100644 --- a/tgt-vvp/stmt_assign.c +++ b/tgt-vvp/stmt_assign.c @@ -976,6 +976,30 @@ static int show_stmt_assign_sig_darray(ivl_statement_t net) return errors; } +static int show_stmt_assign_sig_queue(ivl_statement_t net) +{ + int errors = 0; + ivl_lval_t lval = ivl_stmt_lval(net, 0); + ivl_expr_t rval = ivl_stmt_rval(net); + ivl_signal_t var= ivl_lval_sig(lval); + ivl_type_t var_type= ivl_signal_net_type(var); + assert(ivl_type_base(var_type) == IVL_VT_QUEUE); + + switch (ivl_expr_type(rval)) { + case IVL_EX_NULL: + errors += draw_eval_object(rval); + break; + default: + fprintf(stderr, "XXXX: I don't know how to handle expr_type=%d here\n", ivl_expr_type(rval)); + fprintf(vvp_out, " ; XXXX expr_type=%d\n", ivl_expr_type(rval)); + errors += 1; + break; + } + + fprintf(vvp_out, " %%store/obj v%p_0;\n", var); + return errors; +} + static int show_stmt_assign_sig_cobject(ivl_statement_t net) { int errors = 0; @@ -1066,7 +1090,19 @@ static int show_stmt_assign_sig_cobject(ivl_statement_t net) as an object and assign the entire object to the variable. */ errors += draw_eval_object(rval); - fprintf(vvp_out, " %%store/obj v%p_0;\n", sig); + + if (ivl_signal_array_count(sig) > 1) { + unsigned ix; + ivl_expr_t aidx = ivl_lval_idx(lval); + + draw_eval_expr_into_integer(aidx, (ix = allocate_word())); + fprintf(vvp_out, " %%store/obja v%p, %u;\n", sig, ix); + clr_word(ix); + + } else { + /* Not an array, so no index expression */ + fprintf(vvp_out, " %%store/obj v%p_0;\n", sig); + } } return errors; @@ -1094,6 +1130,10 @@ int show_stmt_assign(ivl_statement_t net) return show_stmt_assign_sig_darray(net); } + if (sig && (ivl_signal_data_type(sig) == IVL_VT_QUEUE)) { + return show_stmt_assign_sig_queue(net); + } + if (sig && (ivl_signal_data_type(sig) == IVL_VT_CLASS)) { return show_stmt_assign_sig_cobject(net); } diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index a41db91b3..b2779f379 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -469,6 +469,10 @@ static void draw_reg_in_scope(ivl_signal_t sig) datatype_flag = "/str"; vector_dims = 0; break; + case IVL_VT_CLASS: + datatype_flag = "/obj"; + vector_dims = 0; + break; default: break; } diff --git a/vvp/array.cc b/vvp/array.cc index 25bfdbf3d..396b7effe 100644 --- a/vvp/array.cc +++ b/vvp/array.cc @@ -1055,6 +1055,18 @@ void array_set_word(vvp_array_t arr, unsigned address, const string&val) array_word_change(arr, address); } +void array_set_word(vvp_array_t arr, unsigned address, const vvp_object_t&val) +{ + assert(arr->vals != 0); + assert(arr->nets == 0); + + if (address >= arr->vals->get_size()) + return; + + arr->vals->set_word(address, val); + array_word_change(arr, address); +} + vvp_vector4_t array_get_word(vvp_array_t arr, unsigned address) { if (arr->vals4) { @@ -1129,6 +1141,28 @@ double array_get_word_r(vvp_array_t arr, unsigned address) } +void array_get_word_obj(vvp_array_t arr, unsigned address, vvp_object_t&val) +{ + if (arr->vals) { + assert(arr->vals4 == 0); + assert(arr->nets == 0); + // In this context, address out of bounds returns 0.0 + // instead of an error. + if (address >= arr->vals->get_size()) { + val = vvp_object_t(); + return; + } + + arr->vals->get_word(address, val); + return; + } + + assert(arr->nets); + // Arrays of string nets not implemented! + assert(0); + return; +} + string array_get_word_str(vvp_array_t arr, unsigned address) { if (arr->vals) { @@ -1354,6 +1388,23 @@ void compile_string_array(char*label, char*name, int last, int first) delete[] name; } +void compile_object_array(char*label, char*name, int last, int first) +{ + vpiHandle obj = vpip_make_array(label, name, first, last, true); + + struct __vpiArray*arr = dynamic_cast<__vpiArray*>(obj); + + /* Make the words. */ + arr->vals = new vvp_darray_object(arr->array_count); + arr->vals_width = 1; + + count_real_arrays += 1; + count_real_array_words += arr->array_count; + + free(label); + delete[] name; +} + void compile_net_array(char*label, char*name, int last, int first) { // At this point we don't know the array data type, so we diff --git a/vvp/array.h b/vvp/array.h index 322852408..d1766ed15 100644 --- a/vvp/array.h +++ b/vvp/array.h @@ -45,9 +45,12 @@ extern void array_set_word(vvp_array_t arr, unsigned idx, double val); extern void array_set_word(vvp_array_t arr, unsigned idx, const std::string&val); +extern void array_set_word(vvp_array_t arr, unsigned idx, + const vvp_object_t&val); extern vvp_vector4_t array_get_word(vvp_array_t array, unsigned address); extern double array_get_word_r(vvp_array_t array, unsigned address); +extern void array_get_word_obj(vvp_array_t array, unsigned address, vvp_object_t&val); extern std::string array_get_word_str(vvp_array_t array, unsigned address); /* VPI hooks */ diff --git a/vvp/codes.h b/vvp/codes.h index d439bf2e0..05e4af45d 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -133,6 +133,7 @@ extern bool of_LOAD_REAL(vthread_t thr, vvp_code_t code); extern bool of_LOAD_DAR_R(vthread_t thr, vvp_code_t code); extern bool of_LOAD_DAR_STR(vthread_t thr, vvp_code_t code); extern bool of_LOAD_OBJ(vthread_t thr, vvp_code_t code); +extern bool of_LOAD_OBJA(vthread_t thr, vvp_code_t code); extern bool of_LOAD_STR(vthread_t thr, vvp_code_t code); extern bool of_LOAD_STRA(vthread_t thr, vvp_code_t code); extern bool of_LOAD_VEC(vthread_t thr, vvp_code_t code); @@ -203,6 +204,7 @@ extern bool of_STORE_QB_STR(vthread_t thr, vvp_code_t code); extern bool of_STORE_QF_R(vthread_t thr, vvp_code_t code); extern bool of_STORE_QF_STR(vthread_t thr, vvp_code_t code); extern bool of_STORE_OBJ(vthread_t thr, vvp_code_t code); +extern bool of_STORE_OBJA(vthread_t thr, vvp_code_t code); extern bool of_STORE_PROP_OBJ(vthread_t thr, vvp_code_t code); extern bool of_STORE_PROP_R(vthread_t thr, vvp_code_t code); extern bool of_STORE_PROP_STR(vthread_t thr, vvp_code_t code); @@ -217,6 +219,8 @@ extern bool of_SUBI(vthread_t thr, vvp_code_t code); extern bool of_SUBSTR(vthread_t thr, vvp_code_t code); extern bool of_SUBSTR_V(vthread_t thr, vvp_code_t code); extern bool of_TEST_NUL(vthread_t thr, vvp_code_t code); +extern bool of_TEST_NUL_A(vthread_t thr, vvp_code_t code); +extern bool of_TEST_NUL_OBJ(vthread_t thr, vvp_code_t code); extern bool of_VPI_CALL(vthread_t thr, vvp_code_t code); extern bool of_WAIT(vthread_t thr, vvp_code_t code); extern bool of_WAIT_FORK(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index 4dac7f1de..697c58064 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -180,6 +180,7 @@ static const struct opcode_table_s opcode_table[] = { { "%load/dar/r", of_LOAD_DAR_R, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE}}, { "%load/dar/str",of_LOAD_DAR_STR, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, { "%load/obj", of_LOAD_OBJ, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, + { "%load/obja", of_LOAD_OBJA,2,{OA_ARR_PTR, OA_BIT1, OA_NONE} }, { "%load/real", of_LOAD_REAL,1,{OA_VPI_PTR, OA_NONE, OA_NONE} }, { "%load/str", of_LOAD_STR, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, { "%load/stra", of_LOAD_STRA,2,{OA_ARR_PTR, OA_BIT1, OA_NONE} }, @@ -224,8 +225,8 @@ static const struct opcode_table_s opcode_table[] = { { "%pushv/str", of_PUSHV_STR, 2, {OA_BIT1,OA_BIT2, OA_NONE} }, { "%putc/str/v",of_PUTC_STR_V,3,{OA_FUNC_PTR,OA_BIT1, OA_BIT2} }, { "%qpop/b", of_QPOP_B, 3,{OA_FUNC_PTR,OA_BIT1, OA_BIT2} }, - { "%qpop/f", of_QPOP_F, 3,{OA_FUNC_PTR,OA_BIT1, OA_BIT2} }, { "%qpop/b/str",of_QPOP_B_STR,1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, + { "%qpop/f", of_QPOP_F, 3,{OA_FUNC_PTR,OA_BIT1, OA_BIT2} }, { "%qpop/f/str",of_QPOP_F_STR,1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, { "%release/net",of_RELEASE_NET,3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, { "%release/reg",of_RELEASE_REG,3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, @@ -246,6 +247,7 @@ static const struct opcode_table_s opcode_table[] = { { "%store/dar/r", of_STORE_DAR_R, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, { "%store/dar/str",of_STORE_DAR_STR, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, { "%store/obj", of_STORE_OBJ, 1, {OA_FUNC_PTR,OA_NONE, OA_NONE} }, + { "%store/obja", of_STORE_OBJA, 2, {OA_ARR_PTR, OA_BIT1, OA_NONE} }, { "%store/prop/obj",of_STORE_PROP_OBJ,1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%store/prop/r", of_STORE_PROP_R, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%store/prop/str",of_STORE_PROP_STR,1, {OA_NUMBER, OA_NONE, OA_NONE} }, @@ -263,7 +265,9 @@ static const struct opcode_table_s opcode_table[] = { { "%subi", of_SUBI, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%substr", of_SUBSTR, 2,{OA_BIT1, OA_BIT2, OA_NONE} }, { "%substr/v",of_SUBSTR_V,3,{OA_BIT1, OA_BIT2, OA_NUMBER} }, - { "%test_nul", of_TEST_NUL, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, + { "%test_nul", of_TEST_NUL, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, + { "%test_nul/a", of_TEST_NUL_A, 2,{OA_ARR_PTR, OA_BIT1, OA_NONE} }, + { "%test_nul/obj",of_TEST_NUL_OBJ,0,{OA_NONE, OA_NONE, OA_NONE} }, { "%wait", of_WAIT, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, { "%wait/fork",of_WAIT_FORK,0,{OA_NONE, OA_NONE, OA_NONE} }, { "%xnor", of_XNOR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, diff --git a/vvp/compile.h b/vvp/compile.h index c385e76f0..48b4e6247 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -373,6 +373,8 @@ extern void compile_real_array(char*label, char*name, int last, int first); extern void compile_string_array(char*label, char*name, int last, int first); +extern void compile_object_array(char*label, char*name, + int last, int first); extern void compile_net_array(char*label, char*name, int last, int first); extern void compile_array_alias(char*label, char*name, char*src); diff --git a/vvp/lexor.lex b/vvp/lexor.lex index e197928bf..58848bd76 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -124,6 +124,7 @@ static char* strdupnew(char const *str) ".array/2s" { return K_ARRAY_2S; } ".array/2u" { return K_ARRAY_2U; } ".array/i" { return K_ARRAY_I; } +".array/obj" { return K_ARRAY_OBJ; } ".array/real" { return K_ARRAY_R; } ".array/s" { return K_ARRAY_S; } ".array/str" { return K_ARRAY_STR; } diff --git a/vvp/opcodes.txt b/vvp/opcodes.txt index 6924f7f65..f3361f67b 100644 --- a/vvp/opcodes.txt +++ b/vvp/opcodes.txt @@ -1134,10 +1134,14 @@ values into the vector space. The string value is NOT popped. * %test_nul +* %test_nul/obj This instruction tests the contents of the addressed variable to see if it is null. If it is, set bit 4 to 1. Otherwise, set bit 4 to 0. +The %test_null/obj tests the object on the top of the stack, instead +of any variable. The value in the stack is NOT popped. + This is intended to implement the SystemVerilog expression (==null), where is a class variable. diff --git a/vvp/parse.y b/vvp/parse.y index 2a9753e00..97e5e8665 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -77,7 +77,7 @@ static struct __vpiModPath*modpath_dst = 0; %token K_ARITH_MOD_R K_ARITH_MOD_S %token K_ARITH_MULT K_ARITH_MULT_R K_ARITH_SUB K_ARITH_SUB_R %token K_ARITH_SUM K_ARITH_SUM_R K_ARITH_POW K_ARITH_POW_R K_ARITH_POW_S -%token K_ARRAY K_ARRAY_2U K_ARRAY_2S K_ARRAY_I K_ARRAY_R K_ARRAY_S K_ARRAY_STR K_ARRAY_PORT +%token K_ARRAY K_ARRAY_2U K_ARRAY_2S K_ARRAY_I K_ARRAY_OBJ K_ARRAY_R K_ARRAY_S K_ARRAY_STR K_ARRAY_PORT %token K_CAST_INT K_CAST_REAL K_CAST_REAL_S K_CAST_2 %token K_CLASS %token K_CMP_EEQ K_CMP_EQ K_CMP_EQX K_CMP_EQZ @@ -234,6 +234,9 @@ statement | T_LABEL K_ARRAY_STR T_STRING ',' signed_t_number signed_t_number ';' { compile_string_array($1, $3, $5, $6); } + | T_LABEL K_ARRAY_OBJ T_STRING ',' signed_t_number signed_t_number ';' + { compile_object_array($1, $3, $5, $6); } + | T_LABEL K_ARRAY T_STRING ',' signed_t_number signed_t_number ';' { compile_net_array($1, $3, $5, $6); } diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 3a83e20c4..ae2986364 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -3531,6 +3531,23 @@ bool of_LOAD_OBJ(vthread_t thr, vvp_code_t cp) return true; } +bool of_LOAD_OBJA(vthread_t thr, vvp_code_t cp) +{ + unsigned idx = cp->bit_idx[0]; + unsigned adr = thr->words[idx].w_int; + vvp_object_t word; + + /* The result is 0.0 if the address is undefined. */ + if (thr_get_bit(thr, 4) == BIT4_1) { + ; // Return nil + } else { + array_get_word_obj(cp->array, adr, word); + } + + thr->push_object(word); + return true; +} + /* * %load/real */ @@ -5444,6 +5461,23 @@ bool of_STORE_OBJ(vthread_t thr, vvp_code_t cp) return true; } +/* + * %store/obja + */ +bool of_STORE_OBJA(vthread_t thr, vvp_code_t cp) +{ + unsigned idx = cp->bit_idx[0]; + unsigned adr = thr->words[idx].w_int; + + vvp_object_t val; + thr->pop_object(val); + + array_set_word(cp->array, adr, val); + + return true; +} + + /* * %store/prop/obj * @@ -5798,6 +5832,36 @@ bool of_TEST_NUL(vthread_t thr, vvp_code_t cp) return true; } +bool of_TEST_NUL_A(vthread_t thr, vvp_code_t cp) +{ + unsigned idx = cp->bit_idx[0]; + unsigned adr = thr->words[idx].w_int; + vvp_object_t word; + + /* If the address is undefined, return true. */ + if (thr_get_bit(thr, 4) == BIT4_1) { + thr_put_bit(thr, 4, BIT4_1); + return true; + } + + array_get_word_obj(cp->array, adr, word); + if (word.test_nil()) + thr_put_bit(thr, 4, BIT4_1); + else + thr_put_bit(thr, 4, BIT4_0); + + return true; +} + +bool of_TEST_NUL_OBJ(vthread_t thr, vvp_code_t) +{ + if (thr->peek_object().test_nil()) + thr_put_bit(thr, 4, BIT4_1); + else + thr_put_bit(thr, 4, BIT4_0); + return true; +} + bool of_VPI_CALL(vthread_t thr, vvp_code_t cp) { vpip_execute_vpi_call(thr, cp->handle); diff --git a/vvp/vvp_darray.cc b/vvp/vvp_darray.cc index 9e3e27a8e..247bc0722 100644 --- a/vvp/vvp_darray.cc +++ b/vvp/vvp_darray.cc @@ -43,6 +43,11 @@ void vvp_darray::set_word(unsigned, const string&) cerr << "XXXX set_word(string) not implemented for " << typeid(*this).name() << endl; } +void vvp_darray::set_word(unsigned, const vvp_object_t&) +{ + cerr << "XXXX set_word(vvp_object_t) not implemented for " << typeid(*this).name() << endl; +} + void vvp_darray::get_word(unsigned, vvp_vector4_t&) { cerr << "XXXX get_word(vvp_vector4_t) not implemented for " << typeid(*this).name() << endl; @@ -58,6 +63,11 @@ void vvp_darray::get_word(unsigned, string&) cerr << "XXXX get_word(string) not implemented for " << typeid(*this).name() << endl; } +void vvp_darray::get_word(unsigned, vvp_object_t&) +{ + cerr << "XXXX get_word(vvp_object_t) not implemented for " << typeid(*this).name() << endl; +} + template vvp_darray_atom::~vvp_darray_atom() { } @@ -101,6 +111,32 @@ template class vvp_darray_atom; template class vvp_darray_atom; template class vvp_darray_atom; +vvp_darray_object::~vvp_darray_object() +{ +} + +size_t vvp_darray_object::get_size() const +{ + return array_.size(); +} + +void vvp_darray_object::set_word(unsigned adr, const vvp_object_t&value) +{ + if (adr >= array_.size()) + return; + array_[adr] = value; +} + +void vvp_darray_object::get_word(unsigned adr, vvp_object_t&value) +{ + if (adr >= array_.size()) { + value = vvp_object_t(); + return; + } + + value = array_[adr]; +} + vvp_darray_real::~vvp_darray_real() { } diff --git a/vvp/vvp_darray.h b/vvp/vvp_darray.h index 62f4797b8..54e65ae6c 100644 --- a/vvp/vvp_darray.h +++ b/vvp/vvp_darray.h @@ -42,6 +42,9 @@ class vvp_darray : public vvp_object { virtual void set_word(unsigned adr, const std::string&value); virtual void get_word(unsigned adr, std::string&value); + + virtual void set_word(unsigned adr, const vvp_object_t&value); + virtual void get_word(unsigned adr, vvp_object_t&value); }; template class vvp_darray_atom : public vvp_darray { @@ -86,6 +89,19 @@ class vvp_darray_string : public vvp_darray { std::vector array_; }; +class vvp_darray_object : public vvp_darray { + + public: + inline vvp_darray_object(size_t siz) : array_(siz) { } + ~vvp_darray_object(); + + size_t get_size(void) const; + void set_word(unsigned adr, const vvp_object_t&value); + void get_word(unsigned adr, vvp_object_t&value); + + private: + std::vector array_; +}; class vvp_queue : public vvp_darray {