Fix reduction methods for verilated types (#5542)

This commit is contained in:
Krzysztof Boroński 2024-10-19 01:51:44 +02:00 committed by GitHub
parent 55661e7f71
commit 7ccc93d9df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 216 additions and 65 deletions

View File

@ -485,6 +485,8 @@ private:
public: public:
using const_iterator = typename Deque::const_iterator; using const_iterator = typename Deque::const_iterator;
template <class Func>
using WithFuncReturnType = decltype(std::declval<Func>()(0, std::declval<T_Value>()));
private: private:
// MEMBERS // MEMBERS
@ -831,70 +833,63 @@ public:
return out; return out;
} }
template <typename Func> template <typename Func>
T_Value r_sum(Func with_func) const { WithFuncReturnType<Func> r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator WithFuncReturnType<Func> out = WithFuncReturnType<Func>(0);
IData index = 0; IData index = 0;
for (const auto& i : m_deque) out += with_func(index++, i); for (const auto& i : m_deque) out += with_func(index++, i);
return out; return out;
} }
T_Value r_product() const { T_Value r_product() const {
if (m_deque.empty()) return T_Value(0); if (m_deque.empty()) return T_Value(0); // The big three do it this way
auto it = m_deque.cbegin(); T_Value out = T_Value(1);
T_Value out{*it}; for (const auto& i : m_deque) out *= i;
++it;
for (; it != m_deque.cend(); ++it) out *= *it;
return out; return out;
} }
template <typename Func> template <typename Func>
T_Value r_product(Func with_func) const { WithFuncReturnType<Func> r_product(Func with_func) const {
if (m_deque.empty()) return T_Value(0); if (m_deque.empty()) return WithFuncReturnType<Func>(0); // The big three do it this way
auto it = m_deque.cbegin(); WithFuncReturnType<Func> out = WithFuncReturnType<Func>(1);
IData index = 0; IData index = 0;
T_Value out{with_func(index, *it)}; for (const auto& i : m_deque) out *= with_func(index++, i);
++it;
++index;
for (; it != m_deque.cend(); ++it) out *= with_func(index++, *it);
return out; return out;
} }
T_Value r_and() const { T_Value r_and() const {
if (m_deque.empty()) return T_Value(0); if (m_deque.empty()) return T_Value(0); // The big three do it this way
auto it = m_deque.cbegin(); T_Value out = ~T_Value(0);
T_Value out{*it}; for (const auto& i : m_deque) out &= i;
++it;
for (; it != m_deque.cend(); ++it) out &= *it;
return out; return out;
} }
template <typename Func> template <typename Func>
T_Value r_and(Func with_func) const { WithFuncReturnType<Func> r_and(Func with_func) const {
if (m_deque.empty()) return T_Value(0); if (m_deque.empty()) return WithFuncReturnType<Func>(0); // The big three do it this way
auto it = m_deque.cbegin();
IData index = 0; IData index = 0;
T_Value out{with_func(index, *it)}; WithFuncReturnType<Func> out = ~WithFuncReturnType<Func>(0);
++it; for (const auto& i : m_deque) out &= with_func(index++, i);
++index;
for (; it != m_deque.cend(); ++it) out &= with_func(index, *it);
return out; return out;
} }
T_Value r_or() const { T_Value r_or() const {
T_Value out(0); // Type must have assignment operator T_Value out = T_Value(0);
for (const auto& i : m_deque) out |= i; for (const auto& i : m_deque) out |= i;
return out; return out;
} }
template <typename Func> template <typename Func>
T_Value r_or(Func with_func) const { WithFuncReturnType<Func> r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator WithFuncReturnType<Func> out = WithFuncReturnType<Func>(0);
IData index = 0; IData index = 0;
for (const auto& i : m_deque) out |= with_func(index++, i); for (const auto& i : m_deque) out |= with_func(index++, i);
return out; return out;
} }
T_Value r_xor() const { T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator #ifdef VERILATOR_BIG3_NULLARY_ARITHMETICS_QUIRKS
if (m_deque.empty()) return T_Value(0);
#endif
T_Value out = T_Value(0);
for (const auto& i : m_deque) out ^= i; for (const auto& i : m_deque) out ^= i;
return out; return out;
} }
template <typename Func> template <typename Func>
T_Value r_xor(Func with_func) const { WithFuncReturnType<Func> r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator WithFuncReturnType<Func> out = WithFuncReturnType<Func>(0);
IData index = 0; IData index = 0;
for (const auto& i : m_deque) out ^= with_func(index++, i); for (const auto& i : m_deque) out ^= with_func(index++, i);
return out; return out;
@ -931,6 +926,9 @@ private:
public: public:
using const_iterator = typename Map::const_iterator; using const_iterator = typename Map::const_iterator;
template <class Func>
using WithFuncReturnType
= decltype(std::declval<Func>()(std::declval<T_Key>(), std::declval<T_Value>()));
private: private:
// MEMBERS // MEMBERS
@ -1177,64 +1175,56 @@ public:
return out; return out;
} }
template <typename Func> template <typename Func>
T_Value r_sum(Func with_func) const { WithFuncReturnType<Func> r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator WithFuncReturnType<Func> out = WithFuncReturnType<Func>(0);
for (const auto& i : m_map) out += with_func(i.first, i.second); for (const auto& i : m_map) out += with_func(i.first, i.second);
return out; return out;
} }
T_Value r_product() const { T_Value r_product() const {
if (m_map.empty()) return T_Value(0); if (m_map.empty()) return T_Value(0); // The big three do it this way
auto it = m_map.cbegin(); T_Value out = T_Value(1);
T_Value out{it->second}; for (const auto& i : m_map) out *= i.second;
++it;
for (; it != m_map.cend(); ++it) out *= it->second;
return out; return out;
} }
template <typename Func> template <typename Func>
T_Value r_product(Func with_func) const { WithFuncReturnType<Func> r_product(Func with_func) const {
if (m_map.empty()) return T_Value(0); if (m_map.empty()) return WithFuncReturnType<Func>(0); // The big three do it this way
auto it = m_map.cbegin(); WithFuncReturnType<Func> out = WithFuncReturnType<Func>(1);
T_Value out{with_func(it->first, it->second)}; for (const auto& i : m_map) out *= with_func(i.first, i.second);
++it;
for (; it != m_map.cend(); ++it) out *= with_func(it->first, it->second);
return out; return out;
} }
T_Value r_and() const { T_Value r_and() const {
if (m_map.empty()) return T_Value(0); if (m_map.empty()) return T_Value(0); // The big three do it this way
auto it = m_map.cbegin(); T_Value out = ~T_Value(0);
T_Value out{it->second}; for (const auto& i : m_map) out &= i.second;
++it;
for (; it != m_map.cend(); ++it) out &= it->second;
return out; return out;
} }
template <typename Func> template <typename Func>
T_Value r_and(Func with_func) const { WithFuncReturnType<Func> r_and(Func with_func) const {
if (m_map.empty()) return T_Value(0); if (m_map.empty()) return WithFuncReturnType<Func>(0); // The big three do it this way
auto it = m_map.cbegin(); WithFuncReturnType<Func> out = ~WithFuncReturnType<Func>(0);
T_Value out{with_func(it->first, it->second)}; for (const auto& i : m_map) out &= with_func(i.first, i.second);
++it;
for (; it != m_map.cend(); ++it) out &= with_func(it->first, it->second);
return out; return out;
} }
T_Value r_or() const { T_Value r_or() const {
T_Value out(0); // Type must have assignment operator T_Value out = T_Value(0);
for (const auto& i : m_map) out |= i.second; for (const auto& i : m_map) out |= i.second;
return out; return out;
} }
template <typename Func> template <typename Func>
T_Value r_or(Func with_func) const { T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator T_Value out = T_Value(0);
for (const auto& i : m_map) out |= with_func(i.first, i.second); for (const auto& i : m_map) out |= with_func(i.first, i.second);
return out; return out;
} }
T_Value r_xor() const { T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator T_Value out = T_Value(0);
for (const auto& i : m_map) out ^= i.second; for (const auto& i : m_map) out ^= i.second;
return out; return out;
} }
template <typename Func> template <typename Func>
T_Value r_xor(Func with_func) const { WithFuncReturnType<Func> r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator WithFuncReturnType<Func> out = WithFuncReturnType<Func>(0);
for (const auto& i : m_map) out ^= with_func(i.first, i.second); for (const auto& i : m_map) out ^= with_func(i.first, i.second);
return out; return out;
} }

