From 9880fb8d5e56e480cb2f9aa15d6374c71b1e9d1c Mon Sep 17 00:00:00 2001 From: mjoekhan Date: Wed, 1 Jul 2026 12:45:16 +0500 Subject: [PATCH 1/5] SV: class queue/darray property foundation Fold in Windows VPI routing for vpip_format_pretty, fix queue method argument elaboration via elaborate_rval_expr, reject class tasks used as expressions cleanly, and update br1005 now that class queues compile. Review follow-up: move vpip_format_pretty to vpip_format.cc with diagnostic return strings, restore NetNet-based queue method elaboration with a separate property path, drop spurious /devel/ from .gitignore, and bump copyright years on touched files. Formatting pass per inline review: brace style for multi-line if bodies, ||/&& at end-of-line continuations, switch/case indentation, single-line if returns, NetNet-based sys_task_method_ again, and aligned extern decls. --- PExpr.h | 12 +- Statement.h | 28 ++ elab_expr.cc | 354 +++++++++++++++--- elab_sig.cc | 5 - elaborate.cc | 241 ++++++++++-- eval_tree.cc | 20 + ivl.def | 1 + ivl_target.h | 4 + ivtest/gold/br1005.gold | 23 +- ivtest/ivltests/sv_class_queue_prop_methods.v | 39 ++ ivtest/regress-ivl1.list | 1 - ivtest/regress-vvp.list | 3 +- .../sv_class_queue_prop_methods.json | 9 + t-dll-api.cc | 14 +- tgt-vvp/draw_class.c | 1 + tgt-vvp/draw_vpi.c | 34 +- tgt-vvp/eval_real.c | 11 +- tgt-vvp/eval_string.c | 11 +- tgt-vvp/eval_vec4.c | 83 +++- tgt-vvp/stmt_assign.c | 11 +- tgt-vvp/vvp_process.c | 98 +++++ vpi/libvpi.c | 5 + vpi/sys_display.c | 30 ++ vpi_user.h | 7 +- vvp/codes.h | 19 + vvp/compile.cc | 29 +- vvp/compile.h | 4 + vvp/lexor.lex | 1 + vvp/parse.y | 4 +- vvp/vpi_darray.cc | 113 ++++-- vvp/vpi_priv.cc | 1 + vvp/vpi_priv.h | 26 +- vvp/vpip_format.cc | 67 +++- vvp/vthread.cc | 316 ++++++++++++++++ vvp/vvp.def | 1 + 35 files changed, 1470 insertions(+), 156 deletions(-) create mode 100644 ivtest/ivltests/sv_class_queue_prop_methods.v create mode 100644 ivtest/vvp_tests/sv_class_queue_prop_methods.json diff --git a/PExpr.h b/PExpr.h index 6f6eaf07e..5cd233726 100644 --- a/PExpr.h +++ b/PExpr.h @@ -37,6 +37,7 @@ class NetExpr; class NetScope; class PPackage; struct symbol_search_results; +class netclass_t; /* * The PExpr class hierarchy supports the description of @@ -974,7 +975,16 @@ class PECallFunction : public PExpr { unsigned elaborate_arguments_(Design*des, NetScope*scope, const NetFuncDef*def, bool need_const, std::vector&parms, - unsigned parm_off) const; + unsigned parm_off, + const std::vector*src_parms = nullptr) const; + + NetExpr* elaborate_class_method_net_(Design*des, NetScope*scope, + NetNet*net, const netclass_t*class_type, + perm_string method_name, + const std::vector*src_parms) const; + + NetExpr* elaborate_expr_method_chained_(Design*des, NetScope*scope, + symbol_search_results&search_results) const; }; /* diff --git a/Statement.h b/Statement.h index d410caaef..03fee609b 100644 --- a/Statement.h +++ b/Statement.h @@ -40,6 +40,8 @@ class NetCAssign; class NetDeassign; class NetForce; class NetScope; +class NetNet; +class netdarray_t; /* * The PProcess is the root of a behavioral process. Each process gets @@ -261,16 +263,42 @@ class PCallTask : public Statement { perm_string method_name, const char *sys_task_name, const std::vector &parm_names = {}) const; + NetProc*elaborate_sys_task_property_method_(Design*des, NetScope*scope, + NetNet*net, int property_idx, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names = {}) const; NetProc*elaborate_queue_method_(Design*des, NetScope*scope, NetNet*net, perm_string method_name, const char *sys_task_name, const std::vector &parm_names) const; + NetProc*elaborate_queue_property_method_(Design*des, NetScope*scope, + NetNet*net, int property_idx, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names) const; NetProc*elaborate_method_func_(NetScope*scope, NetNet*net, ivl_type_t type, perm_string method_name, const char*sys_task_name) const; + NetProc*elaborate_method_property_func_(NetScope*scope, + NetNet*net, int property_idx, + ivl_type_t type, + perm_string method_name, + const char*sys_task_name) const; + NetProc*elaborate_queue_method_expr_(Design*des, NetScope*scope, + NetExpr*queue_base, + const netdarray_t*use_darray, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names) const; + NetProc*elaborate_method_func_expr_(NetScope*scope, + NetExpr*queue_base, + ivl_type_t type, + perm_string method_name, + const char*sys_task_name) const; bool test_task_calls_ok_(Design*des, const NetScope*scope) const; PPackage*package_; diff --git a/elab_expr.cc b/elab_expr.cc index 1035ebe8a..0a03bb3f9 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -461,7 +461,6 @@ NetExpr* PEAssignPattern::elaborate_expr(Design*des, NetScope*, unsigned, unsign cerr << get_fileline() << ": : Expression is: " << *this << endl; des->errors += 1; - ivl_assert(*this, 0); return 0; } @@ -1540,12 +1539,36 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, << "search_results.net->net_type: " << *search_results.net->net_type() << endl; } - // Don't support multiple chained methods yet. + // Chained class methods: obj.m1().m2() — width is that of the last call. if (search_results.path_tail.size() > 1) { + if (search_results.net && search_results.net->data_type()==IVL_VT_CLASS) { + const netclass_t* cur_class = dynamic_cast(search_results.type); + if (cur_class) { + size_t ntail = search_results.path_tail.size(); + size_t idx = 0; + for (auto it = search_results.path_tail.begin() + ; it != search_results.path_tail.end() ; ++it, ++idx) { + perm_string mname = it->name; + NetScope*meth = cur_class->method_from_name(mname); + if (meth == 0) return 0; + const NetNet*res = meth->find_signal(meth->basename()); + if (res == 0) return 0; + if (idx + 1 == ntail) { + expr_type_ = res->data_type(); + expr_width_ = res->vector_width(); + min_width_ = expr_width_; + signed_flag_ = res->get_signed(); + return expr_width_; + } + cur_class = dynamic_cast(res->net_type()); + if (cur_class == 0) return 0; + } + } + } if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::test_width_method_: " << "Chained path tail (" << search_results.path_tail - << ") not supported." << endl; + << ") not supported for this expression type." << endl; } return 0; } @@ -1582,8 +1605,8 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, // the expr_width for the return value of the queue method. For example: // .x.size(); // In this example, x is a queue. - if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE - && search_results.path_head.back().index.empty()) { + if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE && + search_results.path_head.back().index.empty()) { const NetNet*net = search_results.net; const netdarray_t*darray = net->darray_type(); @@ -2732,7 +2755,6 @@ NetExpr* PEIdent::elaborate_expr_class_field_(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { - const netclass_t *class_type = dynamic_cast(sr.type); const name_component_t comp = sr.path_tail.front(); @@ -2810,6 +2832,27 @@ NetExpr* PEIdent::elaborate_expr_class_field_(Design*des, NetScope*scope, canon_index = make_canonical_index(des, scope, this, comp.index, tmp_ua, false); } + } else if (dynamic_cast(tmp_type)) { + /* Queue or dynamic-array property: optional index, e.g. c.q[i] + * or whole-container reference c.q. */ + const std::list*idx_list = &comp.index; + if (idx_list->empty() && !path_.back().index.empty()) { + idx_list = &path_.back().index; + } + if (idx_list->size() == 0) { + canon_index = nullptr; + } else if (idx_list->size() != 1) { + cerr << get_fileline() << ": error: " + << "Got " << idx_list->size() << " indices, " + << "expecting 0 or 1 for the queue/dynamic-array property " + << class_type->get_prop_name(pidx) << "." << endl; + des->errors++; + } else { + const index_component_t&use_index = idx_list->back(); + ivl_assert(*this, use_index.msb != 0); + ivl_assert(*this, use_index.lsb == 0); + canon_index = elab_and_eval(des, scope, use_index.msb, -1, false); + } } if (debug_elaborate && canon_index) { @@ -3128,13 +3171,16 @@ NetExpr* PECallFunction::elaborate_base_(Design*des, NetScope*scope, NetScope*ds unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, const NetFuncDef*def, bool need_const, vector&parms, - unsigned parm_off) const + unsigned parm_off, + const vector*src_parms) const { unsigned parm_errors = 0; unsigned missing_parms = 0; + const vector&use_parms = src_parms ? *src_parms : parms_; + const unsigned parm_count = parms.size() - parm_off; - const unsigned actual_count = parms_.size(); + const unsigned actual_count = use_parms.size(); if (parm_count == 0 && actual_count == 0) return 0; @@ -3147,7 +3193,7 @@ unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, des->errors += 1; } - auto args = map_named_args(des, def, parms_, parm_off); + auto args = map_named_args(des, def, use_parms, parm_off); for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { unsigned pidx = idx + parm_off; @@ -3202,6 +3248,168 @@ unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, return parm_errors; } +/* + * Elaborate a call to a single class method, optionally using an alternate + * argument vector (for chained calls where inner methods use no user args). + */ +NetExpr* PECallFunction::elaborate_class_method_net_(Design*des, NetScope*scope, + NetNet*net, const netclass_t*class_type, + perm_string method_name, + const vector*src_parms) const +{ + NetScope*method = class_type->method_from_name(method_name); + + if (method == 0) { + cerr << get_fileline() << ": Error: " << method_name + << " is not a method of class " << class_type->get_name() + << "." << endl; + des->errors += 1; + return 0; + } + + if (method->type() != NetScope::FUNC) { + cerr << get_fileline() << ": error: Method " << method_name + << " of class " << class_type->get_name() + << " is not a function." << endl; + des->errors += 1; + return 0; + } + + const NetFuncDef*def = method->func_def(); + ivl_assert(*this, def); + + NetNet*res = method->find_signal(method->basename()); + ivl_assert(*this, res); + + vector parms(def->port_count()); + ivl_assert(*this, def->port_count() >= 1); + + NetESignal*ethis = new NetESignal(net); + ethis->set_line(*this); + parms[0] = ethis; + + elaborate_arguments_(des, scope, def, false, parms, 1, src_parms); + + NetESignal*eres = new NetESignal(res); + NetEUFunc*call = new NetEUFunc(scope, method, eres, parms, false); + call->set_line(*this); + return call; +} + +/* + * Handle obj.m1().m2(args): arguments apply only to the last call; intermediate + * methods must be class methods returning class handles. + */ +NetExpr* PECallFunction::elaborate_expr_method_chained_(Design*des, NetScope*scope, + symbol_search_results&search_results) const +{ + static const vector no_parms; + + if (search_results.par_val && search_results.type) { + cerr << get_fileline() << ": sorry: " + << "Method name nesting is not supported for parameter methods yet." << endl; + des->errors += 1; + return 0; + } + + NetExpr* sub_expr = 0; + if (search_results.net) { + NetESignal*tmp = new NetESignal(search_results.net); + tmp->set_line(*this); + sub_expr = tmp; + } + + if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE && + search_results.path_head.back().index.size()==1) { + + const NetNet*net = search_results.net; + const netdarray_t*darray = net->darray_type(); + const index_component_t&use_index = search_results.path_head.back().index.back(); + ivl_assert(*this, use_index.msb != 0); + ivl_assert(*this, use_index.lsb == 0); + + NetExpr*mux = elab_and_eval(des, scope, use_index.msb, -1, false); + if (!mux) + return 0; + + NetESelect*tmp = new NetESelect(sub_expr, mux, darray->element_width(), darray->element_type()); + tmp->set_line(*this); + sub_expr = tmp; + } + + if (!sub_expr) { + cerr << get_fileline() << ": internal error: " + << "Method chain elaborate lost base sub-expression." << endl; + des->errors += 1; + return 0; + } + + if (sub_expr->expr_type() != IVL_VT_CLASS) { + cerr << get_fileline() << ": sorry: " + << "Method name nesting for this expression type is not supported yet " + << "(only class handle chains)." << endl; + cerr << get_fileline() << ": : " + << "method path: " << search_results.path_tail << endl; + des->errors += 1; + return 0; + } + + NetNet* cur_net = search_results.net; + const netclass_t* cur_class = dynamic_cast(search_results.type); + if (cur_class == 0) + cur_class = dynamic_cast(cur_net->net_type()); + if (cur_class == 0) { + cerr << get_fileline() << ": internal error: " + << "IVL_VT_CLASS net without netclass_t type." << endl; + des->errors += 1; + return 0; + } + + size_t chain_len = search_results.path_tail.size() - 1; + size_t step_idx = 0; + for (auto it = search_results.path_tail.begin() + ; step_idx < chain_len ; ++it, ++step_idx) { + perm_string method_name = it->name; + + NetExpr* step = elaborate_class_method_net_(des, scope, cur_net, cur_class, + method_name, &no_parms); + if (step == 0) { + return 0; + } + + NetEUFunc*uf = dynamic_cast (step); + if (uf == 0) { + cerr << get_fileline() << ": internal error: " + << "expected class method call to be NetEUFunc." << endl; + des->errors += 1; + return 0; + } + + const NetESignal*rs = uf->result_sig(); + ivl_assert(*this, rs); + cur_net = const_cast(rs->sig()); + ivl_assert(*this, cur_net); + cur_class = dynamic_cast(cur_net->net_type()); + if (cur_class == 0) { + cerr << get_fileline() << ": sorry: " + << "Method chaining requires intermediate results to be class handles; " + << "after `" << method_name << "` the type is not a class." << endl; + des->errors += 1; + return 0; + } + } + + symbol_search_results tail_sr; + tail_sr.scope = search_results.scope; + tail_sr.path_head = search_results.path_head; + tail_sr.path_tail.clear(); + tail_sr.path_tail.push_back(search_results.path_tail.back()); + tail_sr.net = cur_net; + tail_sr.type = cur_class; + + return elaborate_expr_method_(des, scope, tail_sr); +} + /* * Look for a method of a given object. The search_results gives us the * information we need to look into this case: The net is the object that will @@ -3225,12 +3433,82 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, return 0; } + // e.g. c.q.size() — path_tail is {q, size}; q is a queue-typed property + if (search_results.path_tail.size() == 2) { + const netclass_t*cls = dynamic_cast(search_results.type); + if (!cls && search_results.net && search_results.net->net_type()) { + cls = dynamic_cast(search_results.net->net_type()); + } + if (cls && search_results.net) { + perm_string prop_name = search_results.path_tail.front().name; + int pidx = cls->property_idx_from_name(prop_name); + if (pidx >= 0) { + ivl_type_t ptype = cls->get_prop_type(pidx); + if (ptype && dynamic_cast(ptype)) { + NetEProperty*prop = new NetEProperty(search_results.net, pidx, nullptr); + prop->set_line(*this); + perm_string method_name = search_results.path_tail.back().name; + const netqueue_t*queue = dynamic_cast(ptype); + ivl_assert(*this, queue); + ivl_type_t element_type = queue->element_type(); + if (method_name == "size") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: size() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$size", &netvector_t::atom2u32, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + if (method_name == "pop_back") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: pop_back() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$ivl_queue_method$pop_back", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + if (method_name == "pop_front") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: pop_front() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$ivl_queue_method$pop_front", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + } + if (ptype && ptype->base_type() == IVL_VT_DARRAY) { + NetEProperty*prop = new NetEProperty(search_results.net, pidx, nullptr); + prop->set_line(*this); + perm_string method_name = search_results.path_tail.back().name; + if (method_name == "size") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: size() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$size", &netvector_t::atom2u32, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + } + } + } + } + if (search_results.path_tail.size() > 1) { - cerr << get_fileline() << ": sorry: " - << "Method name nesting is not supported yet." << endl; - cerr << get_fileline() << ": : " - << "method path: " << search_results.path_tail << endl; - return 0; + return elaborate_expr_method_chained_(des, scope, search_results); } if (debug_elaborate) { @@ -3271,8 +3549,8 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, // .x[e].len() // If x is a queue of strings, then x[e] is a string. Elaborate the x[e] // expression and pass that to the len() method. - if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE - && search_results.path_head.back().index.size()==1) { + if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE && + search_results.path_head.back().index.size()==1) { const NetNet*net = search_results.net; const netdarray_t*darray = net->darray_type(); @@ -3326,8 +3604,8 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, // Queue methods. This handles the case that the located signal is a // QUEUE object, and there is a method. - if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE - && search_results.path_head.back().index.size()==0) { + if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE && + search_results.path_head.back().index.size()==0) { // Get the method name that we are looking for. perm_string method_name = search_results.path_tail.back().name; @@ -3394,49 +3672,13 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, // Class methods. Generate function call to the class method. if (sub_expr->expr_type()==IVL_VT_CLASS) { - // Get the method name that we are looking for. perm_string method_name = search_results.path_tail.back().name; NetNet*net = search_results.net; const netclass_t*class_type = dynamic_cast(search_results.type); ivl_assert(*this, class_type); - NetScope*method = class_type->method_from_name(method_name); - - if (method == 0) { - cerr << get_fileline() << ": Error: " << method_name - << " is not a method of class " << class_type->get_name() - << "." << endl; - des->errors += 1; - return 0; - } - - if (method->type() != NetScope::FUNC) { - cerr << get_fileline() << ": error: Method " << method_name - << " of class " << class_type->get_name() - << " is not a function." << endl; - des->errors++; - return nullptr; - } - - const NetFuncDef*def = method->func_def(); - ivl_assert(*this, def); - - NetNet*res = method->find_signal(method->basename()); - ivl_assert(*this, res); - - vector parms(def->port_count()); - ivl_assert(*this, def->port_count() >= 1); - - NetESignal*ethis = new NetESignal(net); - ethis->set_line(*this); - parms[0] = ethis; - - elaborate_arguments_(des, scope, def, false, parms, 1); - - NetESignal*eres = new NetESignal(res); - NetEUFunc*call = new NetEUFunc(scope, method, eres, parms, false); - call->set_line(*this); - return call; + return elaborate_class_method_net_(des, scope, net, class_type, + method_name, nullptr); } // String methods. diff --git a/elab_sig.cc b/elab_sig.cc index 5b752c617..f8cb069ef 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -404,11 +404,6 @@ void netclass_t::elaborate_sig(Design*des, PClass*pclass) << " type=" << *use_type << endl; } - if (dynamic_cast (use_type)) { - cerr << cur->second.get_fileline() << ": sorry: " - << "Queues inside classes are not yet supported." << endl; - des->errors++; - } set_property(cur->first, cur->second.qual, use_type); if (! cur->second.qual.test_static()) diff --git a/elaborate.cc b/elaborate.cc index e4894fd6e..4d48fa546 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -4074,19 +4074,66 @@ NetProc* PCallTask::elaborate_sys_task_method_(Design*des, NetScope*scope, return sys; } -/* - * This private method is called to elaborate queue push methods. The - * sys_task_name is the internal system-task name to use. - */ -NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, - NetNet*net, - perm_string method_name, - const char *sys_task_name, - const std::vector &parm_names) const +NetProc* PCallTask::elaborate_sys_task_property_method_(Design*des, NetScope*scope, + NetNet*net, int property_idx, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names) const { - NetESignal*sig = new NetESignal(net); - sig->set_line(*this); + NetEProperty*prop = new NetEProperty(net, property_idx, 0); + prop->set_line(*this); + unsigned nparms = parms_.size(); + + vectorargv (1 + nparms); + argv[0] = prop; + + if (method_name == "delete") { + const netclass_t*cls = dynamic_cast(net->net_type()); + ivl_assert(*this, cls); + ivl_type_t pt = cls->get_prop_type(property_idx); + bool is_queue = pt && pt->base_type() == IVL_VT_QUEUE; + if (is_queue) { + if (nparms > 1) { + cerr << get_fileline() << ": error: queue delete() " + << "method takes zero or one argument." << endl; + des->errors += 1; + } + } else if (nparms > 0) { + cerr << get_fileline() << ": error: darray delete() " + << "method takes no arguments." << endl; + des->errors += 1; + } + } else if (parm_names.size() != parms_.size()) { + cerr << get_fileline() << ": error: " << method_name + << "() method takes " << parm_names.size() << " arguments, got " + << parms_.size() << "." << endl; + des->errors++; + } + + auto args = map_named_args(des, parm_names, parms_); + for (unsigned idx = 0 ; idx < nparms ; idx += 1) { + argv[idx + 1] = elab_sys_task_arg(des, scope, method_name, + idx, args[idx]); + } + + NetSTask*sys = new NetSTask(sys_task_name, IVL_SFUNC_AS_TASK_IGNORE, argv); + sys->set_line(*this); + return sys; +} + +/* + * Common implementation for queue push/insert methods. The queue_base + * expression is either a NetESignal or NetEProperty; use_darray + * supplies the element type. + */ +NetProc* PCallTask::elaborate_queue_method_expr_(Design*des, NetScope*scope, + NetExpr*queue_base, + const netdarray_t*use_darray, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names) const +{ unsigned nparms = parms_.size(); // insert() requires two arguments. if ((method_name == "insert") && (nparms != 2)) { @@ -4100,10 +4147,10 @@ NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, << "() method requires a single argument." << endl; des->errors += 1; } - ivl_type_t element_type = net->queue_type()->element_type(); + ivl_type_t element_type = use_darray->element_type(); unsigned expected_nparms = method_name == "insert" ? 2 : 1; vectorargv (expected_nparms+1); - argv[0] = sig; + argv[0] = queue_base; auto args = map_named_args(des, parm_names, parms_); if (method_name != "insert") { @@ -4144,14 +4191,55 @@ NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, return sys; } +/* + * This private method is called to elaborate queue push methods. The + * sys_task_name is the internal system-task name to use. + */ +NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, + NetNet*net, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names) const +{ + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + + const netdarray_t*use_darray = net->darray_type(); + ivl_assert(*this, use_darray); + + return elaborate_queue_method_expr_(des, scope, sig, use_darray, + method_name, sys_task_name, + parm_names); +} + +NetProc* PCallTask::elaborate_queue_property_method_(Design*des, NetScope*scope, + NetNet*net, int property_idx, + perm_string method_name, + const char *sys_task_name, + const std::vector &parm_names) const +{ + const netclass_t*cls = dynamic_cast(net->net_type()); + ivl_assert(*this, cls); + ivl_type_t ptype = cls->get_prop_type(property_idx); + const netdarray_t*use_darray = dynamic_cast(ptype); + ivl_assert(*this, use_darray); + + NetEProperty*prop = new NetEProperty(net, property_idx, 0); + prop->set_line(*this); + + return elaborate_queue_method_expr_(des, scope, prop, use_darray, + method_name, sys_task_name, + parm_names); +} + /* * This is used for array/queue function methods called as tasks. */ -NetProc* PCallTask::elaborate_method_func_(NetScope*scope, - NetNet*net, - ivl_type_t type, - perm_string method_name, - const char*sys_task_name) const +NetProc* PCallTask::elaborate_method_func_expr_(NetScope*scope, + NetExpr*queue_base, + ivl_type_t type, + perm_string method_name, + const char*sys_task_name) const { if (!void_cast_) { cerr << get_fileline() << ": warning: method function '" @@ -4161,9 +4249,7 @@ NetProc* PCallTask::elaborate_method_func_(NetScope*scope, // Generate the function. NetESFunc*sys_expr = new NetESFunc(sys_task_name, type, 1); sys_expr->set_line(*this); - NetESignal*arg = new NetESignal(net); - arg->set_line(*net); - sys_expr->parm(0, arg); + sys_expr->parm(0, queue_base); // Create a L-value that matches the function return type. NetNet*tmp; tmp = new NetNet(scope, scope->local_symbol(), NetNet::REG, type); @@ -4175,6 +4261,30 @@ NetProc* PCallTask::elaborate_method_func_(NetScope*scope, return cur; } +NetProc* PCallTask::elaborate_method_func_(NetScope*scope, + NetNet*net, + ivl_type_t type, + perm_string method_name, + const char*sys_task_name) const +{ + NetESignal*arg = new NetESignal(net); + arg->set_line(*this); + return elaborate_method_func_expr_(scope, arg, type, + method_name, sys_task_name); +} + +NetProc* PCallTask::elaborate_method_property_func_(NetScope*scope, + NetNet*net, int property_idx, + ivl_type_t type, + perm_string method_name, + const char*sys_task_name) const +{ + NetEProperty*prop = new NetEProperty(net, property_idx, 0); + prop->set_line(*this); + return elaborate_method_func_expr_(scope, prop, type, + method_name, sys_task_name); +} + NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, bool add_this_flag) const { @@ -4223,13 +4333,92 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, << net->name() << ".data_type() --> " << net->data_type() << endl; } + // Class handle with one path_tail component: queue/darray property method + // e.g. c.q.push_back(x) => sr.path_tail = {q}, method_name = push_back + if (sr.path_tail.size() == 1) { + const netclass_t*cls = dynamic_cast(sr.type); + if (!cls && net->net_type()) + cls = dynamic_cast(net->net_type()); + if (cls) { + perm_string prop_name = sr.path_tail.front().name; + int pidx = cls->property_idx_from_name(prop_name); + if (pidx >= 0) { + ivl_type_t ptype = cls->get_prop_type(pidx); + if (ptype && dynamic_cast(ptype)) { + const netdarray_t*use_darray = dynamic_cast(ptype); + ivl_assert(*this, use_darray); + + if (method_name == "push_back") { + static const std::vector parm_names = { + perm_string::literal("item") + }; + return elaborate_queue_property_method_(des, scope, net, pidx, + method_name, + "$ivl_queue_method$push_back", + parm_names); + } + if (method_name == "push_front") { + static const std::vector parm_names = { + perm_string::literal("item") + }; + return elaborate_queue_property_method_(des, scope, net, pidx, + method_name, + "$ivl_queue_method$push_front", + parm_names); + } + if (method_name == "insert") { + static const std::vector parm_names = { + perm_string::literal("index"), + perm_string::literal("item") + }; + return elaborate_queue_property_method_(des, scope, net, pidx, + method_name, + "$ivl_queue_method$insert", + parm_names); + } + if (method_name == "pop_front") { + return elaborate_method_property_func_(scope, net, pidx, + use_darray->element_type(), + method_name, + "$ivl_queue_method$pop_front"); + } + if (method_name == "pop_back") { + return elaborate_method_property_func_(scope, net, pidx, + use_darray->element_type(), + method_name, + "$ivl_queue_method$pop_back"); + } + if (method_name == "size") { + return elaborate_method_property_func_(scope, net, pidx, + &netvector_t::atom2s32, + method_name, "$size"); + } + } else if (ptype && ptype->base_type() == IVL_VT_DARRAY) { + if (method_name == "delete") { + static const std::vector parm_names = { + perm_string::literal("index") + }; + return elaborate_sys_task_property_method_(des, scope, net, pidx, + method_name, + "$ivl_darray_method$delete", + parm_names); + } + if (method_name == "size") { + return elaborate_method_property_func_(scope, net, pidx, + &netvector_t::atom2s32, + method_name, "$size"); + } + } + } + } + } + // Is this a method of a "string" type? if (dynamic_cast(net->net_type())) { if (method_name == "itoa") { static const std::vector parm_names = { perm_string::literal("i") }; - return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_string_method$itoa", parm_names); @@ -4237,7 +4426,6 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, static const std::vector parm_names = { perm_string::literal("i") }; - return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_string_method$hextoa", parm_names); @@ -4245,7 +4433,6 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, static const std::vector parm_names = { perm_string::literal("i") }; - return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_string_method$octtoa", parm_names); @@ -4253,7 +4440,6 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, static const std::vector parm_names = { perm_string::literal("i") }; - return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_string_method$bintoa", parm_names); @@ -4261,7 +4447,6 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, static const std::vector parm_names = { perm_string::literal("r") }; - return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_string_method$realtoa", parm_names); @@ -4274,7 +4459,6 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, static const std::vector parm_names = { perm_string::literal("index") }; - return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_darray_method$delete", parm_names); @@ -4311,7 +4495,6 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, } if (net->queue_type()) { - const netdarray_t*use_darray = net->darray_type(); if (method_name == "push_back") { static const std::vector parm_names = { perm_string::literal("item") @@ -4338,11 +4521,13 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, "$ivl_queue_method$insert", parm_names); } else if (method_name == "pop_front") { + const netdarray_t*use_darray = net->darray_type(); return elaborate_method_func_(scope, net, use_darray->element_type(), method_name, "$ivl_queue_method$pop_front"); } else if (method_name == "pop_back") { + const netdarray_t*use_darray = net->darray_type(); return elaborate_method_func_(scope, net, use_darray->element_type(), method_name, diff --git a/eval_tree.cc b/eval_tree.cc index 7cbb49321..cab465892 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -26,6 +26,7 @@ # include # include "netlist.h" +# include "netclass.h" # include "ivl_assert.h" # include "netmisc.h" @@ -2182,6 +2183,25 @@ static bool get_array_info(const NetExpr*arg, long dim, left = range.get_msb(); right = range.get_lsb(); return false; + } + /* Class property (e.g. queue field): size is dynamic; defer to runtime + * instead of folding to all-X in evaluate_array_funcs_. */ + if (const NetEProperty*prop = dynamic_cast(arg)) { + const NetNet*obj = prop->get_sig(); + const netclass_t*cls = dynamic_cast(obj->net_type()); + if (cls == 0) return true; + ivl_type_t ptype = cls->get_prop_type(prop->property_idx()); + if (ptype == 0) return true; + switch (ptype->base_type()) { + case IVL_VT_DARRAY: + case IVL_VT_QUEUE: + case IVL_VT_STRING: + defer = true; + return true; + default: + break; + } + return true; } /* The argument must be a signal that has enough dimensions. */ const NetESignal*esig = dynamic_cast(arg); diff --git a/ivl.def b/ivl.def index 867d832f4..f95572ba6 100644 --- a/ivl.def +++ b/ivl.def @@ -337,6 +337,7 @@ ivl_type_packed_width ivl_type_prop_name ivl_type_prop_type ivl_type_properties +ivl_type_queue_max ivl_type_signed ivl_udp_init diff --git a/ivl_target.h b/ivl_target.h index b33ea9aa4..b4eec2c3b 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -2400,6 +2400,10 @@ extern int ivl_type_properties(ivl_type_t net); extern const char* ivl_type_prop_name(ivl_type_t net, int idx); extern ivl_type_t ivl_type_prop_type(ivl_type_t net, int idx); +/* Maximum element count for a queue type (0 = unbounded). Only valid + * when ivl_type_base(net) == IVL_VT_QUEUE. */ +extern unsigned ivl_type_queue_max(ivl_type_t net); + #if defined(__MINGW32__) || defined (__CYGWIN__) # define DLLEXPORT __declspec(dllexport) diff --git a/ivtest/gold/br1005.gold b/ivtest/gold/br1005.gold index e59c1def5..c054f026b 100644 --- a/ivtest/gold/br1005.gold +++ b/ivtest/gold/br1005.gold @@ -1,18 +1,5 @@ -./ivltests/br1005.v:2: sorry: Queues inside classes are not yet supported. -./ivltests/br1005.v:15: error: Enable of unknown task ``a.q.push_back''. -./ivltests/br1005.v:16: error: Enable of unknown task ``a.q.push_back''. -./ivltests/br1005.v:17: error: Enable of unknown task ``a.q.push_back''. -./ivltests/br1005.v:18: error: Enable of unknown task ``a.q.push_back''. -./ivltests/br1005.v:19: sorry: Method name nesting is not supported yet. -./ivltests/br1005.v:19: : method path: q.pop_front -./ivltests/br1005.v:19: error: Object test.a has no method "q.pop_front(...)". -./ivltests/br1005.v:22: sorry: Method name nesting is not supported yet. -./ivltests/br1005.v:22: : method path: q.pop_front -./ivltests/br1005.v:22: error: Object test.a has no method "q.pop_front(...)". -./ivltests/br1005.v:25: sorry: Method name nesting is not supported yet. -./ivltests/br1005.v:25: : method path: q.pop_front -./ivltests/br1005.v:25: error: Object test.a has no method "q.pop_front(...)". -./ivltests/br1005.v:28: sorry: Method name nesting is not supported yet. -./ivltests/br1005.v:28: : method path: q.pop_front -./ivltests/br1005.v:28: error: Object test.a has no method "q.pop_front(...)". -9 error(s) during elaboration. +1 +2 +3 +4 +PASSED diff --git a/ivtest/ivltests/sv_class_queue_prop_methods.v b/ivtest/ivltests/sv_class_queue_prop_methods.v new file mode 100644 index 000000000..47d879bd7 --- /dev/null +++ b/ivtest/ivltests/sv_class_queue_prop_methods.v @@ -0,0 +1,39 @@ +// Regression: queue-typed class properties — push_front/push_back and +// pop_front/pop_back. (VVP asm must recognize %store/prop/qf/* and +// %qpop/prop/*; opcode_table must stay lexicographically sorted.) + +module test; + + bit failed = 1'b0; + + `define check(val, exp) do \ + if (val !== exp) begin \ + $display("FAILED(%0d). expected %0d, got %0d", `__LINE__, exp, val); \ + failed = 1'b1; \ + end \ + while(0) + + class C; + int q[$]; + endclass + + C c; + int t; + + initial begin + c = new; + c.q.push_back(1); + c.q.push_front(0); + c.q.push_back(2); + t = c.q.pop_back(); + `check(t, 32'd2); + `check(c.q.size(), 32'd2); + t = c.q.pop_front(); + `check(t, 32'd0); + `check(c.q[0], 32'd1); + + if (!failed) begin + $display("PASSED"); + end + end +endmodule diff --git a/ivtest/regress-ivl1.list b/ivtest/regress-ivl1.list index af0a5b3e5..599805f1c 100644 --- a/ivtest/regress-ivl1.list +++ b/ivtest/regress-ivl1.list @@ -274,7 +274,6 @@ array_lval_select3a CE ivltests br605a EF ivltests br605b EF ivltests br971 EF ivltests -br1005 CE,-g2009 ivltests br1015b CE,-g2009 ivltests br_ml20150315b CE,-g2009 ivltests sv_deferred_assert1 CE,-g2009 ivltests gold=sv_deferred_assert1.gold diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 424ce1cc6..79fcdcab8 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -284,6 +284,7 @@ sv_class_prop_nest_obj1 vvp_tests/sv_class_prop_nest_obj1.json sv_class_prop_nest_real1 vvp_tests/sv_class_prop_nest_real1.json sv_class_prop_nest_str1 vvp_tests/sv_class_prop_nest_str1.json sv_class_prop_nest_vec1 vvp_tests/sv_class_prop_nest_vec1.json +sv_class_queue_prop_methods vvp_tests/sv_class_queue_prop_methods.json sv_class_task_expr_fail vvp_tests/sv_class_task_expr_fail.json sv_const1 vvp_tests/sv_const1.json sv_const2 vvp_tests/sv_const2.json @@ -375,9 +376,9 @@ sv_queue_method_push_back_too_few_arg_fail vvp_tests/sv_queue_method_push_back_t sv_queue_method_push_back_too_many_arg_fail vvp_tests/sv_queue_method_push_back_too_many_arg_fail.json sv_queue_method_push_front_too_few_arg_fail vvp_tests/sv_queue_method_push_front_too_few_arg_fail.json sv_queue_method_push_front_too_many_arg_fail vvp_tests/sv_queue_method_push_front_too_many_arg_fail.json -sv_string_method_substr_too_few_arg_fail vvp_tests/sv_string_method_substr_too_few_arg_fail.json sv_soft_packed_union vvp_tests/sv_soft_packed_union.json sv_soft_packed_union_fail1 vvp_tests/sv_soft_packed_union_fail1.json +sv_string_method_substr_too_few_arg_fail vvp_tests/sv_string_method_substr_too_few_arg_fail.json sv_super_member_fail vvp_tests/sv_super_member_fail.json sv_type_identifier_ams_name_fields vvp_tests/sv_type_identifier_ams_name_fields.json sv_type_identifier_config_name vvp_tests/sv_type_identifier_config_name.json diff --git a/ivtest/vvp_tests/sv_class_queue_prop_methods.json b/ivtest/vvp_tests/sv_class_queue_prop_methods.json new file mode 100644 index 000000000..735de2f79 --- /dev/null +++ b/ivtest/vvp_tests/sv_class_queue_prop_methods.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_class_queue_prop_methods.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} diff --git a/t-dll-api.cc b/t-dll-api.cc index 7a33018f8..4e586bb0d 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2025 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2026 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * Copyright (c) 2016 CERN Michele Castellana (michele.castellana@cern.ch) * @@ -25,6 +25,7 @@ # include "discipline.h" # include "netclass.h" # include "netdarray.h" +# include "netqueue.h" # include "netenum.h" # include "netvector.h" # include @@ -3308,6 +3309,17 @@ extern "C" ivl_type_t ivl_type_prop_type(ivl_type_t net, int idx) return class_type->get_prop_type(idx); } +extern "C" unsigned ivl_type_queue_max(ivl_type_t net) +{ + if (net == 0) return 0; + if (const netqueue_t*que = dynamic_cast(net)) { + long max_idx = que->max_idx(); + if (max_idx < 0) return 0; + return (unsigned)(max_idx + 1); + } + return 0; +} + extern "C" int ivl_type_signed(ivl_type_t net) { assert(net); diff --git a/tgt-vvp/draw_class.c b/tgt-vvp/draw_class.c index 69431d7b6..f993dfc46 100644 --- a/tgt-vvp/draw_class.c +++ b/tgt-vvp/draw_class.c @@ -58,6 +58,7 @@ static void show_prop_type(ivl_type_t ptype) show_prop_type_vector(ptype); break; case IVL_VT_DARRAY: + case IVL_VT_QUEUE: case IVL_VT_CLASS: fprintf(vvp_out, "\"o\""); if (packed_dimensions > 0) { diff --git a/tgt-vvp/draw_vpi.c b/tgt-vvp/draw_vpi.c index a49ffb308..82587b25d 100644 --- a/tgt-vvp/draw_vpi.c +++ b/tgt-vvp/draw_vpi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2020 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2026 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 @@ -375,6 +375,20 @@ static void draw_vpi_taskfunc_args(const char*call_string, } break; + case IVL_EX_PROPERTY: + if (ivl_expr_oper1(expr) == 0 && + (ivl_expr_value(expr) == IVL_VT_QUEUE || + ivl_expr_value(expr) == IVL_VT_DARRAY)) { + ivl_signal_t clas = ivl_expr_signal(expr); + unsigned pidx = ivl_expr_property_idx(expr); + unsigned isq = ivl_expr_value(expr) == IVL_VT_QUEUE ? 1U : 0U; + snprintf(buffer, sizeof buffer, "&PQ", + clas, pidx, isq); + args[idx].text = strdup(buffer); + continue; + } + break; + case IVL_EX_SIGNAL: case IVL_EX_SELECT: args[idx].stack = vec4_stack_need; @@ -402,6 +416,10 @@ static void draw_vpi_taskfunc_args(const char*call_string, switch (ivl_expr_value(expr)) { case IVL_VT_LOGIC: case IVL_VT_BOOL: + case IVL_VT_QUEUE: + case IVL_VT_DARRAY: + /* Queue/darray element selects may still carry the + * container ivl_expr_value; evaluate as vec4 like logic. */ draw_eval_vec4(expr); args[idx].vec_flag = ivl_expr_signed(expr)? 's' : 'u'; args[idx].str_flag = 0; @@ -433,6 +451,20 @@ static void draw_vpi_taskfunc_args(const char*call_string, buffer[0] = 0; break; default: + /* See eval_vec4.c:draw_eval_vec4 — indexed selects sometimes + * carry an unexpected ivl_expr_value (e.g. NO_TYPE). */ + if (ivl_expr_type(expr) == IVL_EX_SELECT && + ivl_expr_oper2(expr) != 0) { + draw_eval_vec4(expr); + args[idx].vec_flag = ivl_expr_signed(expr)? 's' : 'u'; + args[idx].str_flag = 0; + args[idx].real_flag = 0; + args[idx].stack = vec4_stack_need; + args[idx].vec_wid = ivl_expr_width(expr); + vec4_stack_need += 1; + buffer[0] = 0; + break; + } fprintf(stderr, "%s:%u: Sorry, cannot generate code for argument %u.\n", ivl_expr_file(expr), ivl_expr_lineno(expr), idx+1); fprintf(vvp_out, "\nXXXX Unexpected argument: call_string=<%s>, arg=%u, type=%d\n", diff --git a/tgt-vvp/eval_real.c b/tgt-vvp/eval_real.c index 1a996b814..9712b7003 100644 --- a/tgt-vvp/eval_real.c +++ b/tgt-vvp/eval_real.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2024 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2026 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 @@ -277,6 +277,15 @@ static void real_ex_pop(ivl_expr_t expr) fb = "f"; arg = ivl_expr_parm(expr, 0); + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%qpop/prop/%s/r %u;\n", fb, pidx); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); fprintf(vvp_out, " %%qpop/%s/real v%p_0;\n", fb, ivl_expr_signal(arg)); diff --git a/tgt-vvp/eval_string.c b/tgt-vvp/eval_string.c index 2624c43f4..25ab2a180 100644 --- a/tgt-vvp/eval_string.c +++ b/tgt-vvp/eval_string.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2026 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 @@ -167,6 +167,15 @@ static void string_ex_pop(ivl_expr_t expr) fb = "f"; arg = ivl_expr_parm(expr, 0); + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%qpop/prop/%s/str %u;\n", fb, pidx); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); fprintf(vvp_out, " %%qpop/%s/str v%p_0;\n", fb, ivl_expr_signal(arg)); diff --git a/tgt-vvp/eval_vec4.c b/tgt-vvp/eval_vec4.c index 6bbbb0c0a..4a71f39bf 100644 --- a/tgt-vvp/eval_vec4.c +++ b/tgt-vvp/eval_vec4.c @@ -917,6 +917,28 @@ static void draw_property_vec4(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); unsigned pidx = ivl_expr_property_idx(expr); + /* Queue/dynamic-array property with index: index is oper1 (see + * dll_target::expr_property). */ + ivl_expr_t index_ex = ivl_expr_oper1(expr); + + if (index_ex) { + ivl_type_t cls_type = ivl_signal_net_type(sig); + ivl_type_t ptype = ivl_type_prop_type(cls_type, pidx); + if (ptype) { + ivl_variable_type_t pbase = ivl_type_base(ptype); + if (pbase == IVL_VT_QUEUE || pbase == IVL_VT_DARRAY) { + unsigned wid = ivl_expr_width(expr); + draw_eval_expr_into_integer(index_ex, 3); + fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); + fprintf(vvp_out, " %%load/prop/dar/vec4 %u, %u;\n", + pidx, wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + if (ivl_expr_value(expr) == IVL_VT_BOOL) + fprintf(vvp_out, " %%cast2;\n"); + return; + } + } + } fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%prop/v %u;\n", pidx); @@ -948,7 +970,31 @@ static void draw_select_vec4(ivl_expr_t expr) return; } - if (ivl_expr_value(subexpr)==IVL_VT_DARRAY) { + /* Class property that is a queue or dynamic array: c.q[idx] */ + if (ivl_expr_type(subexpr) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(subexpr); + unsigned pidx = ivl_expr_property_idx(subexpr); + ivl_type_t cls_type = ivl_signal_net_type(clas); + ivl_type_t ptype = ivl_type_prop_type(cls_type, pidx); + if (ptype) { + ivl_variable_type_t pbase = ivl_type_base(ptype); + if (pbase == IVL_VT_QUEUE || pbase == IVL_VT_DARRAY) { + assert(base); + draw_eval_expr_into_integer(base, 3); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%load/prop/dar/vec4 %u, %u;\n", + pidx, wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + if (ivl_expr_value(expr) == IVL_VT_BOOL) { + fprintf(vvp_out, " %%cast2;\n"); + } + return; + } + } + } + + if (ivl_expr_value(subexpr) == IVL_VT_DARRAY || + ivl_expr_value(subexpr) == IVL_VT_QUEUE) { ivl_signal_t sig = ivl_expr_signal(subexpr); assert(sig); assert( (ivl_signal_data_type(sig)==IVL_VT_DARRAY) @@ -957,8 +1003,9 @@ static void draw_select_vec4(ivl_expr_t expr) assert(base); draw_eval_expr_into_integer(base, 3); fprintf(vvp_out, " %%load/dar/vec4 v%p_0;\n", sig); - if (ivl_expr_value(expr) == IVL_VT_BOOL) + if (ivl_expr_value(expr) == IVL_VT_BOOL) { fprintf(vvp_out, " %%cast2;\n"); + } return; } @@ -1011,12 +1058,23 @@ static void draw_darray_pop(ivl_expr_t expr) { const char*fb; - if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_back")==0) + if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_back")==0) { fb = "b"; - else + } else { fb = "f"; + } ivl_expr_t arg = ivl_expr_parm(expr, 0); + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%qpop/prop/%s/v %u, %u;\n", fb, pidx, + ivl_expr_width(expr)); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); fprintf(vvp_out, " %%qpop/%s/v v%p_0, %u;\n", fb, ivl_expr_signal(arg), @@ -1049,6 +1107,18 @@ static void draw_sfunc_vec4(ivl_expr_t expr) return; } + if (strcmp(ivl_expr_name(expr), "$size")==0 && parm_count==1) { + ivl_expr_t arg = ivl_expr_parm(expr, 0); + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t sig = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); + fprintf(vvp_out, " %%prop/queue/size %u;\n", pidx); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + } + draw_vpi_func_call(expr); } @@ -1357,7 +1427,10 @@ void draw_eval_vec4(ivl_expr_t expr) } assert(ivl_expr_value(expr) == IVL_VT_BOOL || - ivl_expr_value(expr) == IVL_VT_VECTOR); + ivl_expr_value(expr) == IVL_VT_VECTOR || + (ivl_expr_type(expr) == IVL_EX_SELECT && + ivl_expr_oper2(expr) != 0) || + ivl_expr_type(expr) == IVL_EX_PROPERTY); switch (ivl_expr_type(expr)) { case IVL_EX_BINARY: diff --git a/tgt-vvp/stmt_assign.c b/tgt-vvp/stmt_assign.c index d8c2aefec..9e3d3a5b2 100644 --- a/tgt-vvp/stmt_assign.c +++ b/tgt-vvp/stmt_assign.c @@ -1360,15 +1360,16 @@ static int show_stmt_assign_sig_cobject(ivl_statement_t net) fprintf(vvp_out, " %%store/prop/str %d;\n", prop_idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); - } else if (ivl_type_base(prop_type) == IVL_VT_DARRAY) { + } else if (ivl_type_base(prop_type) == IVL_VT_DARRAY || + ivl_type_base(prop_type) == IVL_VT_QUEUE) { int idx = 0; - /* The property is a darray, and there is no mux - expression to the assignment is of an entire - array object. */ + /* The property is a darray or queue, and there is no mux + expression so the assignment is of an entire + array/queue object. */ errors += draw_eval_object(rval); - fprintf(vvp_out, " %%store/prop/obj %d, %d; IVL_VT_DARRAY\n", prop_idx, idx); + fprintf(vvp_out, " %%store/prop/obj %d, %d; IVL_VT_DARRAY or QUEUE\n", prop_idx, idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else if (ivl_type_base(prop_type) == IVL_VT_CLASS) { diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index 8382b94ab..d01611671 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -1663,6 +1663,24 @@ static int show_delete_method(ivl_statement_t net) return 1; ivl_expr_t parm = ivl_stmt_parm(net, 0); + if (ivl_expr_type(parm) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(parm); + unsigned pidx = ivl_expr_property_idx(parm); + ivl_type_t sig_type = ivl_signal_net_type(clas); + ivl_type_t queue_type = ivl_type_prop_type(sig_type, pidx); + assert(ivl_type_base(queue_type) == IVL_VT_QUEUE); + + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + if (parm_count == 2) { + draw_eval_expr_into_integer(ivl_stmt_parm(net, 1), 3); + fprintf(vvp_out, " %%delete/prop/elem %u;\n", pidx); + } else { + fprintf(vvp_out, " %%delete/prop/obj %u;\n", pidx); + } + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return 0; + } + assert(ivl_expr_type(parm) == IVL_EX_SIGNAL); ivl_signal_t var = ivl_expr_signal(parm); @@ -1687,6 +1705,47 @@ static int show_insert_method(ivl_statement_t net) return 1; ivl_expr_t parm0 = ivl_stmt_parm(net,0); + if (ivl_expr_type(parm0) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(parm0); + unsigned pidx = ivl_expr_property_idx(parm0); + ivl_type_t sig_type = ivl_signal_net_type(clas); + ivl_type_t var_type = ivl_type_prop_type(sig_type, pidx); + assert(ivl_type_base(var_type) == IVL_VT_QUEUE); + + int idx = allocate_word(); + assert(idx >= 0); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%ix/load %d, %u, 0;\n", idx, + ivl_type_queue_max(var_type)); + + ivl_type_t element_type = ivl_type_element(var_type); + + ivl_expr_t parm1 = ivl_stmt_parm(net,1); + draw_eval_expr_into_integer(parm1, 3); + ivl_expr_t parm2 = ivl_stmt_parm(net,2); + switch (ivl_type_base(element_type)) { + case IVL_VT_REAL: + draw_eval_real(parm2); + fprintf(vvp_out, " %%qinsert/prop/r %u, %d;\n", + pidx, idx); + break; + case IVL_VT_STRING: + draw_eval_string(parm2); + fprintf(vvp_out, " %%qinsert/prop/str %u, %d;\n", + pidx, idx); + break; + default: + draw_eval_vec4(parm2); + fprintf(vvp_out, " %%qinsert/prop/v %u, %d, %u;\n", + pidx, idx, + ivl_type_packed_width(element_type)); + break; + } + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + clr_word(idx); + return 0; + } + assert(ivl_expr_type(parm0) == IVL_EX_SIGNAL); ivl_signal_t var = ivl_expr_signal(parm0); ivl_type_t var_type = ivl_signal_net_type(var); @@ -1740,6 +1799,45 @@ static int show_push_frontback_method(ivl_statement_t net, bool is_front) return 1; ivl_expr_t parm0 = ivl_stmt_parm(net,0); + if (ivl_expr_type(parm0) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(parm0); + unsigned pidx = ivl_expr_property_idx(parm0); + ivl_type_t sig_type = ivl_signal_net_type(clas); + ivl_type_t var_type = ivl_type_prop_type(sig_type, pidx); + assert(ivl_type_base(var_type) == IVL_VT_QUEUE); + + int idx = allocate_word(); + assert(idx >= 0); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%ix/load %d, %u, 0;\n", idx, + ivl_type_queue_max(var_type)); + + ivl_type_t element_type = ivl_type_element(var_type); + + ivl_expr_t parm1 = ivl_stmt_parm(net,1); + switch (ivl_type_base(element_type)) { + case IVL_VT_REAL: + draw_eval_real(parm1); + fprintf(vvp_out, " %%store/prop/%s/r %u, %d;\n", + type_code, pidx, idx); + break; + case IVL_VT_STRING: + draw_eval_string(parm1); + fprintf(vvp_out, " %%store/prop/%s/str %u, %d;\n", + type_code, pidx, idx); + break; + default: + draw_eval_vec4(parm1); + fprintf(vvp_out, " %%store/prop/%s/v %u, %d, %u;\n", + type_code, pidx, idx, + ivl_type_packed_width(element_type)); + break; + } + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + clr_word(idx); + return 0; + } + assert(ivl_expr_type(parm0) == IVL_EX_SIGNAL); ivl_signal_t var = ivl_expr_signal(parm0); ivl_type_t var_type = ivl_signal_net_type(var); diff --git a/vpi/libvpi.c b/vpi/libvpi.c index d82422586..d88254257 100644 --- a/vpi/libvpi.c +++ b/vpi/libvpi.c @@ -286,6 +286,11 @@ void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit) assert(vpip_routines); vpip_routines->format_strength(str, value, bit); } +char* vpip_format_pretty(vpiHandle ref) +{ + assert(vpip_routines); + return vpip_routines->format_pretty(ref); +} void vpip_make_systf_system_defined(vpiHandle ref) { assert(vpip_routines); diff --git a/vpi/sys_display.c b/vpi/sys_display.c index d5b3f2923..6522d20ea 100644 --- a/vpi/sys_display.c +++ b/vpi/sys_display.c @@ -867,6 +867,35 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus, * be a binary string (can contain NULLs). */ break; + case 'p': + case 'P': + *idx += 1; + if (plus != 0 || prec != -1) { + vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", + info->filename, info->lineno, info->name, fmtb); + } + if (*idx >= info->nitems) { + vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", + info->filename, info->lineno, info->name, fmtb); + } else { + char *pp = vpip_format_pretty(info->items[*idx]); + if (pp == 0) { + vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", + info->filename, info->lineno, info->name, fmtb); + } else { + char *cp = pp; + if (width == -1) width = 0; + size = strlen(cp) + 1; + if ((signed)size < (width+1)) size = width+1; + if (size > ini_size) result = realloc(result, size*sizeof(char)); + if (ljust == 0) sprintf(result, "%*s", width, cp); + else sprintf(result, "%-*s", width, cp); + free(pp); + size = strlen(result) + 1; + } + } + break; + default: vpi_printf("WARNING: %s:%d: unknown format %s%s.\n", info->filename, info->lineno, info->name, fmtb); @@ -1215,6 +1244,7 @@ static int sys_check_args(vpiHandle callh, vpiHandle argv, const PLI_BYTE8*name, #endif case vpiClassVar: case vpiSysFuncCall: + case vpiRegArray: /* dynamic arrays, queues, SV array vars */ break; default: diff --git a/vpi_user.h b/vpi_user.h index c347fe15e..9fcfd6342 100644 --- a/vpi_user.h +++ b/vpi_user.h @@ -641,6 +641,10 @@ extern DLLEXPORT void (*vlog_startup_routines[])(void); /* Format a scalar a la %v. The str points to a 4byte character buffer. The value must be a vpiStrengthVal. */ extern void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit); + /* Pretty-print a dynamic array or queue (including class property queues) + * for %p. Returns a malloc'd string, or NULL if the handle is not + * supported. Caller must free the returned pointer when non-NULL. */ +extern char* vpip_format_pretty(vpiHandle ref); /* Set the return value to return from the vvp run time. This is usually 0 or 1. This is the exit code that the vvp process returns, and in distinct from the finish_number that is an @@ -696,7 +700,7 @@ extern void vpip_count_drivers(vpiHandle ref, unsigned idx, */ // Increment the version number any time vpip_routines_s is changed. -static const PLI_UINT32 vpip_routines_version = 1; +static const PLI_UINT32 vpip_routines_version = 2; typedef struct { vpiHandle (*register_cb)(p_cb_data); @@ -737,6 +741,7 @@ typedef struct { s_vpi_vecval(*calc_clog2)(vpiHandle); void (*count_drivers)(vpiHandle, unsigned, unsigned [4]); void (*format_strength)(char*, s_vpi_value*, unsigned); + char* (*format_pretty)(vpiHandle); void (*make_systf_system_defined)(vpiHandle); void (*mcd_rawwrite)(PLI_UINT32, const char*, size_t); void (*set_return_value)(int); diff --git a/vvp/codes.h b/vvp/codes.h index 00594e1f0..b15f51888 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -150,6 +150,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_DAR_VEC4(vthread_t thr, vvp_code_t code); +extern bool of_LOAD_PROP_DAR_VEC4(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); @@ -233,6 +234,24 @@ 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); extern bool of_STORE_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QB_R(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QB_STR(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QB_V(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QF_R(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QF_STR(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QF_V(vthread_t thr, vvp_code_t code); +extern bool of_QINSERT_PROP_R(vthread_t thr, vvp_code_t code); +extern bool of_QINSERT_PROP_STR(vthread_t thr, vvp_code_t code); +extern bool of_QINSERT_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_B_REAL(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_B_STR(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_B_V(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_F_REAL(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_F_STR(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_F_V(vthread_t thr, vvp_code_t code); +extern bool of_PROP_QUEUE_SIZE(vthread_t thr, vvp_code_t code); +extern bool of_DELETE_PROP_ELEM(vthread_t thr, vvp_code_t code); +extern bool of_DELETE_PROP_OBJ(vthread_t thr, vvp_code_t code); extern bool of_STORE_QB_R(vthread_t thr, vvp_code_t code); extern bool of_STORE_QB_STR(vthread_t thr, vvp_code_t code); extern bool of_STORE_QB_V(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index fa5391717..4160c0868 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -48,9 +48,13 @@ unsigned compile_errors = 0; /* * The opcode table lists all the code mnemonics, along with their - * opcode and operand types. The table is written sorted by mnemonic - * so that it can be searched by binary search. The opcode_compare - * function is a helper function for that lookup. + * opcode and operand types. The table must be sorted lexicographically + * by mnemonic string: compile_code() uses bsearch() on this array. + * If the order is wrong, lookup fails and the assembler reports + * "Invalid opcode" for otherwise valid instructions (e.g. class + * property queue ops must sort among all %delete/*, %qpop/*, %store/* + * names, not grouped by feature). + * The opcode_compare function is a helper for that lookup. */ enum operand_e { @@ -151,6 +155,8 @@ static const struct opcode_table_s opcode_table[] = { { "%delayx", of_DELAYX, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%delete/elem",of_DELETE_ELEM,1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, { "%delete/obj",of_DELETE_OBJ,1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, + { "%delete/prop/elem", of_DELETE_PROP_ELEM, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%delete/prop/obj", of_DELETE_PROP_OBJ, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%delete/tail",of_DELETE_TAIL,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%disable", of_DISABLE, 1, {OA_VPI_PTR,OA_NONE, OA_NONE} }, { "%disable/flow", of_DISABLE_FLOW, 1, {OA_VPI_PTR,OA_NONE, OA_NONE} }, @@ -204,6 +210,7 @@ static const struct opcode_table_s opcode_table[] = { { "%load/dar/vec4",of_LOAD_DAR_VEC4,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/prop/dar/vec4", of_LOAD_PROP_DAR_VEC4, 2, {OA_NUMBER, 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} }, @@ -242,6 +249,7 @@ static const struct opcode_table_s opcode_table[] = { { "%pow/s", of_POW_S, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%pow/wr", of_POW_WR, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%prop/obj",of_PROP_OBJ,2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%prop/queue/size", of_PROP_QUEUE_SIZE, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%prop/r", of_PROP_R, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%prop/str",of_PROP_STR,1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%prop/v", of_PROP_V, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, @@ -250,6 +258,9 @@ static const struct opcode_table_s opcode_table[] = { { "%pushi/vec4",of_PUSHI_VEC4,3,{OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%pushv/str", of_PUSHV_STR, 0,{OA_NONE, OA_NONE, OA_NONE} }, { "%putc/str/vec4",of_PUTC_STR_VEC4,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, + { "%qinsert/prop/r", of_QINSERT_PROP_R, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%qinsert/prop/str", of_QINSERT_PROP_STR, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%qinsert/prop/v", of_QINSERT_PROP_V, 3, {OA_NUMBER, OA_BIT1, OA_BIT2} }, { "%qinsert/real",of_QINSERT_REAL,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%qinsert/str", of_QINSERT_STR, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%qinsert/v", of_QINSERT_V, 3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, @@ -259,6 +270,12 @@ static const struct opcode_table_s opcode_table[] = { { "%qpop/f/real",of_QPOP_F_REAL,1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, { "%qpop/f/str", of_QPOP_F_STR, 1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, { "%qpop/f/v", of_QPOP_F_V, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, + { "%qpop/prop/b/r", of_QPOP_PROP_B_REAL, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%qpop/prop/b/str", of_QPOP_PROP_B_STR, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%qpop/prop/b/v", of_QPOP_PROP_B_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%qpop/prop/f/r", of_QPOP_PROP_F_REAL, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%qpop/prop/f/str", of_QPOP_PROP_F_STR, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%qpop/prop/f/v", of_QPOP_PROP_F_V, 2, {OA_NUMBER, OA_BIT1, 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} }, { "%release/wr", of_RELEASE_WR, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, @@ -283,6 +300,12 @@ static const struct opcode_table_s opcode_table[] = { { "%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,2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qb/r", of_STORE_PROP_QB_R, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qb/str", of_STORE_PROP_QB_STR, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qb/v", of_STORE_PROP_QB_V, 3, {OA_NUMBER, OA_BIT1, OA_BIT2} }, + { "%store/prop/qf/r", of_STORE_PROP_QF_R, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qf/str", of_STORE_PROP_QF_STR, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qf/v", of_STORE_PROP_QF_V, 3, {OA_NUMBER, OA_BIT1, OA_BIT2} }, { "%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} }, { "%store/prop/v", of_STORE_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, diff --git a/vvp/compile.h b/vvp/compile.h index f2e949d66..a813db3b2 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -344,6 +344,10 @@ class resolv_list_s { */ extern void functor_ref_lookup(vvp_net_t**ref, char*lab); +extern vpiHandle vpip_make_prop_queue_ref(char* class_label, + unsigned prop_idx, + unsigned is_queue_flag); + /* * This function schedules a lookup of the labeled instruction. The * code points to a code structure that points to the instruction diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 1a6ce88ef..122bcaf45 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -272,6 +272,7 @@ inline uint64_t strtouint64(const char*str, char**endptr, int base) "&A" { return K_A; } "&APV" { return K_APV; } +"&PQ" { return K_PQ; } "&PV" { return K_PV; } "%"[.$_/a-zA-Z0-9]+ { diff --git a/vvp/parse.y b/vvp/parse.y index 2fc3dc93c..b8af4e50f 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -93,7 +93,7 @@ static struct __vpiModPath*modpath_dst = 0; %token K_NET K_NET_S K_NET_R K_NET_2S K_NET_2U %token K_NET8 K_NET8_2S K_NET8_2U K_NET8_S %token K_PARAM_STR K_PARAM_L K_PARAM_REAL K_PART K_PART_PV -%token K_PART_V K_PART_V_S K_PORT K_PORT_INFO K_PV K_REDUCE_AND K_REDUCE_OR K_REDUCE_XOR +%token K_PART_V K_PART_V_S K_PORT K_PORT_INFO K_PQ K_PV K_REDUCE_AND K_REDUCE_OR K_REDUCE_XOR %token K_REDUCE_NAND K_REDUCE_NOR K_REDUCE_XNOR K_REPEAT %token K_RESOLV K_RTRAN K_RTRANIF0 K_RTRANIF1 %token K_SCOPE K_SFUNC K_SFUNC_E K_SHIFTL K_SHIFTR K_SHIFTRS @@ -1122,6 +1122,8 @@ symbol_access { $$ = vpip_make_PV($3, $5, $7); } | K_APV '<' T_SYMBOL ',' T_NUMBER ',' T_NUMBER ',' T_NUMBER '>' { $$ = vpip_make_vthr_APV($3, $5, $7, $9); } + | K_PQ '<' T_SYMBOL ',' T_NUMBER ',' T_NUMBER '>' + { $$ = vpip_make_prop_queue_ref($3, $5, $7); } ; /* functor operands can only be a list of symbols. */ diff --git a/vvp/vpi_darray.cc b/vvp/vpi_darray.cc index db9b902c6..2ad9cb7d7 100644 --- a/vvp/vpi_darray.cc +++ b/vvp/vpi_darray.cc @@ -22,6 +22,7 @@ # include "vpi_priv.h" # include "vvp_net_sig.h" # include "vvp_darray.h" +# include "vvp_cobject.h" # include "array_common.h" # include "schedule.h" #ifdef CHECK_WITH_VALGRIND @@ -294,38 +295,23 @@ vpiHandle vpip_make_darray_var(const char*name, vvp_net_t*net) } __vpiQueueVar::__vpiQueueVar(__vpiScope*sc, const char*na, vvp_net_t*ne) -: __vpiBaseVar(sc, na, ne) +: __vpiDarrayVar(sc, na, ne) { } -int __vpiQueueVar::get_type_code(void) const -{ return vpiArrayVar; } - - int __vpiQueueVar::vpi_get(int code) { - vvp_fun_signal_object*fun = dynamic_cast (get_net()->fun); - assert(fun); - vvp_object_t val = fun->get_object(); - const vvp_queue*aval = val.peek(); - switch (code) { case vpiArrayType: return vpiQueueArray; - case vpiSize: - if (aval == 0) - return 0; - else - return aval->get_size(); - default: - return 0; + return __vpiDarrayVar::vpi_get(code); } } void __vpiQueueVar::vpi_get_value(p_vpi_value val) { - val->format = vpiSuppressVal; + __vpiDarrayVar::vpi_get_value(val); } @@ -339,9 +325,94 @@ vpiHandle vpip_make_queue_var(const char*name, vvp_net_t*net) return obj; } +__vpiPropQueueRef::__vpiPropQueueRef(__vpiScope*scope, unsigned pidx, + bool is_queue) +: class_net_(0), prop_idx_(pidx), is_queue_(is_queue), scope_(scope) +{ +} + +int __vpiPropQueueRef::get_type_code(void) const +{ return vpiArrayVar; } + +static vvp_darray* get_live_darray_from_prop(vvp_net_t* class_net, unsigned pid) +{ + if (!class_net) return 0; + vvp_fun_signal_object* fun = dynamic_cast(class_net->fun); + if (!fun) return 0; + vvp_object_t obj = fun->get_object(); + vvp_cobject* cobj = obj.peek(); + if (!cobj) return 0; + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + return qobj.peek(); +} + +int __vpiPropQueueRef::vpi_get(int code) +{ + vvp_darray* aobj = get_live_darray_from_prop(class_net_, prop_idx_); + switch (code) { + case vpiArrayType: + return is_queue_ ? vpiQueueArray : vpiDynamicArray; + case vpiLeftRange: + return 0; + case vpiRightRange: + return aobj ? (int) (aobj->get_size() - 1) : 0; + case vpiSize: + return aobj ? (int) aobj->get_size() : 0; + default: + return 0; + } +} + +char* __vpiPropQueueRef::vpi_get_str(int code) +{ + if (code == vpiFile) { + return simple_set_rbuf_str(file_names[0]); + } + return generic_get_str(code, scope_, "prop_darray", NULL); +} + +void __vpiPropQueueRef::vpi_get_value(p_vpi_value val) +{ + val->format = vpiSuppressVal; +} + +vpiHandle vpip_make_prop_queue_ref(char* class_label, unsigned prop_idx, + unsigned is_queue_flag) +{ + __vpiPropQueueRef* obj = new __vpiPropQueueRef(vpip_peek_current_scope(), + prop_idx, + is_queue_flag != 0); + functor_ref_lookup(&obj->class_net_, class_label); + return obj; +} + +vvp_darray* vpip_vpi_darray_from_handle(vpiHandle ref) +{ + if (!ref) return 0; + + if (__vpiPropQueueRef* pr = dynamic_cast<__vpiPropQueueRef*>(ref)) { + return get_live_darray_from_prop(pr->class_net_, pr->prop_idx_); + } + + if (__vpiDarrayVar* dv = dynamic_cast<__vpiDarrayVar*>(ref)) { + vvp_fun_signal_object* fun = dynamic_cast(dv->get_net()->fun); + if (!fun) return 0; + vvp_object_t obj = fun->get_object(); + return obj.peek(); + } + + return 0; +} + #ifdef CHECK_WITH_VALGRIND void array_delete(vpiHandle item) { + if (__vpiPropQueueRef* pq = dynamic_cast<__vpiPropQueueRef*>(item)) { + delete pq; + return; + } + __vpiDarrayVar*dobj = dynamic_cast<__vpiDarrayVar*>(item); if (dobj) { if (dobj->vals_words) delete [] (dobj->vals_words-1); @@ -349,12 +420,6 @@ void array_delete(vpiHandle item) return; } - __vpiQueueVar*qobj = dynamic_cast<__vpiQueueVar*>(item); - if (qobj) { - delete qobj; - return; - } - fprintf(stderr, "Need support for deleting array type: %d\n", item->vpi_get(vpiArrayType)); assert(0); } diff --git a/vvp/vpi_priv.cc b/vvp/vpi_priv.cc index c3aabdfa9..1d84146b2 100644 --- a/vvp/vpi_priv.cc +++ b/vvp/vpi_priv.cc @@ -2098,6 +2098,7 @@ vpip_routines_s vpi_routines = { .calc_clog2 = vpip_calc_clog2, .count_drivers = vpip_count_drivers, .format_strength = vpip_format_strength, + .format_pretty = vpip_format_pretty, .make_systf_system_defined = vpip_make_systf_system_defined, .mcd_rawwrite = vpip_mcd_rawwrite, .set_return_value = vpip_set_return_value, diff --git a/vvp/vpi_priv.h b/vvp/vpi_priv.h index 3b253c999..83fce94ce 100644 --- a/vvp/vpi_priv.h +++ b/vvp/vpi_priv.h @@ -870,18 +870,40 @@ class __vpiDarrayVar : public __vpiBaseVar, public __vpiArrayBase { extern vpiHandle vpip_make_darray_var(const char*name, vvp_net_t*net); -class __vpiQueueVar : public __vpiBaseVar { +class __vpiQueueVar : public __vpiDarrayVar { public: __vpiQueueVar(__vpiScope*scope, const char*name, vvp_net_t*net); - int get_type_code(void) const override; int vpi_get(int code) override; void vpi_get_value(p_vpi_value val) override; }; extern vpiHandle vpip_make_queue_var(const char*name, vvp_net_t*net); +class __vpiPropQueueRef : public __vpiHandle { + + public: + explicit __vpiPropQueueRef(__vpiScope*scope, unsigned pidx, bool is_queue); + + int get_type_code(void) const override; + int vpi_get(int code) override; + char* vpi_get_str(int code) override; + void vpi_get_value(p_vpi_value val) override; + + vvp_net_t* class_net_; + unsigned prop_idx_; + bool is_queue_; + + private: + __vpiScope* scope_; +}; + +extern vpiHandle vpip_make_prop_queue_ref(char* class_label, + unsigned prop_idx, + unsigned is_queue_flag); +extern vvp_darray* vpip_vpi_darray_from_handle(vpiHandle ref); + class __vpiCobjectVar : public __vpiBaseVar { public: diff --git a/vvp/vpip_format.cc b/vvp/vpip_format.cc index 4d0b085f8..fdc63c9dd 100644 --- a/vvp/vpip_format.cc +++ b/vvp/vpip_format.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2026 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 @@ -18,7 +18,15 @@ */ # include "vpi_user.h" +# include "vpi_priv.h" +# include "vvp_darray.h" +# include "vvp_net_sig.h" # include +# include +# include +# include +# include +# include "ivl_alloc.h" static const char str_char1_table[257] = { ".HS1M222" "W3333333" /* 00 0F */ "L4444444" "44444444" /* 10 1F */ @@ -104,3 +112,60 @@ extern "C" void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit) assert(0); } } + +static char* vpip_format_pretty_string(const char*msg) +{ + return strdup(msg); +} + +static char* format_darray_pretty(vvp_darray* aobj) +{ + if (!aobj || aobj->get_size() == 0) + return strdup("{}"); + + std::string out = "{"; + + for (size_t i = 0; i < aobj->get_size(); i += 1) { + if (i > 0) + out += ", "; + if (dynamic_cast(aobj)) { + double d; + aobj->get_word((unsigned) i, d); + char buf[256]; + snprintf(buf, sizeof buf, "%g", d); + out += buf; + } else if (dynamic_cast(aobj)) { + std::string s; + aobj->get_word((unsigned) i, s); + out += "\""; + out += s; + out += "\""; + } else { + vvp_vector4_t v; + aobj->get_word((unsigned) i, v); + s_vpi_value val; + val.format = vpiDecStrVal; + vpip_vec4_get_value(v, v.size(), false, &val); + out += val.value.str; + } + } + + out += "}"; + return strdup(out.c_str()); +} + +extern "C" char* vpip_format_pretty(vpiHandle ref) +{ + if (!ref) + return vpip_format_pretty_string("Handle is NULL"); + + if (dynamic_cast<__vpiPropQueueRef*>(ref) || + dynamic_cast<__vpiDarrayVar*>(ref)) { + vvp_darray* aobj = vpip_vpi_darray_from_handle(ref); + if (!aobj) + return vpip_format_pretty_string("Object not found"); + return format_darray_pretty(aobj); + } + + return vpip_format_pretty_string("Unsupported object type"); +} diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 2b171111f..81989cb29 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -3873,6 +3873,36 @@ bool of_LOAD_DAR_VEC4(vthread_t thr, vvp_code_t cp) return load_dar(thr, cp); } +/* + * %load/prop/dar/vec4 , ; + * Indexed read of queue/dynamic-array class property; object on stack, + * index in words[3] (same as %load/dar/vec4). + */ +bool of_LOAD_PROP_DAR_VEC4(vthread_t thr, vvp_code_t cp) +{ + size_t pid = cp->number; + unsigned wid = cp->bit_idx[0]; + int64_t adr = thr->words[3].w_int; + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t pobj; + cobj->get_object(pid, pobj, 0); + vvp_darray*darray = pobj.peek(); + + vvp_vector4_t word; + if (darray && (adr >= 0) && (thr->flags[4] == BIT4_0)) { + darray->get_word(adr, word); + } else { + dq_default(word, wid); + } + + thr->push_vec4(word); + return true; +} + /* * %load/obj */ @@ -5897,6 +5927,292 @@ bool of_STORE_PROP_V(vthread_t thr, vvp_code_t cp) return store_prop(thr, cp, cp->bit_idx[0]); } +template +static QTYPE* get_queue_prop(vvp_cobject*cobj, size_t pid) +{ + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + QTYPE* queue = qobj.peek(); + if (queue == 0) { + queue = new QTYPE; + vvp_object_t val(queue); + cobj->set_object(pid, val, 0); + } + return queue; +} + +template +static bool store_prop_qb(vthread_t thr, vvp_code_t cp, unsigned wid) +{ + size_t pid = cp->number; + ELEM value; + unsigned max_size = thr->words[cp->bit_idx[0]].w_uint; + pop_value(thr, value, wid); + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + QTYPE* queue = get_queue_prop(cobj, pid); + assert(queue); + queue->push_back(value, max_size); + return true; +} + +template +static bool store_prop_qf(vthread_t thr, vvp_code_t cp, unsigned wid) +{ + size_t pid = cp->number; + ELEM value; + unsigned max_size = thr->words[cp->bit_idx[0]].w_uint; + pop_value(thr, value, wid); + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + QTYPE* queue = get_queue_prop(cobj, pid); + assert(queue); + queue->push_front(value, max_size); + return true; +} + +bool of_STORE_PROP_QB_R(vthread_t thr, vvp_code_t cp) +{ + return store_prop_qb(thr, cp, 0); +} + +bool of_STORE_PROP_QB_STR(vthread_t thr, vvp_code_t cp) +{ + return store_prop_qb(thr, cp, 0); +} + +bool of_STORE_PROP_QB_V(vthread_t thr, vvp_code_t cp) +{ + return store_prop_qb(thr, cp, cp->bit_idx[1]); +} + +bool of_STORE_PROP_QF_R(vthread_t thr, vvp_code_t cp) +{ + return store_prop_qf(thr, cp, 0); +} + +bool of_STORE_PROP_QF_STR(vthread_t thr, vvp_code_t cp) +{ + return store_prop_qf(thr, cp, 0); +} + +bool of_STORE_PROP_QF_V(vthread_t thr, vvp_code_t cp) +{ + return store_prop_qf(thr, cp, cp->bit_idx[1]); +} + +template +static bool qinsert_prop(vthread_t thr, vvp_code_t cp, unsigned wid) +{ + int64_t idx = thr->words[3].w_int; + ELEM value; + size_t pid = cp->number; + pop_value(thr, value, wid); + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + QTYPE* queue = get_queue_prop(cobj, pid); + assert(queue); + if (idx < 0) { + cerr << thr->get_fileline() + << "Warning: cannot insert at a negative " + << get_queue_type(value) + << " index (" << idx << "). "; + print_queue_value(value); + cerr << " was not added." << endl; + } else if (thr->flags[4] != BIT4_0) { + cerr << thr->get_fileline() + << "Warning: cannot insert at an undefined " + << get_queue_type(value) << " index. "; + print_queue_value(value); + cerr << " was not added." << endl; + } else { + unsigned max_size = thr->words[cp->bit_idx[0]].w_int; + queue->insert(idx, value, max_size); + } + return true; +} + +bool of_QINSERT_PROP_R(vthread_t thr, vvp_code_t cp) +{ + return qinsert_prop(thr, cp, 0); +} + +bool of_QINSERT_PROP_STR(vthread_t thr, vvp_code_t cp) +{ + return qinsert_prop(thr, cp, 0); +} + +bool of_QINSERT_PROP_V(vthread_t thr, vvp_code_t cp) +{ + return qinsert_prop(thr, cp, cp->bit_idx[1]); +} + +template +static bool q_pop_prop(vthread_t thr, vvp_code_t cp, + void (*get_val_func)(vvp_queue*, ELEM&), + const char*loc, unsigned wid) +{ + size_t pid = cp->number; + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + QTYPE* queue = get_queue_prop(cobj, pid); + assert(queue); + + size_t size = queue->get_size(); + + ELEM value; + if (size) { + get_val_func(queue, value); + } else { + dq_default(value, wid); + cerr << thr->get_fileline() + << "Warning: pop_" << loc << "() on empty " + << get_queue_type(value) << "." << endl; + } + + push_value(thr, value, wid); + return true; +} + +template +static bool qpop_b_prop(vthread_t thr, vvp_code_t cp, unsigned wid) +{ + return q_pop_prop(thr, cp, get_back_value, "back", wid); +} + +template +static bool qpop_f_prop(vthread_t thr, vvp_code_t cp, unsigned wid) +{ + return q_pop_prop(thr, cp, get_front_value, "front", wid); +} + +bool of_QPOP_PROP_B_REAL(vthread_t thr, vvp_code_t cp) +{ + return qpop_b_prop(thr, cp, 0); +} + +bool of_QPOP_PROP_B_STR(vthread_t thr, vvp_code_t cp) +{ + return qpop_b_prop(thr, cp, 0); +} + +bool of_QPOP_PROP_B_V(vthread_t thr, vvp_code_t cp) +{ + return qpop_b_prop(thr, cp, cp->bit_idx[0]); +} + +bool of_QPOP_PROP_F_REAL(vthread_t thr, vvp_code_t cp) +{ + return qpop_f_prop(thr, cp, 0); +} + +bool of_QPOP_PROP_F_STR(vthread_t thr, vvp_code_t cp) +{ + return qpop_f_prop(thr, cp, 0); +} + +bool of_QPOP_PROP_F_V(vthread_t thr, vvp_code_t cp) +{ + return qpop_f_prop(thr, cp, cp->bit_idx[0]); +} + +bool of_PROP_QUEUE_SIZE(vthread_t thr, vvp_code_t cp) +{ + size_t pid = cp->number; + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + vvp_queue* queue = qobj.peek(); + + size_t sz = queue ? queue->get_size() : 0; + + vvp_vector4_t val(32, BIT4_0); + unsigned long ul = sz; + for (unsigned idx = 0; idx < 32; idx++) { + if (ul & 1UL) { + val.set_bit(idx, BIT4_1); + } else { + val.set_bit(idx, BIT4_0); + } + ul >>= 1; + } + thr->push_vec4(val); + return true; +} + +bool of_DELETE_PROP_ELEM(vthread_t thr, vvp_code_t cp) +{ + size_t pid = cp->number; + + int64_t idx_val = thr->words[3].w_int; + if (thr->flags[4] == BIT4_1) { + cerr << thr->get_fileline() + << "Warning: skipping queue delete() with undefined index." + << endl; + return true; + } + if (idx_val < 0) { + cerr << thr->get_fileline() + << "Warning: skipping queue delete() with negative index." + << endl; + return true; + } + size_t idx = idx_val; + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + vvp_queue* queue = qobj.peek(); + if (queue == 0) { + cerr << thr->get_fileline() + << "Warning: skipping delete(" << idx + << ") on empty queue." << endl; + } else { + size_t size = queue->get_size(); + if (idx >= size) { + cerr << thr->get_fileline() + << "Warning: skipping out of range delete(" << idx + << ") on queue of size " << size << "." << endl; + } else { + queue->erase(idx); + } + } + + return true; +} + +bool of_DELETE_PROP_OBJ(vthread_t thr, vvp_code_t cp) +{ + size_t pid = cp->number; + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + cobj->set_object(pid, vvp_object_t(), 0); + + return true; +} + template static bool store_qb(vthread_t thr, vvp_code_t cp, unsigned wid=0) { diff --git a/vvp/vvp.def b/vvp/vvp.def index 1d1737c77..702103d60 100644 --- a/vvp/vvp.def +++ b/vvp/vvp.def @@ -43,6 +43,7 @@ vpi_vprintf vpip_calc_clog2 vpip_count_drivers vpip_format_strength +vpip_format_pretty vpip_make_systf_system_defined vpip_mcd_rawwrite vpip_set_return_value From 82350c4866ecfe20d495d32af511e97f7c2420fd Mon Sep 17 00:00:00 2001 From: "Cary R." Date: Sun, 12 Jul 2026 07:44:20 -0700 Subject: [PATCH 2/5] Fix conditional block for boolean type handling --- tgt-vvp/eval_vec4.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tgt-vvp/eval_vec4.c b/tgt-vvp/eval_vec4.c index 4a71f39bf..f92eb27db 100644 --- a/tgt-vvp/eval_vec4.c +++ b/tgt-vvp/eval_vec4.c @@ -933,8 +933,9 @@ static void draw_property_vec4(ivl_expr_t expr) fprintf(vvp_out, " %%load/prop/dar/vec4 %u, %u;\n", pidx, wid); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); - if (ivl_expr_value(expr) == IVL_VT_BOOL) + if (ivl_expr_value(expr) == IVL_VT_BOOL) { fprintf(vvp_out, " %%cast2;\n"); + } return; } } From 9a7178fef0f7ac358cd9e3aacd07e403f14fa0b0 Mon Sep 17 00:00:00 2001 From: "Cary R." Date: Sun, 12 Jul 2026 07:52:48 -0700 Subject: [PATCH 3/5] Refactor format_darray_pretty for readability --- vvp/vpip_format.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vvp/vpip_format.cc b/vvp/vpip_format.cc index fdc63c9dd..c7c09e296 100644 --- a/vvp/vpip_format.cc +++ b/vvp/vpip_format.cc @@ -120,14 +120,12 @@ static char* vpip_format_pretty_string(const char*msg) static char* format_darray_pretty(vvp_darray* aobj) { - if (!aobj || aobj->get_size() == 0) - return strdup("{}"); + if (!aobj || aobj->get_size() == 0) return strdup("{}"); std::string out = "{"; for (size_t i = 0; i < aobj->get_size(); i += 1) { - if (i > 0) - out += ", "; + if (i > 0) out += ", "; if (dynamic_cast(aobj)) { double d; aobj->get_word((unsigned) i, d); @@ -156,14 +154,16 @@ static char* format_darray_pretty(vvp_darray* aobj) extern "C" char* vpip_format_pretty(vpiHandle ref) { - if (!ref) + if (!ref) { return vpip_format_pretty_string("Handle is NULL"); + } if (dynamic_cast<__vpiPropQueueRef*>(ref) || dynamic_cast<__vpiDarrayVar*>(ref)) { vvp_darray* aobj = vpip_vpi_darray_from_handle(ref); - if (!aobj) + if (!aobj) { return vpip_format_pretty_string("Object not found"); + } return format_darray_pretty(aobj); } From e7233856c85fd342b95e3ff36e4f4d764816aea4 Mon Sep 17 00:00:00 2001 From: "Cary R." Date: Sun, 12 Jul 2026 07:57:09 -0700 Subject: [PATCH 4/5] Simplify conditional return statements --- elab_expr.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/elab_expr.cc b/elab_expr.cc index 0a03bb3f9..553d7bf21 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -3329,8 +3329,7 @@ NetExpr* PECallFunction::elaborate_expr_method_chained_(Design*des, NetScope*sco ivl_assert(*this, use_index.lsb == 0); NetExpr*mux = elab_and_eval(des, scope, use_index.msb, -1, false); - if (!mux) - return 0; + if (!mux) return 0; NetESelect*tmp = new NetESelect(sub_expr, mux, darray->element_width(), darray->element_type()); tmp->set_line(*this); @@ -3356,8 +3355,9 @@ NetExpr* PECallFunction::elaborate_expr_method_chained_(Design*des, NetScope*sco NetNet* cur_net = search_results.net; const netclass_t* cur_class = dynamic_cast(search_results.type); - if (cur_class == 0) + if (cur_class == 0) { cur_class = dynamic_cast(cur_net->net_type()); + } if (cur_class == 0) { cerr << get_fileline() << ": internal error: " << "IVL_VT_CLASS net without netclass_t type." << endl; @@ -3373,9 +3373,7 @@ NetExpr* PECallFunction::elaborate_expr_method_chained_(Design*des, NetScope*sco NetExpr* step = elaborate_class_method_net_(des, scope, cur_net, cur_class, method_name, &no_parms); - if (step == 0) { - return 0; - } + if (step == 0) return 0; NetEUFunc*uf = dynamic_cast (step); if (uf == 0) { From 2ff0be6bfd17e5297d600f77e7f91005a9cfd18a Mon Sep 17 00:00:00 2001 From: "Cary R." Date: Sun, 12 Jul 2026 08:00:14 -0700 Subject: [PATCH 5/5] Fix conditional check for net class type --- elaborate.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/elaborate.cc b/elaborate.cc index 4d48fa546..e2e409f7f 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -4337,8 +4337,9 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, // e.g. c.q.push_back(x) => sr.path_tail = {q}, method_name = push_back if (sr.path_tail.size() == 1) { const netclass_t*cls = dynamic_cast(sr.type); - if (!cls && net->net_type()) + if (!cls && net->net_type()) { cls = dynamic_cast(net->net_type()); + } if (cls) { perm_string prop_name = sr.path_tail.front().name; int pidx = cls->property_idx_from_name(prop_name);