From e597156d4d6fd7eaf0f4c9ed4db6e6aa133b3359 Mon Sep 17 00:00:00 2001 From: mjoekhan Date: Wed, 29 Apr 2026 00:48:56 +0500 Subject: [PATCH] SV: implement reverse() for queues and dynamic arrays Lower a.reverse() and q.reverse() to $ivl_darray_method$reverse with new VVP opcodes %reverse/obj and %reverse/prop/obj. Implement reverse_elems() on vvp_darray and queue storage types; dispatch queues via peek before vvp_darray so virtual overrides apply. Includes regressions for standalone arrays/queues and class properties. README_sv_queue_locators.txt documents behavior and tests. Made-with: Cursor --- elaborate.cc | 23 +++++-- ivtest/ivltests/README_sv_queue_locators.txt | 14 +++- .../ivltests/sv_class_darray_prop_locators.v | 6 ++ .../ivltests/sv_class_queue_prop_locators.v | 6 ++ ivtest/ivltests/sv_darray_reverse.v | 32 ++++++++++ ivtest/ivltests/sv_queue_reverse.v | 31 +++++++++ ivtest/regress-vvp.list | 2 + ivtest/vvp_tests/sv_darray_reverse.json | 9 +++ ivtest/vvp_tests/sv_queue_reverse.json | 9 +++ netlist.cc | 5 +- tgt-vvp/vvp_process.c | 35 +++++++++- vvp/codes.h | 2 + vvp/compile.cc | 2 + vvp/vthread.cc | 56 ++++++++++++++++ vvp/vvp_darray.cc | 64 +++++++++++++++++++ vvp/vvp_darray.h | 12 ++++ 16 files changed, 297 insertions(+), 11 deletions(-) create mode 100644 ivtest/ivltests/sv_darray_reverse.v create mode 100644 ivtest/ivltests/sv_queue_reverse.v create mode 100644 ivtest/vvp_tests/sv_darray_reverse.json create mode 100644 ivtest/vvp_tests/sv_queue_reverse.json diff --git a/elaborate.cc b/elaborate.cc index cdbbbeb0d..a3d71afbd 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -3965,6 +3965,12 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, &netvector_t::atom2s32, method_name, "$size"); } + if (method_name == "reverse") { + static const std::vector parm_names; + return elaborate_sys_task_method_(des, scope, prop, method_name, + "$ivl_darray_method$reverse", + parm_names); + } } else if (ptype && ptype->base_type() == IVL_VT_DARRAY) { NetEProperty*prop = new NetEProperty(net, pidx, 0); prop->set_line(*this); @@ -3981,6 +3987,12 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, &netvector_t::atom2s32, method_name, "$size"); } + if (method_name == "reverse") { + static const std::vector parm_names; + return elaborate_sys_task_method_(des, scope, prop, method_name, + "$ivl_darray_method$reverse", + parm_names); + } } } } @@ -4055,11 +4067,12 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, &netvector_t::atom2s32, method_name, "$size"); } else if (method_name == "reverse") { - cerr << get_fileline() << ": sorry: 'reverse()' " - "array sorting method is not currently supported." - << endl; - des->errors += 1; - return 0; + static const std::vector parm_names; + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + return elaborate_sys_task_method_(des, scope, sig, method_name, + "$ivl_darray_method$reverse", + parm_names); } else if (method_name=="sort") { cerr << get_fileline() << ": sorry: 'sort()' " "array sorting method is not currently supported." diff --git a/ivtest/ivltests/README_sv_queue_locators.txt b/ivtest/ivltests/README_sv_queue_locators.txt index c2f6282e8..cb7560160 100644 --- a/ivtest/ivltests/README_sv_queue_locators.txt +++ b/ivtest/ivltests/README_sv_queue_locators.txt @@ -6,7 +6,7 @@ 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/product (integral) + sum/product (integral), reverse (ordering) Behavior notes (LRM-oriented): @@ -29,6 +29,10 @@ Behavior notes (LRM-oriented): vector types). `product() with (expr)` reduces the expression result for each item. + * reverse() reverses the order of elements in place for queues and dynamic + arrays (integral, vector, real, string, and class-handle elements as stored + by Icarus). + * 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 @@ -39,8 +43,10 @@ Compiler / codegen touchpoints (typical): * Elaboration: elab_expr.cc — NetESFunc $ivl_queue_method$* and with-predicate lowering. * Code generation: tgt-vvp/eval_object.c — eval_queue_method_find, - eval_queue_method_find_with; eval_vec4.c for non-with find*. - * VVP: vvp/vthread.cc — opcode implementations; vvp/compile.cc — opcode tables. + eval_queue_method_find_with; eval_vec4.c for non-with find*; + tgt-vvp/vvp_process.c — reverse/delete as system tasks (`%reverse/obj`, …). + * VVP: vvp/vthread.cc — opcode implementations; vvp/vvp_darray.{h,cc} — + reverse_elems(); vvp/compile.cc — opcode tables. Regression tests (see ivtest/vvp_tests/*.json and regress-vvp.list): @@ -64,6 +70,8 @@ Regression tests (see ivtest/vvp_tests/*.json and regress-vvp.list): sv_darray_product.v integral product() reduction on dynamic arrays. sv_queue_product_with.v product() with expression on queues. sv_darray_product_with.v product() with expression on dynamic arrays. + sv_darray_reverse.v reverse() ordering on dynamic arrays. + sv_queue_reverse.v reverse() ordering on queues. 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. diff --git a/ivtest/ivltests/sv_class_darray_prop_locators.v b/ivtest/ivltests/sv_class_darray_prop_locators.v index 03409a5c6..b8ec972ab 100644 --- a/ivtest/ivltests/sv_class_darray_prop_locators.v +++ b/ivtest/ivltests/sv_class_darray_prop_locators.v @@ -62,6 +62,12 @@ module test; `check(c.d.sum() with (item + 1), 12); `check(c.d.product() with (item + 1), 60); + c.d = '{1, 2, 3}; + c.d.reverse(); + `check(c.d[0], 3); + `check(c.d[1], 2); + `check(c.d[2], 1); + if (!failed) $display("PASSED"); end diff --git a/ivtest/ivltests/sv_class_queue_prop_locators.v b/ivtest/ivltests/sv_class_queue_prop_locators.v index 2a30a0460..ce28e366f 100644 --- a/ivtest/ivltests/sv_class_queue_prop_locators.v +++ b/ivtest/ivltests/sv_class_queue_prop_locators.v @@ -54,6 +54,12 @@ module test; `check(c.q.sum() with (item + 1), 12); `check(c.q.product() with (item + 1), 60); + c.q = '{1, 2, 3}; + c.q.reverse(); + `check(c.q[0], 3); + `check(c.q[1], 2); + `check(c.q[2], 1); + if (!failed) $display("PASSED"); end diff --git a/ivtest/ivltests/sv_darray_reverse.v b/ivtest/ivltests/sv_darray_reverse.v new file mode 100644 index 000000000..b55ddf6c9 --- /dev/null +++ b/ivtest/ivltests/sv_darray_reverse.v @@ -0,0 +1,32 @@ +// Regression: dynamic array reverse() ordering method. + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int a[]; + + initial begin + a = '{1, 2, 3, 4}; + a.reverse(); + `CHK(a[0] === 4 && a[1] === 3 && a[2] === 2 && a[3] === 1); + + a = new [1]; + a[0] = 42; + a.reverse(); + `CHK(a[0] === 42); + + a = new [0]; + a.reverse(); + `CHK(a.size() === 0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_reverse.v b/ivtest/ivltests/sv_queue_reverse.v new file mode 100644 index 000000000..5e7574cfb --- /dev/null +++ b/ivtest/ivltests/sv_queue_reverse.v @@ -0,0 +1,31 @@ +// Regression: queue reverse() ordering method. + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int q[$]; + + initial begin + q = '{1, 2, 3, 4}; + q.reverse(); + `CHK(q[0] === 4 && q[1] === 3 && q[2] === 2 && q[3] === 1); + + q = '{99}; + q.reverse(); + `CHK(q[0] === 99); + + q.delete(); + q.reverse(); + `CHK(q.size() === 0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 21a733eb1..a44ce4653 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -259,6 +259,7 @@ 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_reverse vvp_tests/sv_darray_reverse.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 @@ -290,6 +291,7 @@ 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_reverse vvp_tests/sv_queue_reverse.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 diff --git a/ivtest/vvp_tests/sv_darray_reverse.json b/ivtest/vvp_tests/sv_darray_reverse.json new file mode 100644 index 000000000..11b7c17d9 --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_reverse.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_reverse.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog dynamic array reverse()", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_reverse.json b/ivtest/vvp_tests/sv_queue_reverse.json new file mode 100644 index 000000000..771b7dbea --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_reverse.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_reverse.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queue reverse()", + "type" : "CE" + } +} diff --git a/netlist.cc b/netlist.cc index b26c508ec..37c38c332 100644 --- a/netlist.cc +++ b/netlist.cc @@ -3454,9 +3454,10 @@ bool NetScope::check_synth(ivl_process_type_t pr_type, bool NetSTask::check_synth(ivl_process_type_t pr_type, const NetScope* /* scope */) const { - if (strcmp(name(), "$ivl_darray_method$delete") == 0) { + if (strcmp(name(), "$ivl_darray_method$delete") == 0 || + strcmp(name(), "$ivl_darray_method$reverse") == 0) { cerr << get_fileline() << ": warning: Dynamic array " - "delete method cannot be synthesized " + "ordering/delete method cannot be synthesized " << get_process_type_as_string(pr_type) << endl; } else { cerr << get_fileline() << ": warning: System task (" diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index e90b6f432..104eeb300 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -1691,11 +1691,41 @@ static int show_delete_method(ivl_statement_t net) draw_eval_expr_into_integer(ivl_stmt_parm(net, 1), 3); fprintf(vvp_out, " %%delete/elem v%p_0;\n", var); } else { - fprintf(vvp_out, " %%delete/obj v%p_0;\n", var); + fprintf(vvp_out, " %%delete/obj v%p_0;\n", var); } return 0; } +static int show_reverse_method(ivl_statement_t net) +{ + show_stmt_file_line(net, "reverse dynamic array or queue"); + + unsigned parm_count = ivl_stmt_parm_count(net); + if (parm_count != 1) + return 1; + + ivl_expr_t parm = ivl_stmt_parm(net, 0); + if (ivl_expr_type(parm) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(parm); + unsigned pidx = ivl_expr_property_idx(parm); + ivl_type_t sig_type = ivl_signal_net_type(clas); + ivl_type_t arr_type = ivl_type_prop_type(sig_type, pidx); + ivl_variable_type_t bt = ivl_type_base(arr_type); + if (bt != IVL_VT_QUEUE && bt != IVL_VT_DARRAY) + return 1; + + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%reverse/prop/obj %u;\n", pidx); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return 0; + } + + assert(ivl_expr_type(parm) == IVL_EX_SIGNAL); + ivl_signal_t var = ivl_expr_signal(parm); + fprintf(vvp_out, " %%reverse/obj v%p_0;\n", var); + return 0; +} + static int show_insert_method(ivl_statement_t net) { show_stmt_file_line(net, "queue: insert"); @@ -1881,6 +1911,9 @@ static int show_system_task_call(ivl_statement_t net) if (strcmp(stmt_name,"$ivl_darray_method$delete") == 0) return show_delete_method(net); + if (strcmp(stmt_name,"$ivl_darray_method$reverse") == 0) + return show_reverse_method(net); + if (strcmp(stmt_name,"$ivl_queue_method$insert") == 0) return show_insert_method(net); diff --git a/vvp/codes.h b/vvp/codes.h index 5f4832671..58e2f316d 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -213,6 +213,8 @@ extern bool of_REPLICATE(vthread_t thr, vvp_code_t code); extern bool of_RET_REAL(vthread_t thr, vvp_code_t code); extern bool of_RET_STR(vthread_t thr, vvp_code_t code); extern bool of_RET_VEC4(vthread_t thr, vvp_code_t code); +extern bool of_REVERSE_OBJ(vthread_t thr, vvp_code_t code); +extern bool of_REVERSE_PROP_OBJ(vthread_t thr, vvp_code_t code); extern bool of_RETLOAD_REAL(vthread_t thr, vvp_code_t code); extern bool of_RETLOAD_STR(vthread_t thr, vvp_code_t code); extern bool of_RETLOAD_VEC4(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index ba688eabf..5c46842e9 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -323,6 +323,8 @@ static const struct opcode_table_s opcode_table[] = { { "%retload/real",of_RETLOAD_REAL,1,{OA_NUMBER, OA_NONE,OA_NONE} }, { "%retload/str", of_RETLOAD_STR, 1,{OA_NUMBER, OA_NONE,OA_NONE} }, { "%retload/vec4",of_RETLOAD_VEC4,1,{OA_NUMBER, OA_NONE,OA_NONE} }, + { "%reverse/obj", of_REVERSE_OBJ, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, + { "%reverse/prop/obj", of_REVERSE_PROP_OBJ, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%scopy", of_SCOPY, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%set/dar/obj/real",of_SET_DAR_OBJ_REAL,1,{OA_NUMBER,OA_NONE,OA_NONE} }, { "%set/dar/obj/str", of_SET_DAR_OBJ_STR, 1,{OA_NUMBER,OA_NONE,OA_NONE} }, diff --git a/vvp/vthread.cc b/vvp/vthread.cc index f378d38e4..4f952714f 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -2610,6 +2610,62 @@ bool of_DELETE_OBJ(vthread_t thr, vvp_code_t cp) return true; } +/* %reverse/obj