View File

@ -1,15 +1,21 @@
// DESCRIPTION: Verilator: Verilog Test module // DESCRIPTION: Verilator: Verilog Test module
// //
// This file ONLY is placed under the Creative Commons Public Domain, for // This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2019 by Wilson Snyder. // any use, without warranty, 2024 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0 // SPDX-License-Identifier: CC0-1.0
`define stop $stop `define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); `define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
`define checkp(gotv,expv_s) do begin string gotv_s; gotv_s = $sformatf("%p", gotv); if ((gotv_s) !== (expv_s)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv_s), (expv_s)); `stop; end end while(0); `define checkp(gotv,expv_s) do begin string gotv_s; gotv_s = $sformatf("%p", gotv); if ((gotv_s) !== (expv_s)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv_s), (expv_s)); `stop; end end while(0);
module t (/*AUTOARG*/); module t (/*AUTOARG*/);
typedef struct { int x, y; } point; typedef struct { int x, y; } point;
function automatic int vec_len_squared(point p);
return p.x * p.x + p.y * p.y;
endfunction
initial begin initial begin
int q[int]; int q[int];
int qe[int]; // Empty int qe[int]; // Empty
@ -18,6 +24,7 @@ module t (/*AUTOARG*/);
point points_q[int]; point points_q[int];
point points_qv[$]; point points_qv[$];
int i; int i;
bit b;
q = '{10:1, 11:2, 12:2, 13:4, 14:3}; q = '{10:1, 11:2, 12:2, 13:4, 14:3};
`checkp(q, "'{'ha:'h1, 'hb:'h2, 'hc:'h2, 'hd:'h4, 'he:'h3} "); `checkp(q, "'{'ha:'h1, 'hb:'h2, 'hc:'h2, 'hd:'h4, 'he:'h3} ");
@ -116,8 +123,12 @@ module t (/*AUTOARG*/);
i = qe.sum; i = qe.sum;
`checkh(i, 32'h0); `checkh(i, 32'h0);
i = qe.sum with (item + 1);
`checkh(i, 32'h0);
i = qe.product; i = qe.product;
`checkh(i, 32'h0); `checkh(i, 32'h0);
i = qe.product with (item + 1);
`checkh(i, 32'h0);
q = '{10:32'b1100, 11:32'b1010}; q = '{10:32'b1100, 11:32'b1010};
i = q.and; i = q.and;
@ -135,10 +146,16 @@ module t (/*AUTOARG*/);
i = qe.and; i = qe.and;
`checkh(i, 32'b0); `checkh(i, 32'b0);
i = qe.and with (item + 1);
`checkh(i, 32'h0);
i = qe.or; i = qe.or;
`checkh(i, 32'b0); `checkh(i, 32'b0);
i = qe.or with (item + 1);
`checkh(i, 32'b0);
i = qe.xor; i = qe.xor;
`checkh(i, 32'b0); `checkh(i, 32'b0);
i = qe.xor with (item + 1);
`checkh(i, 32'b0);
i = q.and(); i = q.and();
`checkh(i, 32'b1000); `checkh(i, 32'b1000);
@ -165,6 +182,19 @@ module t (/*AUTOARG*/);
`checkh(q == qe, 1'b1); `checkh(q == qe, 1'b1);
`checkh(q != qe, 1'b0); `checkh(q != qe, 1'b0);
i = points_q.sum with (vec_len_squared(item));
`checkh(i, 32'h2a);
i = points_q.product with (vec_len_squared(item));
`checkh(i, 32'h6a4);
b = points_q.sum with (vec_len_squared(item) == 5);
`checkh(b, 1'b1);
b = points_q.sum with (vec_len_squared(item) == 0);
`checkh(b, 1'b0);
b = points_q.product with (vec_len_squared(item) inside {5, 17});
`checkh(b, 1'b0);
b = points_q.sum with (vec_len_squared(item) inside {5, 17, 20});
`checkh(b, 1'b1);
$write("*-* All Finished *-*\n"); $write("*-* All Finished *-*\n");
$finish; $finish;
end end

View File

@ -9,12 +9,20 @@
`define checkp(gotv,expv_s) do begin string gotv_s; gotv_s = $sformatf("%p", gotv); if ((gotv_s) !== (expv_s)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv_s), (expv_s)); `stop; end end while(0); `define checkp(gotv,expv_s) do begin string gotv_s; gotv_s = $sformatf("%p", gotv); if ((gotv_s) !== (expv_s)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv_s), (expv_s)); `stop; end end while(0);
module t (/*AUTOARG*/); module t (/*AUTOARG*/);
typedef struct { int x, y; } point;
function automatic int vec_len_squared(point p);
return p.x * p.x + p.y * p.y;
endfunction
initial begin initial begin
int q[*]; int q[*];
int qe [ * ]; // Empty - Note spaces around [*] for parsing coverage int qe [ * ]; // Empty - Note spaces around [*] for parsing coverage
point points_q[*] = '{"a": point'{1, 2}, "b": point'{2, 4}, "c": point'{1, 4}};
int qv[$]; // Value returns int qv[$]; // Value returns
int qi[$]; // Index returns int qi[$]; // Index returns
int i; int i;
bit b;
string v; string v;
q = '{"a":1, "b":2, "c":2, "d":4, "e":3}; q = '{"a":1, "b":2, "c":2, "d":4, "e":3};
@ -95,11 +103,17 @@ module t (/*AUTOARG*/);
`checkh(i, 32'b0110); `checkh(i, 32'b0110);
i = qe.and; i = qe.and;
`checkh(i, 32'b0); `checkh(i, 32'h0);
i = qe.and with (item + 1);
`checkh(i, 32'h0);
i = qe.or; i = qe.or;
`checkh(i, 32'b0); `checkh(i, 32'b0);
i = qe.or with (item + 1);
`checkh(i, 32'b0);
i = qe.xor; i = qe.xor;
`checkh(i, 32'b0); `checkh(i, 32'b0);
i = qe.xor with (item + 1);
`checkh(i, 32'b0);
i = q.and(); i = q.and();
`checkh(i, 32'b1000); `checkh(i, 32'b1000);
@ -121,6 +135,19 @@ module t (/*AUTOARG*/);
i = qe.xor(); i = qe.xor();
`checkh(i, 32'b0); `checkh(i, 32'b0);
i = points_q.sum with (vec_len_squared(item));
`checkh(i, 32'h2a);
i = points_q.product with (vec_len_squared(item));
`checkh(i, 32'h6a4);
b = points_q.sum with (vec_len_squared(item) == 5);
`checkh(b, 1'b1);
b = points_q.sum with (vec_len_squared(item) == 0);
`checkh(b, 1'b0);
b = points_q.product with (vec_len_squared(item) inside {5, 17});
`checkh(b, 1'b0);
b = points_q.sum with (vec_len_squared(item) inside {5, 17, 20});
`checkh(b, 1'b1);
$write("*-* All Finished *-*\n"); $write("*-* All Finished *-*\n");
$finish; $finish;
end end

View File

@ -1,7 +1,7 @@
// DESCRIPTION: Verilator: Verilog Test module // DESCRIPTION: Verilator: Verilog Test module
// //
// This file ONLY is placed under the Creative Commons Public Domain, for // This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2019 by Wilson Snyder. // any use, without warranty, 2024 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0 // SPDX-License-Identifier: CC0-1.0
`define stop $stop `define stop $stop

View File

@ -0,0 +1,27 @@
%Warning-WIDTHTRUNC: t/t_dynarray_method_bad.v:19:9: Operator ASSIGN expects 32 bits on the Assign RHS, but Assign RHS's CMETHODHARD 'r_sum' generates 64 bits.
: ... note: In instance 't'
19 | i = s.sum with (item.len);
| ^
... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest
... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message.
%Warning-WIDTHTRUNC: t/t_dynarray_method_bad.v:21:9: Operator ASSIGN expects 32 bits on the Assign RHS, but Assign RHS's CMETHODHARD 'r_product' generates 64 bits.
: ... note: In instance 't'
21 | i = s.product with (item.len);
| ^
%Warning-WIDTHTRUNC: t/t_dynarray_method_bad.v:23:9: Operator ASSIGN expects 1 bits on the Assign RHS, but Assign RHS's CMETHODHARD 'r_sum' generates 64 bits.
: ... note: In instance 't'
23 | b = s.sum with (item == "hello");
| ^
%Warning-WIDTHTRUNC: t/t_dynarray_method_bad.v:25:9: Operator ASSIGN expects 1 bits on the Assign RHS, but Assign RHS's CMETHODHARD 'r_sum' generates 64 bits.
: ... note: In instance 't'
25 | b = s.sum with (item == "");
| ^
%Warning-WIDTHTRUNC: t/t_dynarray_method_bad.v:27:9: Operator ASSIGN expects 1 bits on the Assign RHS, but Assign RHS's CMETHODHARD 'r_product' generates 64 bits.
: ... note: In instance 't'
27 | b = s.product with (item inside {"hello", "sad"});
| ^
%Warning-WIDTHTRUNC: t/t_dynarray_method_bad.v:29:9: Operator ASSIGN expects 1 bits on the Assign RHS, but Assign RHS's CMETHODHARD 'r_product' generates 64 bits.
: ... note: In instance 't'
29 | b = s.product with (item inside { "hello", "sad", "world" });
| ^
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. 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-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()

View File

@ -0,0 +1,35 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
`define checkp(gotv,expv_s) do begin string gotv_s; gotv_s = $sformatf("%p", gotv); if ((gotv_s) !== (expv_s)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv_s), (expv_s)); `stop; end end while(0);
module t (/*AUTOARG*/);
string s[] = { "hello", "sad", "sad", "world" };
initial begin
int i;
bit b;
i = s.sum with (item.len);
`checkh(i, 10);
i = s.product with (item.len);
`checkh(i, 24);
b = s.sum with (item == "hello");
`checkh(b, 1'b1);
b = s.sum with (item == "");
`checkh(b, 1'b0);
b = s.product with (item inside {"hello", "sad"});
`checkh(b, 1'b0);
b = s.product with (item inside { "hello", "sad", "world" });
`checkh(b, 1'b1);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -1,7 +1,7 @@
// DESCRIPTION: Verilator: Verilog Test module // DESCRIPTION: Verilator: Verilog Test module
// //
// This file ONLY is placed under the Creative Commons Public Domain, for // This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2019 by Wilson Snyder. // any use, without warranty, 2024 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0 // SPDX-License-Identifier: CC0-1.0
`define stop $stop `define stop $stop
@ -26,6 +26,7 @@ module t (/*AUTOARG*/);
int qvunused[$]; // Value returns (unused) int qvunused[$]; // Value returns (unused)
int qi[$]; // Index returns int qi[$]; // Index returns
int i; int i;
bit b;
string string_q[$]; string string_q[$];
string string_qv[$]; string string_qv[$];
point_3d points_q[$]; // Same as q and qv, but complex value type point_3d points_q[$]; // Same as q and qv, but complex value type
@ -186,9 +187,13 @@ module t (/*AUTOARG*/);
i = qe.sum; i = qe.sum;
`checkh(i, 32'h0); `checkh(i, 32'h0);
i = qe.sum with (item + 1);
`checkh(i, 32'h0);
i = qe.product; i = qe.product;
`checkh(i, 32'h0); `checkh(i, 32'h0);
i = qe.product with (item + 1);
`checkh(i, 32'h0);
q = '{32'b1100, 32'b1010}; q = '{32'b1100, 32'b1010};
i = q.and; i = q.and;
@ -206,16 +211,37 @@ module t (/*AUTOARG*/);
i = qe.and; i = qe.and;
`checkh(i, 32'b0); `checkh(i, 32'b0);
i = qe.and with (item + 1);
`checkh(i, 32'b0);
i = qe.or; i = qe.or;
`checkh(i, 32'b0); `checkh(i, 32'b0);
i = qe.or with (item + 1);
`checkh(i, 32'b0);
i = qe.xor; i = qe.xor;
`checkh(i, 32'b0); `checkh(i, 32'b0);
i = qe.xor with (item + 1);
`checkh(i, 32'b0);
q = '{1, 2}; q = '{1, 2};
qe = '{1, 2}; qe = '{1, 2};
`checkh(q == qe, 1'b1); `checkh(q == qe, 1'b1);
`checkh(q != qe, 1'b0); `checkh(q != qe, 1'b0);
string_q = {"a", "bc", "def", "ghij"};
i = string_q.sum with (item.len);
`checkh(i, 10);
i = string_q.product with (item.len);
`checkh(i, 24);
b = string_q.sum with (item == "bc");
`checkh(b, 1'b1);
b = string_q.sum with (item == "");
`checkh(b, 1'b0);
b = string_q.product with (item inside {"a", "bc", "def"});
`checkh(b, 1'b0);
b = string_q.product with (item inside {"a", "bc", "def", "ghij"});
`checkh(b, 1'b1);
$write("*-* All Finished *-*\n"); $write("*-* All Finished *-*\n");
$finish; $finish;
end end