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
This commit is contained in:
mjoekhan 2026-04-28 22:29:40 +05:00
parent 7fe2ddda0b
commit f9e3f9a3e4
11 changed files with 322 additions and 6 deletions

View File

@ -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 "

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,9 @@
{
"type" : "normal",
"source" : "sv_darray_sum.v",
"iverilog-args" : [ "-g2005-sv" ],
"vlog95" : {
"__comment" : "SystemVerilog dynamic array sum() reduction",
"type" : "CE"
}
}

View File

@ -0,0 +1,9 @@
{
"type" : "normal",
"source" : "sv_queue_sum.v",
"iverilog-args" : [ "-g2005-sv" ],
"vlog95" : {
"__comment" : "SystemVerilog queue sum() reduction",
"type" : "CE"
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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} },

View File

@ -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 <class SRC>
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<vvp_queue_vec4>(thr, net);
vvp_queue_vec4* src = dynamic_cast<vvp_queue_vec4*>(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<vvp_cobject>();
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();