Merge pull request #1421 from muhammadjawadkhan/split/05-sv-sum-product

SV: sum() and product() reductions on queues/darrays
This commit is contained in:
Cary R. 2026-07-20 22:51:40 -07:00 committed by GitHub
commit f0b6d3addc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 810 additions and 37 deletions

View File

@ -105,8 +105,14 @@ static NetExpr* elab_queue_locator_with_predicate(
index_net->set_line(loc);
index_net->local_flag(true);
NetExpr* pred = elab_and_eval(des, ws, with_expr, 1, false, false,
IVL_VT_BOOL);
NetExpr* pred = 0;
if (method_suffix == "sum" || method_suffix == "product") {
pred = elab_and_eval(des, ws, with_expr, (int)ew, false, false,
ivl_type_base(element_type));
} else {
pred = elab_and_eval(des, ws, with_expr, 1, false, false,
IVL_VT_BOOL);
}
if (!pred) return 0;
perm_string sfunc_name;
@ -130,6 +136,10 @@ static NetExpr* elab_queue_locator_with_predicate(
sfunc_name = lex_strings.make("$ivl_queue_method$min_with");
} else if (method_suffix == "max") {
sfunc_name = lex_strings.make("$ivl_queue_method$max_with");
} else if (method_suffix == "sum") {
sfunc_name = lex_strings.make("$ivl_queue_method$sum_with");
} else if (method_suffix == "product") {
sfunc_name = lex_strings.make("$ivl_queue_method$product_with");
} else {
ivl_assert(loc, 0);
}
@ -225,6 +235,58 @@ static NetExpr* elab_array_locator_method(Design* des, NetScope* scope,
return sys_expr;
}
static bool is_array_reduction_method(perm_string name)
{
return name == "sum" || name == "product";
}
/*
* Elaborate sum()/product() on a queue or dynamic array expression.
* Returns an integral value with the element type/width.
*/
static NetExpr* elab_array_reduction_method(Design* des, NetScope* scope,
const LineInfo& loc,
perm_string method_name,
NetExpr* array_arg,
ivl_type_t element_type,
const vector<named_pexpr_t>& parms,
PExpr* with_expr, const char* kind)
{
if (!queue_method_element_is_integral_vec4(element_type)) {
cerr << loc.get_fileline() << ": sorry: " << kind << " "
<< method_name << "() for this "
<< "element type is not yet supported." << endl;
des->errors += 1;
return 0;
}
char sfunc[64];
snprintf(sfunc, sizeof sfunc, "$ivl_queue_method$%s", method_name.str());
if (with_expr) {
if (!parms.empty()) {
cerr << loc.get_fileline() << ": error: array locator "
<< "`with` clause cannot be combined with a "
<< "method argument." << endl;
des->errors += 1;
return 0;
}
return elab_queue_locator_with_predicate(des, scope, loc, with_expr,
array_arg, element_type,
element_type, method_name);
}
if (parms.size() != 0) {
cerr << loc.get_fileline() << ": error: " << method_name
<< "() method takes no arguments" << endl;
des->errors += 1;
}
NetESFunc*sys_expr = new NetESFunc(sfunc, element_type, 1);
sys_expr->set_line(loc);
sys_expr->parm(0, array_arg);
return sys_expr;
}
bool type_is_vectorable(ivl_variable_type_t type)
{
switch (type) {
@ -1830,6 +1892,21 @@ 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 == "product") {
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") {
@ -1887,6 +1964,21 @@ 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 == "product") {
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" ||
method_name == "find" || method_name == "find_index") {
@ -3838,8 +3930,15 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
static_cast<ivl_type_t>(queue),
parms_, with_expr_, "queue");
}
if (is_array_reduction_method(method_name)) {
return elab_array_reduction_method(des, scope, *this,
method_name, prop, element_type,
parms_, with_expr_, "queue");
}
}
if (ptype && ptype->base_type() == IVL_VT_DARRAY) {
if (ptype &&
(ptype->base_type() == IVL_VT_DARRAY ||
ptype->base_type() == IVL_VT_QUEUE)) {
NetEProperty*prop = new NetEProperty(search_results.net, pidx, nullptr);
prop->set_line(*this);
perm_string method_name = search_results.path_tail.back().name;
@ -3859,7 +3958,12 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
return elab_array_locator_method(des, scope, *this,
method_name, prop, element_type,
ptype, parms_, with_expr_,
"dynamic array");
"array");
}
if (is_array_reduction_method(method_name)) {
return elab_array_reduction_method(des, scope, *this,
method_name, prop, element_type,
parms_, with_expr_, "array");
}
}
}
@ -3968,6 +4072,12 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
queue_rtype, parms_, with_expr_,
"dynamic array");
}
if (is_array_reduction_method(method_name)) {
return elab_array_reduction_method(des, scope, *this, method_name,
sub_expr, element_type,
parms_, with_expr_,
"dynamic array");
}
cerr << get_fileline() << ": error: Method " << method_name
<< " is not a dynamic array method." << endl;
@ -4027,6 +4137,11 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
static_cast<ivl_type_t>(queue),
parms_, with_expr_, "queue");
}
if (is_array_reduction_method(method_name)) {
return elab_array_reduction_method(des, scope, *this, method_name,
sub_expr, element_type,
parms_, with_expr_, "queue");
}
cerr << get_fileline() << ": error: Method " << method_name
<< " is not a queue method." << endl;
@ -5252,6 +5367,38 @@ 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 == "product") {
if (!queue_method_element_is_integral_vec4(element_type)) {
cerr << get_fileline() << ": sorry: queue product() for this "
<< "element type is not yet supported." << endl;
des->errors += 1;
return 0;
}
NetESFunc*fun = new NetESFunc(
"$ivl_queue_method$product",
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
@ -5586,6 +5733,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
@ -5666,17 +5829,39 @@ 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 "
"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 product() for this "
<< "element type is not yet supported." << endl;
des->errors += 1;
return 0;
}
NetESFunc*fun = new NetESFunc("$ivl_queue_method$product",
element_type, 1);
fun->set_line(*this);
NetESignal*arg = new NetESignal(sr.net);
arg->set_line(*sr.net);
fun->parm(0, arg);
return fun;
// FIXME: Check this is only an integral type.
} else if (member_comp.name == "and") {
cerr << get_fileline() << ": sorry: 'and()' "

View File

@ -57,6 +57,9 @@ module test;
`check(r.size, 1);
`check(r[0], 6);
c.d = '{2, 3, 4};
`check(c.d.product(), 24);
if (!failed)
$display("PASSED");
end

View File

@ -0,0 +1,58 @@
// Regression: class queue property locator methods.
module test;
bit failed = 1'b0;
`define check(val, exp) do if ((val) !== (exp)) begin $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); failed = 1'b1; end while (0)
class C;
int q[$];
endclass
C c;
int r[$];
initial begin
c = new;
c.q = '{4, 7, 2, 5, 7, 1, 6, 3, 1};
r = c.q.find() with (item > 3);
`check(r.size, 5);
`check(r[0], 4);
`check(r[4], 6);
r = c.q.find_last_index() with (item < 3);
`check(r.size, 1);
`check(r[0], 8);
r = c.q.unique() with (item > 2);
`check(r.size, 5);
`check(r[0], 4);
`check(r[4], 3);
r = c.q.unique_index() with (item > 2);
`check(r.size, 6);
`check(r[0], 0);
`check(r[5], 7);
r = c.q.min();
`check(r.size, 2);
`check(r[0], 1);
`check(r[1], 1);
r = c.q.max() with (item < 7);
`check(r.size, 1);
`check(r[0], 6);
r = c.q.max();
`check(r.size, 2);
`check(r[0], 7);
c.q = '{2, 3, 4};
`check(c.q.product(), 24);
if (!failed)
$display("PASSED");
end
endmodule

View File

@ -0,0 +1,24 @@
// Regression: dynamic array product() reduction (integral).
module top;
bit failed = 0;
`define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end
int a[];
int p;
initial begin
a = '{2, 3, 4};
p = a.product();
`CHK(p === 24);
a = '{-2, 3, 5};
p = a.product();
`CHK(p === -30);
if (!failed)
$display("PASSED");
end
endmodule

