diff --git a/elab_expr.cc b/elab_expr.cc index bb6d34eb1..ed7e450a9 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1736,7 +1736,8 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, return expr_width_; } - if (method_name == "find" || method_name == "find_index") { + if (method_name == "find" || method_name == "find_index" || + method_name == "min" || method_name == "max") { expr_type_ = IVL_VT_QUEUE; expr_width_ = 1; min_width_ = 1; @@ -1791,7 +1792,8 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, return expr_width_; } - if (method_name == "unique" || method_name == "unique_index") { + if (method_name == "unique" || method_name == "unique_index" || + method_name == "min" || method_name == "max") { expr_type_ = IVL_VT_QUEUE; expr_width_ = 1; min_width_ = 1; @@ -3788,6 +3790,26 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, 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 + << "() method takes no arguments" << endl; + des->errors += 1; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue " << method_name + << "() for this element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + method_name == "min" ? "$ivl_queue_method$min" + : "$ivl_queue_method$max", + static_cast(queue), 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } if (method_name == "find") { if (!queue_method_element_is_integral_vec4(element_type)) { cerr << get_fileline() << ": sorry: queue find() for this " @@ -4289,6 +4311,32 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, sys_expr->parm(1, cmp); return sys_expr; } + if (method_name == "min" || method_name == "max") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: " << method_name + << "() method takes no arguments" << endl; + des->errors += 1; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array " << method_name + << "() for this element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + cerr << get_fileline() << ": sorry: dynamic array " << method_name + << "() with a `with` clause is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + method_name == "min" ? "$ivl_queue_method$min" + : "$ivl_queue_method$max", + queue_rtype, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } cerr << get_fileline() << ": error: Method " << method_name << " is not a dynamic array method." << endl; @@ -4381,6 +4429,32 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, 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 + << "() method takes no arguments" << endl; + des->errors += 1; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue " << method_name + << "() for this element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + cerr << get_fileline() << ": sorry: queue " << method_name + << "() with a `with` clause is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + method_name == "min" ? "$ivl_queue_method$min" + : "$ivl_queue_method$max", + static_cast(queue), 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } if (method_name == "find") { if (!queue_method_element_is_integral_vec4(element_type)) { @@ -5798,6 +5872,23 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, 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 + << "() for this element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc( + member_comp.name == "min" ? "$ivl_queue_method$min" + : "$ivl_queue_method$max", + static_cast(queue), 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; + } cerr << get_fileline() << ": error: Unknown or unsupported queue " << "member `" << member_comp.name << "'." << endl; des->errors += 1; @@ -6112,6 +6203,23 @@ NetExpr* PEIdent::elaborate_expr_(Design*des, NetScope*scope, 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 + << "() for this element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc( + member_comp.name == "min" ? "$ivl_queue_method$min" + : "$ivl_queue_method$max", + static_cast(queue), 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; + } } // Dynamic array (not queue) — array location / reduction methods. diff --git a/ivtest/ivltests/README_sv_queue_locators.txt b/ivtest/ivltests/README_sv_queue_locators.txt index b79af2018..310371202 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, unique, unique_index + find_last_index, min, max, unique, unique_index Behavior notes (LRM-oriented): @@ -17,6 +17,9 @@ Behavior notes (LRM-oriented): queue with zero or one element; no match yields an empty queue (not a scalar sentinel). + * min() and max() return queues containing all elements equal to the + selected extrema. + * 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 @@ -37,3 +40,5 @@ Regression tests (see ivtest/vvp_tests/*.json and regress-vvp.list): sv_queue_find_locators_ext.v Longer queue, compound predicates. sv_queue_unique.v unique / unique_index. sv_darray_find_locators.v Same locator patterns on int[] dynamic array. + sv_queue_min_max.v min() and max() on queue values. + sv_darray_min_max.v min() and max() on dynamic array values. diff --git a/ivtest/ivltests/sv_darray_min_max.v b/ivtest/ivltests/sv_darray_min_max.v new file mode 100644 index 000000000..58265c560 --- /dev/null +++ b/ivtest/ivltests/sv_darray_min_max.v @@ -0,0 +1,36 @@ +// Regression: dynamic array min() and max() locator methods return queues. + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int a[] = '{4, 7, 2, 5, 7, 1, 6, 3, 1}; + int empty[]; + int r[$]; + + initial begin + r = a.min(); + `CHK(r.size == 2); + `CHK(r[0] == 1); + `CHK(r[1] == 1); + + r = a.max(); + `CHK(r.size == 2); + `CHK(r[0] == 7); + `CHK(r[1] == 7); + + r = empty.min(); + `CHK(r.size == 0); + r = empty.max(); + `CHK(r.size == 0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_min_max.v b/ivtest/ivltests/sv_queue_min_max.v new file mode 100644 index 000000000..872bc0d44 --- /dev/null +++ b/ivtest/ivltests/sv_queue_min_max.v @@ -0,0 +1,47 @@ +// Regression: queue min() and max() locator methods return queues. + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int q[$]; + int e[$]; + int r[$]; + + initial begin + q.delete(); + q.push_back(4); + q.push_back(7); + q.push_back(2); + q.push_back(5); + q.push_back(7); + q.push_back(1); + q.push_back(6); + q.push_back(3); + q.push_back(1); + + r = q.min(); + `CHK(r.size == 2); + `CHK(r[0] == 1); + `CHK(r[1] == 1); + + r = q.max(); + `CHK(r.size == 2); + `CHK(r[0] == 7); + `CHK(r[1] == 7); + + r = e.min(); + `CHK(r.size == 0); + r = e.max(); + `CHK(r.size == 0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 8e1fc2ae2..7ba00290f 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -253,6 +253,7 @@ sv_const_fail8 vvp_tests/sv_const_fail8.json sv_const_fail9 vvp_tests/sv_const_fail9.json 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_default_port_value1 vvp_tests/sv_default_port_value1.json sv_default_port_value2 vvp_tests/sv_default_port_value2.json sv_default_port_value3 vvp_tests/sv_default_port_value3.json @@ -276,6 +277,7 @@ sv_queue_unique vvp_tests/sv_queue_unique.json sv_queue_find vvp_tests/sv_queue_find.json 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_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_min_max.json b/ivtest/vvp_tests/sv_darray_min_max.json new file mode 100644 index 000000000..d184b70b4 --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_min_max.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_min_max.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog dynamic array min/max locator methods", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_min_max.json b/ivtest/vvp_tests/sv_queue_min_max.json new file mode 100644 index 000000000..623bad2f8 --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_min_max.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_min_max.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queue min/max locator methods", + "type" : "CE" + } +} diff --git a/tgt-vvp/eval_object.c b/tgt-vvp/eval_object.c index dfcc1ac1d..7a542ed4f 100644 --- a/tgt-vvp/eval_object.c +++ b/tgt-vvp/eval_object.c @@ -315,6 +315,26 @@ static int eval_queue_method_unique(ivl_expr_t expr) return 0; } + if (strcmp(name, "$ivl_queue_method$min") == 0 || + strcmp(name, "$ivl_queue_method$max") == 0) { + const char* opname = strcmp(name, "$ivl_queue_method$min") == 0 + ? "min" + : "max"; + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t cl = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", cl); + fprintf(vvp_out, " %%queue/%s/prop/v %u, %u;\n", opname, pidx, + elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + } else { + ivl_signal_t sig = ivl_expr_signal(arg); + fprintf(vvp_out, " %%queue/%s/v v%p_0, %u;\n", opname, sig, + elem_wid); + } + return 0; + } + return 1; } diff --git a/vvp/codes.h b/vvp/codes.h index 87514e5e5..ed383c939 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -269,6 +269,10 @@ extern bool of_QUEUE_FIND_LAST_INDEX_PROP_V(vthread_t thr, vvp_code_t code); extern bool of_QUEUE_FIND_LAST_INDEX_V(vthread_t thr, vvp_code_t code); extern bool of_QUEUE_FIND_LAST_PROP_V(vthread_t thr, vvp_code_t code); extern bool of_QUEUE_FIND_LAST_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_MIN_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_MIN_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_MAX_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_MAX_V(vthread_t thr, vvp_code_t code); extern bool of_QUEUE_UNIQUE_INDEX_PROP_V(vthread_t thr, vvp_code_t code); extern bool of_QUEUE_UNIQUE_INDEX_V(vthread_t thr, vvp_code_t code); extern bool of_QUEUE_UNIQUE_PROP_V(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index a40ba2714..d9171af77 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -291,6 +291,10 @@ static const struct opcode_table_s opcode_table[] = { { "%queue/find_last/index/v", of_QUEUE_FIND_LAST_INDEX_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, { "%queue/find_last/prop/v", of_QUEUE_FIND_LAST_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, { "%queue/find_last/v", of_QUEUE_FIND_LAST_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%queue/max/prop/v", of_QUEUE_MAX_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/max/v", of_QUEUE_MAX_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%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/size/v", of_QUEUE_SIZE_V, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, { "%queue/unique/index/prop/v", of_QUEUE_UNIQUE_INDEX_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 96f127565..cb4c01672 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -6357,6 +6357,41 @@ static vvp_queue_vec4* queue_find_first_last_to_queue_src(SRC* src, return dst; } +/* min()/max() return all elements equal to the selected extrema. */ +template +static vvp_queue_vec4* queue_run_min_max_src(SRC* src, unsigned wid, bool want_max) +{ + vvp_queue_vec4* dst = new vvp_queue_vec4(); + if (!src || src->get_size() == 0) + return dst; + + vvp_vector4_t best(wid); + src->get_word(0, best); + dst->push_back(best, 0); + + size_t n = src->get_size(); + for (size_t i = 1; i < n; i += 1) { + vvp_vector4_t vi(wid); + src->get_word(i, vi); + if (vi.eeq(best)) { + dst->push_back(vi, 0); + continue; + } + + vvp_bit4_t ge = compare_gtge(vi, best, BIT4_1); + bool replace = want_max ? (ge == BIT4_1) : (ge == BIT4_0); + if (!replace) + continue; + + best = vi; + delete dst; + dst = new vvp_queue_vec4(); + dst->push_back(best, 0); + } + + return dst; +} + bool of_QUEUE_FIND_V(vthread_t thr, vvp_code_t cp) { vvp_vector4_t cmp = thr->pop_vec4(); @@ -6621,6 +6656,96 @@ bool of_QUEUE_FIND_LAST_INDEX_PROP_V(vthread_t thr, vvp_code_t cp) return true; } +bool of_QUEUE_MIN_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_queue_vec4* dst; + if (qsrc) + dst = queue_run_min_max_src(qsrc, wid, false); + else if (dsrc) + dst = queue_run_min_max_src(dsrc, wid, false); + else { + vvp_queue* src_q = get_queue_object(thr, net); + vvp_queue_vec4* src = dynamic_cast(src_q); + dst = queue_run_min_max_src(src, wid, false); + } + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_MIN_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 = qobj.peek(); + vvp_darray* dsrc = 0; + if (!qsrc) { + vvp_darray* dany = qobj.peek(); + if (dany && dynamic_cast(dany) == 0) + dsrc = dany; + } + vvp_queue_vec4* dst = qsrc ? queue_run_min_max_src(qsrc, wid, false) + : queue_run_min_max_src(dsrc, wid, false); + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_MAX_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_queue_vec4* dst; + if (qsrc) + dst = queue_run_min_max_src(qsrc, wid, true); + else if (dsrc) + dst = queue_run_min_max_src(dsrc, wid, true); + else { + vvp_queue* src_q = get_queue_object(thr, net); + vvp_queue_vec4* src = dynamic_cast(src_q); + dst = queue_run_min_max_src(src, wid, true); + } + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_MAX_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 = qobj.peek(); + vvp_darray* dsrc = 0; + if (!qsrc) { + vvp_darray* dany = qobj.peek(); + if (dany && dynamic_cast(dany) == 0) + dsrc = dany; + } + vvp_queue_vec4* dst = qsrc ? queue_run_min_max_src(qsrc, wid, true) + : queue_run_min_max_src(dsrc, wid, true); + thr->push_object(vvp_object_t(dst)); + return true; +} + bool of_QUEUE_UNIQUE_V(vthread_t thr, vvp_code_t cp) { vvp_net_t*net = cp->net;