From 75993ca9ea414f9fc2b9333d0c7f2f795c572688 Mon Sep 17 00:00:00 2001 From: pawelktk Date: Wed, 10 Jun 2026 15:39:43 +0200 Subject: [PATCH] Support assoc array methods with wide value types (#7680) --- include/verilated_types.h | 38 ++++++++++++--- src/V3Width.cpp | 6 +++ test_regress/t/t_assoc_method.v | 81 +++++++++++++++++++++++++++++++- test_regress/t/t_assoc_unsup.out | 78 ++++++++++++++++++++++++++++++ test_regress/t/t_assoc_unsup.py | 16 +++++++ test_regress/t/t_assoc_unsup.v | 50 ++++++++++++++++++++ 6 files changed, 261 insertions(+), 8 deletions(-) create mode 100644 test_regress/t/t_assoc_unsup.out create mode 100755 test_regress/t/t_assoc_unsup.py create mode 100644 test_regress/t/t_assoc_unsup.v diff --git a/include/verilated_types.h b/include/verilated_types.h index c2502bcdf..40847b65e 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -97,6 +97,28 @@ struct VlWide final { bool operator!=(const VlWide& that) const VL_PURE { return !(*this == that); } EData& operator[](size_t index) VL_MT_SAFE { return m_storage[index]; } const EData& operator[](size_t index) const VL_MT_SAFE { return m_storage[index]; } + VlWide& operator&=(const VlWide& rhs) { + VL_AND_W(N_Words, *this, *this, rhs); + return *this; + } + VlWide& operator|=(const VlWide& rhs) { + VL_OR_W(N_Words, *this, *this, rhs); + return *this; + } + VlWide& operator^=(const VlWide& rhs) { + VL_XOR_W(N_Words, *this, *this, rhs); + return *this; + } + VlWide& operator+=(const VlWide& rhs) { + VL_ADD_W(N_Words, *this, *this, rhs); + return *this; + } + VlWide& operator*=(const VlWide& rhs) { + VlWide out{}; + VL_MUL_W(N_Words, out, *this, rhs); + for (size_t i = 0; i < N_Words; ++i) m_storage[i] = out.m_storage[i]; + return *this; + } // METHODS EData& at(size_t index) VL_MT_SAFE { return m_storage[index]; } @@ -1280,7 +1302,7 @@ public: } T_Value r_sum() const { - T_Value out(0); // Type must have assignment operator + T_Value out = T_Value{}; for (const auto& i : m_map) out += i.second; return out; } @@ -1291,8 +1313,9 @@ public: return out; } T_Value r_product() const { - if (m_map.empty()) return T_Value(0); // The big three do it this way - T_Value out = T_Value(1); + // The big three return 0 when assoc array is empty + if (m_map.empty()) return T_Value{}; + T_Value out = T_Value{1}; for (const auto& i : m_map) out *= i.second; return out; } @@ -1304,8 +1327,9 @@ public: return out; } T_Value r_and() const { - if (m_map.empty()) return T_Value(0); // The big three do it this way - T_Value out = ~T_Value(0); + // The big three return 0 when assoc array is empty + if (m_map.empty()) return T_Value{}; + T_Value out = m_map.cbegin()->second; for (const auto& i : m_map) out &= i.second; return out; } @@ -1317,7 +1341,7 @@ public: return out; } T_Value r_or() const { - T_Value out = T_Value(0); + T_Value out = T_Value{}; for (const auto& i : m_map) out |= i.second; return out; } @@ -1328,7 +1352,7 @@ public: return out; } T_Value r_xor() const { - T_Value out = T_Value(0); + T_Value out = T_Value{}; for (const auto& i : m_map) out ^= i.second; return out; } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 5a24539b2..861714e34 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -4352,6 +4352,12 @@ class WidthVisitor final : public VNVisitor { } void methodCallAssoc(AstMethodCall* nodep, AstAssocArrayDType* adtypep) { AstCMethodHard* newp = nullptr; + if (nodep->withp() && adtypep->subDTypep()->isWide()) { + nodep->v3warn( + E_UNSUPPORTED, + "Unsupported: `with` clause on assoc arrays with wide value types in method '" + << nodep->prettyName() << "'"); + } if (nodep->name() == "num" // function int num() || nodep->name() == "size") { methodOkArguments(nodep, 0, 0); diff --git a/test_regress/t/t_assoc_method.v b/test_regress/t/t_assoc_method.v index 3a7ecdfde..13ab0411f 100644 --- a/test_regress/t/t_assoc_method.v +++ b/test_regress/t/t_assoc_method.v @@ -22,15 +22,23 @@ module t; int qe[int]; // Empty int qv[$]; // Value returns int qi[$]; // Index returns + bit[229:0] qw[int]; // Wide values + bit[229:0] qwe[int]; // Wide values - empty + bit[229:0] qwv[$]; // Wide values - Value returns + int qwi[$]; // Wide values - Index returns point points_q[int]; point points_qe[int]; // Empty points point points_qv[$]; int i; bit b; + bit[229:0] w; q = '{10: 1, 11: 2, 12: 2, 13: 4, 14: 3}; `checkp(q, "'{'ha:'h1, 'hb:'h2, 'hc:'h2, 'hd:'h4, 'he:'h3}"); + qw = '{10: 1, 11: 2, 12: 2, 13: 4, 14: 3}; + `checkp(qw, "'{'ha:'h1, 'hb:'h2, 'hc:'h2, 'hd:'h4, 'he:'h3}"); + // NOT tested: with ... selectors //q.sort; // Not legal on assoc - see t_assoc_meth_bad @@ -43,16 +51,34 @@ module t; `checkp(qv, "'{'h1, 'h2, 'h4, 'h3}"); qv = qe.unique; `checkp(qv, "'{}"); + + qwv = qw.unique; + `checkp(qwv, "'{'h1, 'h2, 'h4, 'h3}"); + qwv = qwe.unique; + `checkp(qwv, "'{}"); + qi = q.unique_index; qi.sort; `checkp(qi, "'{'ha, 'hb, 'hd, 'he}"); qi = qe.unique_index; `checkp(qi, "'{}"); + qwi = qw.unique_index; + qwi.sort; + `checkp(qwi, "'{'ha, 'hb, 'hd, 'he}"); + qwi = qwe.unique_index; + `checkp(qwi, "'{}"); + points_q[0] = point'{1, 2}; points_q[1] = point'{2, 4}; points_q[5] = point'{1, 4}; + qi = points_qe.unique_index(); + `checkp(qi, "'{}"); + + qi = points_q.unique_index(); + `checkh(qi.size, 3); + points_qv = points_q.unique(p) with (p.x); `checkh(points_qv.size, 2); qi = points_q.unique_index (p) with (p.x + p.y); @@ -113,6 +139,10 @@ module t; qv = q.min; `checkp(qv, "'{'h1}"); + + qwv = qw.min; + `checkp(qwv, "'{'h1}"); + points_qv = points_q.min(p) with (p.x + p.y); if (points_qv[0].x != 1 || points_qv[0].y != 2) $stop; @@ -130,8 +160,13 @@ module t; qv = qe.max(x) with (x + 1); `checkp(qv, "'{}"); - // Reduction methods + // Wide + qwv = qwe.min; + `checkp(qwv, "'{}"); + qwv = qwe.max; + `checkp(qwv, "'{}"); + // Reduction methods i = q.sum; `checkh(i, 32'hc); i = q.sum with (item + 1); @@ -141,6 +176,12 @@ module t; i = q.product with (item + 1); `checkh(i, 32'h168); + // Wide + w = qw.sum; + `checkh(w, 230'hc); + w = qw.product; + `checkh(w, 230'h30); + i = qe.sum; `checkh(i, 32'h0); i = qe.sum with (item + 1); @@ -150,6 +191,12 @@ module t; i = qe.product with (item + 1); `checkh(i, 32'h0); + // Wide + w = qwe.sum; + `checkh(w, 230'h0); + w = qwe.product; + `checkh(w, 230'h0); + q = '{10: 32'b1100, 11: 32'b1010}; i = q.and; `checkh(i, 32'b1000); @@ -164,6 +211,14 @@ module t; i = q.xor with (item + 1); `checkh(i, 32'b0110); + qw = '{10: 230'b1100, 11: 230'b1010}; + w = qw.and; + `checkh(w, 230'b1000); + w = qw.or; + `checkh(w, 230'b1110); + w = qw.xor; + `checkh(w, 230'b0110); + i = qe.and; `checkh(i, 32'b0); i = qe.and with (item + 1); @@ -177,6 +232,14 @@ module t; i = qe.xor with (item + 1); `checkh(i, 32'b0); + // Wide + w = qwe.and; + `checkh(w, 230'b0); + w = qwe.or; + `checkh(w, 230'b0); + w = qwe.xor; + `checkh(w, 230'b0); + i = q.and(); `checkh(i, 32'b1000); i = q.and() with (item + 1); @@ -190,6 +253,14 @@ module t; i = q.xor() with (item + 1); `checkh(i, 32'b0110); + // Wide + w = qw.and(); + `checkh(w, 230'b1000); + w = qw.or(); + `checkh(w, 230'b1110); + w = qw.xor(); + `checkh(w, 230'b0110); + i = qe.and(); `checkh(i, 32'b0); i = qe.or(); @@ -197,6 +268,14 @@ module t; i = qe.xor(); `checkh(i, 32'b0); + // Wide + w = qwe.and(); + `checkh(w, 230'b0); + w = qwe.or(); + `checkh(w, 230'b0); + w = qwe.xor(); + `checkh(w, 230'b0); + q = '{10: 1, 11: 2}; qe = '{10: 1, 11: 2}; `checkh(q == qe, 1'b1); diff --git a/test_regress/t/t_assoc_unsup.out b/test_regress/t/t_assoc_unsup.out new file mode 100644 index 000000000..0895427a3 --- /dev/null +++ b/test_regress/t/t_assoc_unsup.out @@ -0,0 +1,78 @@ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:17:15: Unsupported: `with` clause on assoc arrays with wide value types in method 'min' + : ... note: In instance 't' + 17 | qwv = qwe.min(x) with (x + 1); + | ^~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_assoc_unsup.v:18:15: Unsupported: `with` clause on assoc arrays with wide value types in method 'max' + : ... note: In instance 't' + 18 | qwv = qwe.max(x) with (x + 1); + | ^~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:20:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'sum' + : ... note: In instance 't' + 20 | w = qw.sum with (item + 1); + | ^~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:21:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'product' + : ... note: In instance 't' + 21 | w = qw.product with (item + 1); + | ^~~~~~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:23:13: Unsupported: `with` clause on assoc arrays with wide value types in method 'sum' + : ... note: In instance 't' + 23 | w = qwe.sum with (item + 1); + | ^~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:24:13: Unsupported: `with` clause on assoc arrays with wide value types in method 'product' + : ... note: In instance 't' + 24 | w = qwe.product with (item + 1); + | ^~~~~~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:27:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'and' + : ... note: In instance 't' + 27 | w = qw.and with (item + 1); + | ^~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:28:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'or' + : ... note: In instance 't' + 28 | w = qw.or with (item + 1); + | ^~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:29:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'xor' + : ... note: In instance 't' + 29 | w = qw.xor with (item + 1); + | ^~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:31:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'and' + : ... note: In instance 't' + 31 | w = qw.and() with (item + 1); + | ^~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:32:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'or' + : ... note: In instance 't' + 32 | w = qw.or() with (item + 1); + | ^~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:33:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'xor' + : ... note: In instance 't' + 33 | w = qw.xor() with (item + 1); + | ^~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:35:14: Unsupported: `with` clause on assoc arrays with wide value types in method 'find' + : ... note: In instance 't' + 35 | qwv = qw.find with (item == 2); + | ^~~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:36:14: Unsupported: `with` clause on assoc arrays with wide value types in method 'find_first' + : ... note: In instance 't' + 36 | qwv = qw.find_first with (item == 2); + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:37:14: Unsupported: `with` clause on assoc arrays with wide value types in method 'find_last' + : ... note: In instance 't' + 37 | qwv = qw.find_last with (item == 2); + | ^~~~~~~~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:39:13: Unsupported: `with` clause on assoc arrays with wide value types in method 'find_index' + : ... note: In instance 't' + 39 | qi = qw.find_index with (item == 2); + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:40:13: Unsupported: `with` clause on assoc arrays with wide value types in method 'find_first_index' + : ... note: In instance 't' + 40 | qi = qw.find_first_index with (item == 2); + | ^~~~~~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:41:13: Unsupported: `with` clause on assoc arrays with wide value types in method 'find_last_index' + : ... note: In instance 't' + 41 | qi = qw.find_last_index with (item == 2); + | ^~~~~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_assoc_unsup.v:45:14: Unsupported: `with` clause on assoc arrays with wide value types in method 'map' + : ... note: In instance 't' + 45 | qwv = qw.map(el) with (el / 100); + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_assoc_unsup.py b/test_regress/t/t_assoc_unsup.py new file mode 100755 index 000000000..30986489e --- /dev/null +++ b/test_regress/t/t_assoc_unsup.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2024 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios("linter") + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_assoc_unsup.v b/test_regress/t/t_assoc_unsup.v new file mode 100644 index 000000000..848bf0bf8 --- /dev/null +++ b/test_regress/t/t_assoc_unsup.v @@ -0,0 +1,50 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +module t; + initial begin + bit[229:0] qw[int]; // Wide values + bit[229:0] qwe[int]; // Wide values - empty + bit[229:0] qwv[$]; // Wide values - Value returns + int qi[$]; // Index returns + bit[229:0] w; + + qw = '{10: 1, 11: 2, 12: 2, 13: 4, 14: 3}; + + qwv = qwe.min(x) with (x + 1); + qwv = qwe.max(x) with (x + 1); + + w = qw.sum with (item + 1); + w = qw.product with (item + 1); + + w = qwe.sum with (item + 1); + w = qwe.product with (item + 1); + + qw = '{10: 230'b1100, 11: 230'b1010}; + w = qw.and with (item + 1); + w = qw.or with (item + 1); + w = qw.xor with (item + 1); + + w = qw.and() with (item + 1); + w = qw.or() with (item + 1); + w = qw.xor() with (item + 1); + + qwv = qw.find with (item == 2); + qwv = qw.find_first with (item == 2); + qwv = qw.find_last with (item == 2); + + qi = qw.find_index with (item == 2); + qi = qw.find_first_index with (item == 2); + qi = qw.find_last_index with (item == 2); + + // Map method (IEEE 1800-2023 7.12.5) + qw = '{1: 100, 2: 200, 3: 300}; + qwv = qw.map(el) with (el / 100); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule