From f9e3f9a3e4b38f5ee1885d5e6d01bfe5f384a127 Mon Sep 17 00:00:00 2001 From: mjoekhan Date: Tue, 28 Apr 2026 22:29:40 +0500 Subject: [PATCH] SystemVerilog: integral sum() reduction for queues and dynamic arrays Add $ivl_queue_method$sum elaboration, eval_vec4 lowering (%queue/sum/v and property variant), and VVP runtime that sums words with Verilog-style vec4 addition. Empty arrays yield 0. Regressions: sv_queue_sum, sv_darray_sum; document in README_sv_queue_locators. Made-with: Cursor --- elab_expr.cc | 169 ++++++++++++++++++- ivtest/ivltests/README_sv_queue_locators.txt | 4 +- ivtest/ivltests/sv_darray_sum.v | 28 +++ ivtest/ivltests/sv_queue_sum.v | 28 +++ ivtest/regress-vvp.list | 2 + ivtest/vvp_tests/sv_darray_sum.json | 9 + ivtest/vvp_tests/sv_queue_sum.json | 9 + tgt-vvp/eval_vec4.c | 18 ++ vvp/codes.h | 2 + vvp/compile.cc | 2 + vvp/vthread.cc | 57 +++++++ 11 files changed, 322 insertions(+), 6 deletions(-) create mode 100644 ivtest/ivltests/sv_darray_sum.v create mode 100644 ivtest/ivltests/sv_queue_sum.v create mode 100644 ivtest/vvp_tests/sv_darray_sum.json create mode 100644 ivtest/vvp_tests/sv_queue_sum.json diff --git a/elab_expr.cc b/elab_expr.cc index fecbf7f32..4af726d41 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1744,6 +1744,14 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, return expr_width_; } + if (method_name == "sum") { + expr_type_ = darray->element_base_type(); + expr_width_ = darray->element_width(); + min_width_ = expr_width_; + signed_flag_ = darray->get_signed(); + return expr_width_; + } + if (method_name == "find" || method_name == "find_index" || method_name == "unique" || method_name == "unique_index" || method_name == "min" || method_name == "max") { @@ -1801,6 +1809,14 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, return expr_width_; } + if (method_name == "sum") { + expr_type_ = darray->element_base_type(); + expr_width_ = darray->element_width(); + min_width_ = expr_width_; + signed_flag_ = darray->get_signed(); + return expr_width_; + } + if (method_name == "unique" || method_name == "unique_index" || method_name == "min" || method_name == "max") { expr_type_ = IVL_VT_QUEUE; @@ -3826,6 +3842,31 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, sys_expr->parm(0, prop); return sys_expr; } + if (method_name == "sum") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: sum() method " + << "takes no arguments" << endl; + des->errors += 1; + } + if (with_expr_) { + cerr << get_fileline() << ": sorry: queue sum() with a " + << "`with` clause is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$sum", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } if (method_name == "min" || method_name == "max") { if (parms_.size() != 0) { cerr << get_fileline() << ": error: " << method_name @@ -4334,6 +4375,31 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, sys_expr->parm(1, cmp); return sys_expr; } + if (method_name == "sum") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: sum() method " + << "takes no arguments" << endl; + des->errors += 1; + } + if (with_expr_) { + cerr << get_fileline() << ": sorry: array sum() with a " + << "`with` clause is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: array sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$sum", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } if (method_name == "min" || method_name == "max") { if (parms_.size() != 0) { cerr << get_fileline() << ": error: " << method_name @@ -4709,6 +4775,31 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, sys_expr->parm(1, cmp); return sys_expr; } + if (method_name == "sum") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: sum() method " + << "takes no arguments" << endl; + des->errors += 1; + } + if (with_expr_) { + cerr << get_fileline() << ": sorry: dynamic array sum() with a " + << "`with` clause is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$sum", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } if (method_name == "min" || method_name == "max") { if (parms_.size() != 0) { cerr << get_fileline() << ": error: " << method_name @@ -4855,6 +4946,31 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, sys_expr->parm(0, sub_expr); return sys_expr; } + if (method_name == "sum") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: sum() method " + << "takes no arguments" << endl; + des->errors += 1; + } + if (with_expr_) { + cerr << get_fileline() << ": sorry: queue sum() with a " + << "`with` clause is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$sum", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } if (method_name == "min" || method_name == "max") { if (parms_.size() != 0) { cerr << get_fileline() << ": error: " << method_name @@ -6303,6 +6419,22 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, fun->parm(0, arg); return fun; } + if (member_comp.name == "sum") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc( + "$ivl_queue_method$sum", + element_type, 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; + } if (member_comp.name == "min" || member_comp.name == "max") { if (!queue_method_element_is_integral_vec4(element_type)) { cerr << get_fileline() << ": sorry: queue " << member_comp.name @@ -6634,6 +6766,22 @@ NetExpr* PEIdent::elaborate_expr_(Design*des, NetScope*scope, fun->parm(0, arg); return fun; } + if (member_comp.name == "sum") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc( + "$ivl_queue_method$sum", + element_type, 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; + } if (member_comp.name == "min" || member_comp.name == "max") { if (!queue_method_element_is_integral_vec4(element_type)) { cerr << get_fileline() << ": sorry: queue " << member_comp.name @@ -6714,11 +6862,22 @@ NetExpr* PEIdent::elaborate_expr_(Design*des, NetScope*scope, return 0; // FIXME: Check this is a real or integral type. } else if (member_comp.name == "sum") { - cerr << get_fileline() << ": sorry: 'sum()' " - "array reduction method is not currently " - "implemented." << endl; - des->errors += 1; - return 0; + const netdarray_t* dar_sum = sr.net->darray_type(); + ivl_assert(*this, dar_sum); + ivl_type_t element_type = dar_sum->element_type(); + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc("$ivl_queue_method$sum", + element_type, 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; } else if (member_comp.name == "product") { cerr << get_fileline() << ": sorry: 'product()' " "array reduction method is not currently " diff --git a/ivtest/ivltests/README_sv_queue_locators.txt b/ivtest/ivltests/README_sv_queue_locators.txt index d0f0a16f9..ad992413b 100644 --- a/ivtest/ivltests/README_sv_queue_locators.txt +++ b/ivtest/ivltests/README_sv_queue_locators.txt @@ -5,7 +5,7 @@ This directory includes regression tests for IEEE 1800 locator methods on unpacked queues (int q[$]) and dynamic arrays (int da[]): find, find_index, find_first, find_first_index, find_last, - find_last_index, min, max, unique, unique_index + find_last_index, min, max, unique, unique_index, sum (integral) Behavior notes (LRM-oriented): @@ -49,3 +49,5 @@ Regression tests (see ivtest/vvp_tests/*.json and regress-vvp.list): sv_queue_unique_with.v unique()/unique_index() with predicate on queues. sv_darray_unique_with.v unique()/unique_index() with predicate on dynamic arrays. sv_class_queue_prop_locators.v locator methods on class queue properties. + sv_queue_sum.v integral sum() reduction on queues. + sv_darray_sum.v integral sum() reduction on dynamic arrays. diff --git a/ivtest/ivltests/sv_darray_sum.v b/ivtest/ivltests/sv_darray_sum.v new file mode 100644 index 000000000..0ae6986fe --- /dev/null +++ b/ivtest/ivltests/sv_darray_sum.v @@ -0,0 +1,28 @@ +// Regression: dynamic array sum() reduction (integral). + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int a[]; + int s; + + initial begin + a = new[0]; + s = a.sum(); + `CHK(s === 0); + + a = '{10, -3, 5}; + s = a.sum(); + `CHK(s === 12); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_sum.v b/ivtest/ivltests/sv_queue_sum.v new file mode 100644 index 000000000..49baa17ee --- /dev/null +++ b/ivtest/ivltests/sv_queue_sum.v @@ -0,0 +1,28 @@ +// Regression: queue sum() reduction (integral). + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int q[$]; + int s; + + initial begin + q = '{}; + s = q.sum(); + `CHK(s === 0); + + q = '{4, 7, 2}; + s = q.sum(); + `CHK(s === 13); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index ac42dc5c0..80a9e9b0d 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -257,6 +257,7 @@ sv_darray_assign_op vvp_tests/sv_darray_assign_op.json sv_darray_find_locators vvp_tests/sv_darray_find_locators.json sv_darray_min_max vvp_tests/sv_darray_min_max.json sv_darray_min_max_with vvp_tests/sv_darray_min_max_with.json +sv_darray_sum vvp_tests/sv_darray_sum.json sv_darray_unique vvp_tests/sv_darray_unique.json sv_darray_unique_with vvp_tests/sv_darray_unique_with.json sv_default_port_value1 vvp_tests/sv_default_port_value1.json @@ -284,6 +285,7 @@ sv_queue_find_locators_ext vvp_tests/sv_queue_find_locators_ext.json sv_queue_find_with vvp_tests/sv_queue_find_with.json sv_queue_min_max vvp_tests/sv_queue_min_max.json sv_queue_min_max_with vvp_tests/sv_queue_min_max_with.json +sv_queue_sum vvp_tests/sv_queue_sum.json sv_wildcard_import8 vvp_tests/sv_wildcard_import8.json sdf_header vvp_tests/sdf_header.json task_return1 vvp_tests/task_return1.json diff --git a/ivtest/vvp_tests/sv_darray_sum.json b/ivtest/vvp_tests/sv_darray_sum.json new file mode 100644 index 000000000..844957ccc --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_sum.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_sum.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog dynamic array sum() reduction", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_sum.json b/ivtest/vvp_tests/sv_queue_sum.json new file mode 100644 index 000000000..523bc9dee --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_sum.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_sum.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queue sum() reduction", + "type" : "CE" + } +} diff --git a/tgt-vvp/eval_vec4.c b/tgt-vvp/eval_vec4.c index 11b045501..a17be29f6 100644 --- a/tgt-vvp/eval_vec4.c +++ b/tgt-vvp/eval_vec4.c @@ -1119,6 +1119,24 @@ static void draw_sfunc_vec4(ivl_expr_t expr) } } + if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$sum") == 0 && + parm_count == 1) { + ivl_expr_t arg = ivl_expr_parm(expr, 0); + unsigned wid = ivl_expr_width(expr); + 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, " %%queue/sum/prop/v %u, %u;\n", pidx, wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); + fprintf(vvp_out, " %%queue/sum/v v%p_0, %u;\n", + ivl_expr_signal(arg), wid); + return; + } + draw_vpi_func_call(expr); } diff --git a/vvp/codes.h b/vvp/codes.h index be50a0fdb..a1c3b0f4b 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -252,6 +252,8 @@ extern bool of_PROP_QUEUE_SIZE(vthread_t thr, vvp_code_t code); extern bool of_QUEUE_APPEND_WORD_V(vthread_t thr, vvp_code_t code); extern bool of_QUEUE_NEW_EMPTY_V(vthread_t thr, vvp_code_t code); extern bool of_QUEUE_SIZE_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_SUM_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_SUM_V(vthread_t thr, vvp_code_t code); extern bool of_QUEUE_WORD_PROP_V(vthread_t thr, vvp_code_t code); extern bool of_QUEUE_WORD_V(vthread_t thr, vvp_code_t code); extern bool of_CMPIX_LTU(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index e2de497ef..a6bab1c45 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -299,6 +299,8 @@ static const struct opcode_table_s opcode_table[] = { { "%queue/min/v", of_QUEUE_MIN_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, { "%queue/new_empty/v", of_QUEUE_NEW_EMPTY_V, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%queue/size/v", of_QUEUE_SIZE_V, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, + { "%queue/sum/prop/v", of_QUEUE_SUM_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/sum/v", of_QUEUE_SUM_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, { "%queue/unique/index/obj/v", of_QUEUE_UNIQUE_INDEX_OBJ_V, 1, {OA_BIT1, OA_NONE, OA_NONE} }, { "%queue/unique/index/prop/v", of_QUEUE_UNIQUE_INDEX_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, { "%queue/unique/index/v", of_QUEUE_UNIQUE_INDEX_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 5a40e727e..1997647b8 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -6410,6 +6410,63 @@ static vvp_queue_vec4* queue_run_min_max_src(SRC* src, unsigned wid, bool want_m return dst; } +/* sum() reduction returns the scalar sum (Verilog-style addition per word). */ +template +static vvp_vector4_t queue_sum_words_src(SRC* src, unsigned wid) +{ + vvp_vector4_t acc(wid, BIT4_0); + if (!src) + return acc; + for (size_t i = 0; i < src->get_size(); i += 1) { + vvp_vector4_t vi(wid); + src->get_word(i, vi); + acc.add(vi); + } + return acc; +} + +bool of_QUEUE_SUM_V(vthread_t thr, vvp_code_t cp) +{ + vvp_net_t* net = cp->net; + unsigned wid = cp->bit_idx[0]; + vvp_queue_vec4* qsrc = 0; + vvp_darray* dsrc = 0; + get_queue_or_darray_vec4_from_net(thr, net, qsrc, dsrc); + vvp_vector4_t sum; + if (qsrc) + sum = queue_sum_words_src(qsrc, wid); + else if (dsrc) + sum = queue_sum_words_src(dsrc, wid); + else { + vvp_queue* src_q = get_queue_object(thr, net); + vvp_queue_vec4* src = dynamic_cast(src_q); + sum = queue_sum_words_src(src, wid); + } + thr->push_vec4(sum); + return true; +} + +bool of_QUEUE_SUM_PROP_V(vthread_t thr, vvp_code_t cp) +{ + size_t pid = cp->number; + unsigned wid = cp->bit_idx[0]; + + 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_vec4* qsrc = 0; + vvp_darray* dsrc = 0; + get_queue_or_darray_vec4_from_object(qobj, qsrc, dsrc); + vvp_vector4_t sum = + qsrc ? queue_sum_words_src(qsrc, wid) + : queue_sum_words_src(dsrc, wid); + thr->push_vec4(sum); + return true; +} + bool of_QUEUE_FIND_V(vthread_t thr, vvp_code_t cp) { vvp_vector4_t cmp = thr->pop_vec4();