SystemVerilog: add integral product() reduction on arrays
Implement queue/darray product() reduction (including class property paths) via new %queue/product/v and %queue/product/prop/v opcodes, with elaboration/codegen support and regressions for queue, dynamic-array, and class-property usage. Made-with: Cursor
This commit is contained in:
parent
0568961f6b
commit
03512faee6
151
elab_expr.cc
151
elab_expr.cc
|
|
@ -1758,6 +1758,13 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*,
|
|||
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" ||
|
||||
|
|
@ -1823,6 +1830,13 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*,
|
|||
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") {
|
||||
|
|
@ -3880,6 +3894,31 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
|
|||
sys_expr->parm(0, prop);
|
||||
return sys_expr;
|
||||
}
|
||||
if (method_name == "product") {
|
||||
if (parms_.size() != 0) {
|
||||
cerr << get_fileline() << ": error: product() method "
|
||||
<< "takes no arguments" << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
if (with_expr_) {
|
||||
cerr << get_fileline() << ": sorry: queue product() 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 product() for this "
|
||||
<< "element type is not yet supported." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
NetESFunc*sys_expr = new NetESFunc(
|
||||
"$ivl_queue_method$product",
|
||||
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
|
||||
|
|
@ -4419,6 +4458,31 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
|
|||
sys_expr->parm(0, prop);
|
||||
return sys_expr;
|
||||
}
|
||||
if (method_name == "product") {
|
||||
if (parms_.size() != 0) {
|
||||
cerr << get_fileline() << ": error: product() method "
|
||||
<< "takes no arguments" << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
if (with_expr_) {
|
||||
cerr << get_fileline() << ": sorry: array product() 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 product() for this "
|
||||
<< "element type is not yet supported." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
NetESFunc*sys_expr = new NetESFunc(
|
||||
"$ivl_queue_method$product",
|
||||
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
|
||||
|
|
@ -4824,6 +4888,31 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
|
|||
sys_expr->parm(0, sub_expr);
|
||||
return sys_expr;
|
||||
}
|
||||
if (method_name == "product") {
|
||||
if (parms_.size() != 0) {
|
||||
cerr << get_fileline() << ": error: product() method "
|
||||
<< "takes no arguments" << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
if (with_expr_) {
|
||||
cerr << get_fileline() << ": sorry: dynamic array product() 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 product() for this "
|
||||
<< "element type is not yet supported." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
NetESFunc*sys_expr = new NetESFunc(
|
||||
"$ivl_queue_method$product",
|
||||
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
|
||||
|
|
@ -5000,6 +5089,31 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
|
|||
sys_expr->parm(0, sub_expr);
|
||||
return sys_expr;
|
||||
}
|
||||
if (method_name == "product") {
|
||||
if (parms_.size() != 0) {
|
||||
cerr << get_fileline() << ": error: product() method "
|
||||
<< "takes no arguments" << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
if (with_expr_) {
|
||||
cerr << get_fileline() << ": sorry: queue product() 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 product() for this "
|
||||
<< "element type is not yet supported." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
NetESFunc*sys_expr = new NetESFunc(
|
||||
"$ivl_queue_method$product",
|
||||
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
|
||||
|
|
@ -6464,6 +6578,22 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
|
|||
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
|
||||
|
|
@ -6908,11 +7038,22 @@ NetExpr* PEIdent::elaborate_expr_(Design*des, NetScope*scope,
|
|||
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()' "
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ 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, sum (integral)
|
||||
find_last_index, min, max, unique, unique_index,
|
||||
sum/product (integral)
|
||||
|
||||
Behavior notes (LRM-oriented):
|
||||
|
||||
|
|
@ -24,6 +25,9 @@ Behavior notes (LRM-oriented):
|
|||
empty arrays/queues yield 0. `sum() with (expr)` reduces the expression
|
||||
result for each item.
|
||||
|
||||
* product() returns the scalar reduction product of elements (integral
|
||||
vector types).
|
||||
|
||||
* For dynamic arrays, runtime support treats storage as vvp_darray (including
|
||||
atom-backed integral arrays), not only vvp_queue_vec4. See vvp/vthread.cc:
|
||||
get_queue_or_darray_vec4_from_net() and the %queue/* opcodes used for
|
||||
|
|
@ -53,6 +57,8 @@ 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_product.v integral product() reduction on queues.
|
||||
sv_darray_product.v integral product() reduction on dynamic arrays.
|
||||
sv_queue_sum.v integral sum() reduction on queues.
|
||||
sv_darray_sum.v integral sum() reduction on dynamic arrays.
|
||||
sv_queue_sum_with.v sum() with expression on queues.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@ module test;
|
|||
`check(r.size, 2);
|
||||
`check(r[0], 7);
|
||||
|
||||
c.q = '{2, 3, 4};
|
||||
`check(c.q.product(), 24);
|
||||
|
||||
if (!failed)
|
||||
$display("PASSED");
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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_product vvp_tests/sv_darray_product.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
|
||||
|
|
@ -286,6 +287,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_product vvp_tests/sv_queue_product.json
|
||||
sv_queue_sum vvp_tests/sv_queue_sum.json
|
||||
sv_queue_sum_with vvp_tests/sv_queue_sum_with.json
|
||||
sv_wildcard_import8 vvp_tests/sv_wildcard_import8.json
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_darray_product.v",
|
||||
"iverilog-args" : [ "-g2005-sv" ],
|
||||
"vlog95" : {
|
||||
"__comment" : "SystemVerilog dynamic array product() reduction",
|
||||
"type" : "CE"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_queue_product.v",
|
||||
"iverilog-args" : [ "-g2005-sv" ],
|
||||
"vlog95" : {
|
||||
"__comment" : "SystemVerilog queue product() reduction",
|
||||
"type" : "CE"
|
||||
}
|
||||
}
|
||||
|
|
@ -1119,21 +1119,25 @@ static void draw_sfunc_vec4(ivl_expr_t expr)
|
|||
}
|
||||
}
|
||||
|
||||
if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$sum") == 0 &&
|
||||
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/sum/prop/v %u, %u;\n", pidx, wid);
|
||||
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/sum/v v%p_0, %u;\n",
|
||||
ivl_expr_signal(arg), wid);
|
||||
fprintf(vvp_out, " %%queue/%s/v v%p_0, %u;\n",
|
||||
op, ivl_expr_signal(arg), wid);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -251,6 +251,8 @@ 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_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);
|
||||
|
|
|
|||
|
|
@ -298,6 +298,8 @@ 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/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} },
|
||||
|
|
|
|||
|
|
@ -6425,6 +6425,62 @@ static vvp_vector4_t queue_sum_words_src(SRC* src, unsigned wid)
|
|||
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_SUM_V(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
vvp_net_t* net = cp->net;
|
||||
|
|
|
|||
Loading…
Reference in New Issue