View File

@ -0,0 +1,24 @@
// Regression: dynamic array product() reduction with expression.
module top;
bit failed = 0;
`define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end
int a[];
int p;
initial begin
a = '{10, -3, 5};
p = a.product() with (item > 0);
`CHK(p === 0);
p = a.product() with (item + 1);
`CHK(p === -132);
if (!failed)
$display("PASSED");
end
endmodule

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,24 @@
// Regression: dynamic array sum() reduction with expression.
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 = '{10, -3, 5};
s = a.sum() with (item > 0);
`CHK(s === 2);
s = a.sum() with (item + 1);
`CHK(s === 15);
if (!failed)
$display("PASSED");
end
endmodule

View File

@ -0,0 +1,24 @@
// Regression: queue product() reduction (integral).
module top;
bit failed = 0;
`define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end
int q[$];
int p;
initial begin
q = '{2, 3, 4};
p = q.product();
`CHK(p === 24);
q = '{-2, 3, 5};
p = q.product();
`CHK(p === -30);
if (!failed)
$display("PASSED");
end
endmodule

View File

@ -0,0 +1,24 @@
// Regression: queue product() reduction with expression.
module top;
bit failed = 0;
`define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end
int q[$];
int p;
initial begin
q = '{4, 7, 2};
p = q.product() with (item > 3);
`CHK(p === 0);
p = q.product() with (item + 1);
`CHK(p === 120);
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

@ -0,0 +1,24 @@
// Regression: queue sum() reduction with expression.
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 = '{4, 7, 2, 5};
s = q.sum() with (item > 3);
`CHK(s === 3);
s = q.sum() with (item * 2);
`CHK(s === 36);
if (!failed)
$display("PASSED");
end
endmodule

View File

@ -291,6 +291,7 @@ sv_class_darray_prop_locators vvp_tests/sv_class_darray_prop_locators.json
sv_class_prop_packed_dims vvp_tests/sv_class_prop_packed_dims.json
sv_class_prop_type_name vvp_tests/sv_class_prop_type_name.json
sv_class_prop_wildcard_type_name vvp_tests/sv_class_prop_wildcard_type_name.json
sv_class_queue_prop_locators vvp_tests/sv_class_queue_prop_locators.json
sv_class_prop_nest_darray1 vvp_tests/sv_class_prop_nest_darray1.json
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
@ -316,6 +317,10 @@ 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_product vvp_tests/sv_darray_product.json
sv_darray_product_with vvp_tests/sv_darray_product_with.json
sv_darray_sum vvp_tests/sv_darray_sum.json
sv_darray_sum_with vvp_tests/sv_darray_sum_with.json
sv_darray_unique vvp_tests/sv_darray_unique.json
sv_darray_unique_with vvp_tests/sv_darray_unique_with.json
sv_declaration_after_null_statement_fail vvp_tests/sv_declaration_after_null_statement_fail.json
@ -415,6 +420,10 @@ sv_queue_method_push_front_too_few_arg_fail vvp_tests/sv_queue_method_push_front
sv_queue_method_push_front_too_many_arg_fail vvp_tests/sv_queue_method_push_front_too_many_arg_fail.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_product vvp_tests/sv_queue_product.json
sv_queue_product_with vvp_tests/sv_queue_product_with.json
sv_queue_sum vvp_tests/sv_queue_sum.json
sv_queue_sum_with vvp_tests/sv_queue_sum_with.json
sv_queue_unique vvp_tests/sv_queue_unique.json
sv_queue_unique_with vvp_tests/sv_queue_unique_with.json
sv_soft_packed_union vvp_tests/sv_soft_packed_union.json

View File

@ -0,0 +1,9 @@
{
"type" : "normal",
"source" : "sv_class_queue_prop_locators.v",
"iverilog-args" : [ "-g2005-sv" ],
"vlog95" : {
"__comment" : "SystemVerilog class queue property locator methods",
"type" : "CE"
}
}

View File

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

View File

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

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_darray_sum_with.v",
"iverilog-args" : [ "-g2005-sv" ],
"vlog95" : {
"__comment" : "SystemVerilog dynamic array sum() with expression",
"type" : "CE"
}
}

View File

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

View File

@ -0,0 +1,9 @@
{
"type" : "normal",
"source" : "sv_queue_product_with.v",
"iverilog-args" : [ "-g2005-sv" ],
"vlog95" : {
"__comment" : "SystemVerilog queue product() with expression",
"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

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

View File

@ -173,33 +173,59 @@ static int eval_darray_new(ivl_expr_t ex)
return errors;
}
/* Build a dynamic-array object value from an array pattern expression. */
static int eval_darray_pattern_object(ivl_expr_t ex)
/* Build a queue/dynamic-array object value from an array pattern expression. */
static int eval_array_pattern_object(ivl_expr_t ex)
{
int errors = 0;
ivl_type_t net_type = ivl_expr_net_type(ex);
if (!net_type || ivl_type_base(net_type) != IVL_VT_DARRAY) return 1;
if (!net_type) return 1;
ivl_variable_type_t base_type = ivl_type_base(net_type);
if (base_type != IVL_VT_DARRAY && base_type != IVL_VT_QUEUE) return 1;
ivl_type_t element_type = ivl_type_element(net_type);
if (!element_type) return 1;
unsigned size_reg = allocate_word();
fprintf(vvp_out, " %%ix/load %u, %u, 0;\n", size_reg, ivl_expr_parms(ex));
fprintf(vvp_out, " %%flag_set/imm 4, 0;\n");
darray_new(element_type, size_reg);
unsigned max_elems = ivl_expr_parms(ex);
if (base_type == IVL_VT_QUEUE) {
unsigned max_size = ivl_type_queue_max(net_type);
if (max_size != 0 && max_elems > max_size) {
fprintf(stderr, "%s:%u: Warning: Array pattern assignment has more "
"elements (%u) than bounded queue supports (%u).\n"
" Only using first %u elements.\n",
ivl_expr_file(ex), ivl_expr_lineno(ex), max_elems, max_size, max_size);
max_elems = max_size;
}
fprintf(vvp_out, " %%queue/new_empty/v;\n");
} else {
unsigned size_reg = allocate_word();
fprintf(vvp_out, " %%ix/load %u, %u, 0;\n", size_reg, max_elems);
fprintf(vvp_out, " %%flag_set/imm 4, 0;\n");
darray_new(element_type, size_reg);
}
switch (ivl_type_base(element_type)) {
case IVL_VT_BOOL:
case IVL_VT_LOGIC:
for (unsigned idx = 0; idx < ivl_expr_parms(ex); idx += 1) {
for (unsigned idx = 0; idx < max_elems; idx += 1) {
draw_eval_vec4(ivl_expr_parm(ex, idx));
fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx);
fprintf(vvp_out, " %%set/dar/obj/vec4 3;\n");
fprintf(vvp_out, " %%pop/vec4 1;\n");
if (base_type == IVL_VT_QUEUE) {
fprintf(vvp_out, " %%queue/append_word/v %u;\n",
ivl_type_packed_width(element_type));
} else {
fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx);
fprintf(vvp_out, " %%set/dar/obj/vec4 3;\n");
fprintf(vvp_out, " %%pop/vec4 1;\n");
}
}
break;
case IVL_VT_REAL:
for (unsigned idx = 0; idx < ivl_expr_parms(ex); idx += 1) {
for (unsigned idx = 0; idx < max_elems; idx += 1) {
if (base_type == IVL_VT_QUEUE) {
fprintf(vvp_out, "; ERROR: eval_array_pattern_object: queue real "
"element type not implemented\n");
errors += 1;
break;
}
draw_eval_real(ivl_expr_parm(ex, idx));
fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx);
fprintf(vvp_out, " %%set/dar/obj/real 3;\n");
@ -207,7 +233,13 @@ static int eval_darray_pattern_object(ivl_expr_t ex)
}
break;
case IVL_VT_STRING:
for (unsigned idx = 0; idx < ivl_expr_parms(ex); idx += 1) {
for (unsigned idx = 0; idx < max_elems; idx += 1) {
if (base_type == IVL_VT_QUEUE) {
fprintf(vvp_out, "; ERROR: eval_array_pattern_object: queue string "
"element type not implemented\n");
errors += 1;
break;
}
draw_eval_string(ivl_expr_parm(ex, idx));
fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx);
fprintf(vvp_out, " %%set/dar/obj/str 3;\n");
@ -215,7 +247,7 @@ static int eval_darray_pattern_object(ivl_expr_t ex)
}
break;
default:
fprintf(vvp_out, "; ERROR: eval_darray_pattern_object: unsupported "
fprintf(vvp_out, "; ERROR: eval_array_pattern_object: unsupported "
"element type %d\n", ivl_type_base(element_type));
errors += 1;
break;
@ -397,7 +429,9 @@ enum queue_locator_with_mode_e {
QUEUE_WITH_MIN,
QUEUE_WITH_MAX,
QUEUE_WITH_UNIQUE,
QUEUE_WITH_UNIQUE_INDEX
QUEUE_WITH_UNIQUE_INDEX,
QUEUE_WITH_SUM,
QUEUE_WITH_PRODUCT
};
static enum queue_locator_with_mode_e queue_locator_with_mode_from_name(
@ -423,6 +457,10 @@ static enum queue_locator_with_mode_e queue_locator_with_mode_from_name(
return QUEUE_WITH_UNIQUE;
} else if (strcmp(name, "$ivl_queue_method$unique_index_with") == 0) {
return QUEUE_WITH_UNIQUE_INDEX;
} else if (strcmp(name, "$ivl_queue_method$sum_with") == 0) {
return QUEUE_WITH_SUM;
} else if (strcmp(name, "$ivl_queue_method$product_with") == 0) {
return QUEUE_WITH_PRODUCT;
}
return (enum queue_locator_with_mode_e) -1;
}
@ -440,7 +478,14 @@ static int queue_with_multi(enum queue_locator_with_mode_e mode)
mode == QUEUE_WITH_MIN ||
mode == QUEUE_WITH_MAX ||
mode == QUEUE_WITH_UNIQUE ||
mode == QUEUE_WITH_UNIQUE_INDEX;
mode == QUEUE_WITH_UNIQUE_INDEX ||
mode == QUEUE_WITH_SUM ||
mode == QUEUE_WITH_PRODUCT;
}
static int queue_with_expr_value(enum queue_locator_with_mode_e mode)
{
return mode == QUEUE_WITH_SUM || mode == QUEUE_WITH_PRODUCT;
}
static int queue_with_as_index(enum queue_locator_with_mode_e mode)
@ -527,6 +572,20 @@ static void emit_queue_with_finish(enum queue_locator_with_mode_e mode,
}
return;
}
if (mode == QUEUE_WITH_SUM) {
fprintf(vvp_out, " %%queue/sum/obj/v %u;\n", elem_wid);
if (is_prop) {
fprintf(vvp_out, " %%pop/obj 1, 0;\n");
}
return;
}
if (mode == QUEUE_WITH_PRODUCT) {
fprintf(vvp_out, " %%queue/product/obj/v %u;\n", elem_wid);
if (is_prop) {
fprintf(vvp_out, " %%pop/obj 1, 0;\n");
}
return;
}
if (queue_with_multi(mode)) {
if (is_prop) {
fprintf(vvp_out, " %%pop/obj 1, 1;\n");
@ -608,11 +667,16 @@ static int eval_queue_method_find_with(ivl_expr_t expr)
fprintf(vvp_out, " %%push/ix/vec4 %d, 32, 1;\n", i_reg);
fprintf(vvp_out, " %%store/vec4 v%p_0, 0, 32;\n", idx_sig);
int pf = draw_eval_condition(pred);
fprintf(vvp_out, " %%jmp/0 T_%u.%u, %d;\n", thread_count, lab_nom, pf);
clr_flag(pf);
emit_queue_with_on_match(mode, item_sig, i_reg, elem_wid, match_lab);
if (queue_with_expr_value(mode)) {
draw_eval_vec4(pred);
fprintf(vvp_out, " %%queue/append_word/v %u;\n", elem_wid);
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom);
} else {
int pf = draw_eval_condition(pred);
fprintf(vvp_out, " %%jmp/0 T_%u.%u, %d;\n", thread_count, lab_nom, pf);
clr_flag(pf);
emit_queue_with_on_match(mode, item_sig, i_reg, elem_wid, match_lab);
}
fprintf(vvp_out, "T_%u.%u ; nomatch\n", thread_count, lab_nom);
emit_queue_with_step_index(reverse, i_reg, lab_top);
@ -720,7 +784,7 @@ int draw_eval_object(ivl_expr_t ex)
return eval_object_ufunc(ex);
case IVL_EX_ARRAY_PATTERN:
return eval_darray_pattern_object(ex);
return eval_array_pattern_object(ex);
case IVL_EX_SFUNC:
/* Queue locator `with` may report IVL_VT_DARRAY in ivl; handle by name. */

View File

@ -1130,6 +1130,28 @@ static void draw_sfunc_vec4(ivl_expr_t expr)
}
}
if ((strcmp(ivl_expr_name(expr), "$ivl_queue_method$sum") == 0 ||
strcmp(ivl_expr_name(expr), "$ivl_queue_method$product") == 0) &&
parm_count == 1) {
ivl_expr_t arg = ivl_expr_parm(expr, 0);
unsigned wid = ivl_expr_width(expr);
const char* op = strcmp(ivl_expr_name(expr), "$ivl_queue_method$product") == 0
? "product"
: "sum";
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/%s/prop/v %u, %u;\n", op, pidx, wid);
fprintf(vvp_out, " %%pop/obj 1, 0;\n");
return;
}
assert(ivl_expr_type(arg) == IVL_EX_SIGNAL);
fprintf(vvp_out, " %%queue/%s/v v%p_0, %u;\n",
op, ivl_expr_signal(arg), wid);
return;
}
draw_vpi_func_call(expr);
}

View File

@ -252,7 +252,13 @@ 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_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_PRODUCT_PROP_V(vthread_t thr, vvp_code_t code);
extern bool of_QUEUE_PRODUCT_OBJ_V(vthread_t thr, vvp_code_t code);
extern bool of_QUEUE_PRODUCT_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_OBJ_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

@ -52,7 +52,7 @@ unsigned compile_errors = 0;
* 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/*
* 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.
*/
@ -299,7 +299,13 @@ static const struct opcode_table_s opcode_table[] = {
{ "%queue/min/prop/v", of_QUEUE_MIN_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} },
{ "%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/product/obj/v", of_QUEUE_PRODUCT_OBJ_V, 1, {OA_BIT1, OA_NONE, OA_NONE} },
{ "%queue/product/prop/v", of_QUEUE_PRODUCT_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} },
{ "%queue/product/v", of_QUEUE_PRODUCT_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} },
{ "%queue/size/v", of_QUEUE_SIZE_V, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} },
{ "%queue/sum/obj/v", of_QUEUE_SUM_OBJ_V, 1, {OA_BIT1, 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

@ -6326,6 +6326,145 @@ 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;
}
template <class SRC>
static vvp_vector4_t queue_product_words_src(SRC* src, unsigned wid)
{
vvp_vector4_t acc = queue_unique_ulong_to_vec4(1UL, wid);
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.mul(vi);
}
return acc;
}
bool of_QUEUE_PRODUCT_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 prod;
if (qsrc) prod = queue_product_words_src(qsrc, wid);
else if (dsrc) prod = queue_product_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);
prod = queue_product_words_src(src, wid);
}
thr->push_vec4(prod);
return true;
}
bool of_QUEUE_PRODUCT_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 prod =
qsrc ? queue_product_words_src(qsrc, wid)
: queue_product_words_src(dsrc, wid);
thr->push_vec4(prod);
return true;
}
bool of_QUEUE_PRODUCT_OBJ_V(vthread_t thr, vvp_code_t cp)
{
unsigned wid = cp->bit_idx[0];
vvp_object_t src_obj;
thr->pop_object(src_obj);
vvp_queue_vec4* qsrc = 0;
vvp_darray* dsrc = 0;
get_queue_or_darray_vec4_from_object(src_obj, qsrc, dsrc);
vvp_vector4_t prod =
qsrc ? queue_product_words_src(qsrc, wid)
: queue_product_words_src(dsrc, wid);
thr->push_vec4(prod);
return true;
}
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_SUM_OBJ_V(vthread_t thr, vvp_code_t cp)
{
unsigned wid = cp->bit_idx[0];
vvp_object_t src_obj;
thr->pop_object(src_obj);
vvp_queue_vec4* qsrc = 0;
vvp_darray* dsrc = 0;
get_queue_or_darray_vec4_from_object(src_obj, 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();